ECMA-262中,把对象定义为:”无序属性的集合,其属性可以为基本类型值、对象或者函数。”而一般我们要创建对象(更准确一点叫做创建对象实例)时,会使用 new
操作符。最简单的如 new Object()
,然后我们把创建对象实例的那个东西(也就是 new
操作符后面跟着的那个东西,一般都是一个函数)叫做构造函数(有人也叫做构造器)。再然后我们会把 new Object()
的返回值赋值给一个变量如 obj
,写成 var obj=new Object();
,而后我们就会对obj进行各种操作,但其中到底发生了什么?
当 new Foo(...)
被执行
引用 MDN 上关于当
new Foo(...)
被执行,会发生如下事情:
- 创建一个从
Foo.prototype
继承的新对象;- 构造函数
Foo
使用指定的参数调用,并将Foo
中的this
绑定到新创建的对象。new Foo
相当于new Foo()
,即如果没有指定参数列表,则使用无参调用Foo;- 构造函数返回的对象成为整个
new
表达式的结果。如果构造函数没有显式地返回一个对象,那么将使用步骤1中创建的对象。(通常构造函数不返回值,但是它们可以选择这样做,如果它们想要覆盖正常的对象创建过程。)
上面那段说白了就是:
- 使用
new
操作符时,会创建一个新对象,该新对象的__proto__
属性指向其构造函数的原型,即Foo.prototype
; - 构造函数中的
this
会指向新对象; - 执行构造函数中语句;
- 如果构造函数定义了返回值且返回值是对象(包括数组、函数之类的),则返回值为这些对象;如果构造函数未定义返回值(默认返回
Undefined
)或定义的返回值是基本类型(Undefined
类型、Null
类型、Boolean
类型、Number
类型、String
类型),则返回new
创建的那个新对象。
需注意的是:新对象的 __proto__
属性指向其构造函数的原型,即 Foo.prototype
,而不是指向 Foo
。 Foo.prototype
为创建 Foo
(一般为使用 function Foo(){...}
)时创建的的属性,prototype
中包括一个名为 constructor
的属性,该属性才是指向 Foo
。
2019.11.07 update
__proto__
为浏览器(Mozilla)为访问[[prototype]]
私有属性而定义的,类似的访问[[class]]
属性要通过Object.prototype.toString
举例说明:
字面量定义 prototype
还需要注意的一点为,如果通过字面量形式定义 prototype
时,如下:
1 | function Person(){ |
此时 Person.prototype 的 constructor 属性就不是指向 Person 了 而是指向默认的 Object ,因为字面量形式相当于重写 prototype,如需像关键字声明那样使 constructor 指向 Person,需在字面量中手动声明,这个要特别注意。如下:
1 | function Person(){ |
例题
设计一个工具函数extend(A, B), 使 A 继承 B
1 | function extend(A, B) { |