

这也就解释了如果构造函数显式返回对象类型,则直接返回这个对象,而不是返回最开始创建的对象。
最后在看第 10 步:
10)如果F 返回的不是对象类型(第 9 步不成立),则返回创建的对象 obj。如果构造函数没有显式返回对象类型(显式返回基本数据类型或者直接不返回),则返回最开始创建的对象。
那如果构造函数是箭头函数怎么办?
箭头函数中没有 [[Construct]] 方法,不能使用 new 调用,会报错。
NOTICE:其中 [[Construct]] 就是指构造函数本身。
除了箭头函数之外的任何函数,都可以使用 new 进行调用,背后发生了什么,上节英文讲述的很清楚了,再用中文描述如下:
1)创建 ECMAScript 原生对象 obj;
2)给 obj 设置原生对象的内部属性;(和原型属性不同,内部属性表示为 [[PropertyName]],两个方括号包裹属性名,并且属性名大写,比如常见 [[Prototype]]、[[Constructor]])
3)设置 obj 的内部属性 [[Class]] 为 Object;
4)设置 obj 的内部属性 [[Extensible]] 为 true;
5)将 proto 的值设置为 F 的 prototype 属性值;
6)如果 proto 是对象类型,则设置 obj 的内部属性 [[Prototype]] 值为 proto;(进行原型链关联,实现继承的关键)
7)如果 proto 是不对象类型,则设置 obj 的内部属性 [[Prototype]] 值为内建构造函数 Object 的 prototype 值;(函数 prototype 属性可以被改写,如果改成非对象类型,obj 的 [[Prototype]] 就指向 Object 的原型对象)
8)9)10)见上节分析。(决定返回什么)
对于第 7 步的情况,见下面代码:
function Foo(name) {
this.name = name;
}
var o1 = new Foo("xiaoming");
console.log(o1.__proto__ === Foo.prototype); // true
// 重写构造函数原型属性为非对象类型,实例内部 [[Prototype]] 属性指向 Object 原型对象
// 因为实例是一个对象类型的数据,默认会继承内建对象的原型,
// 如果构造函数的原型不满足形成原型链的要求,那就跳过直接和内建对象原型关联
Foo.prototype = 1;
var o2 = new Foo("xiaohong");
console.log(o2.__proto__ === Foo.prototype); // false
console.log(o2.__proto__ === Object.prototype); // true若执行 new Foo(),过程如下:
1)创建新对象 o;
2)给新对象的内部属性赋值,关键是给[[Prototype]]属性赋值,构造原型链(如果构造函数的原型是 Object 类型,则指向构造函数的原型;不然指向 Object 对象的原型);
3)执行函数 Foo,执行过程中内部 this 指向新创建的对象 o;
4)如果 Foo 内部显式返回对象类型数据,则,返回该数据,执行结束;不然返回新创建的对象 o。
关于一个数据是否是 Object 类型,可以通过 instanceof 操作符进行判断:如果 x instanceof Object 返回 true,则 x 为 Object 类型。
由上可知,null instanceof Object 返回 false,所以 null 不是 Object 类型,尽管typeof null 返回 "Object"。
instanceof 的工作原理是:在表达式 x instanceof Foo 中,如果 Foo 的原型(即 Foo.prototype)出现在 x 的原型链中,则返回 true,不然,返回 false。
因为函数的原型可以被改写,所以会出现在 x 通过 Foo new 出来之后完全改写 Foo 的原型 x instanceof Foo 返回 false 的情况。因为实例创建之后重写构造函数原型,实例指向的原型已经不是构造函数的新的原型了,见下面代码:
const Foo = function() {};
const o = new Foo();
o instanceof Foo; // true
// 重写 Foo 原型
Foo.prototype = {};
o instanceof Foo; // false