看下面代碼
function Person(){}
function Man(){}
function Woman(){}
Woman.prototype = new Person
var a = new Man
var b = new Woman
a.constructor
b.constructor
問 a.constructor和b.constructor分別是什么占遥?
a.constructor
-> function Man(){}
b.constructor
-> function Person(){}
會不會覺得 b.constructor = function Person(){} 好像不太對勁呢?
接下來你會干的一件事情是什么呢颊糜?既然是通過new對應的構造函數(shù)得到的對象,我猜你應該會打印下面的語句
Man.constructor
-> function Function() { [native code] }
Woman.constructor
-> function Function() { [native code] }
Person.constructor
-> function Function() { [native code] }
可能你會反應個2-3秒秃踩,然后哈哈一笑衬鱼,Man是函數(shù),函數(shù)不都是Function構造的嗎憔杨!
現(xiàn)在距離真相就只差一步之遙了鸟赫,接下來做什么呢?
Man.prototype.constructor
-> function Man(){}
Woman.prototype.constructor
-> function Person(){}
Person.prototype.constructor
-> function Person(){}
原來 Woman.prototype = new Person這個操作把Woman自己的constructor屬性覆蓋了,為什么說是覆蓋了呢抛蚤?因為constructor屬性在函數(shù)定義之時就已經(jīng)存在了台谢。
這樣當b訪問constructor屬性時在當前的Women的prototype上就找不到對應的constructor屬性,進而就會向上訪問對象的原型鏈岁经,最終找到的是Person對象的constructor屬性对碌。
既然知道了原因,要修正這個問題就簡單了
Woman.prototype.constructor = Woman
-> function Woman(){}
var c = new Woman
-> undefined
c.constructor
-> function Woman(){}
new 操作符
*ECMA262中對new操作符有下面的定義 *
11.2.2 The new Operator
The production NewExpression : new NewExpression is evaluated as follows:
- Let ref be the result of evaluating NewExpression.
- Let constructor be GetValue(ref).
- If Type(constructor) is not Object, throw a TypeError exception.
- If constructor does not implement the [[Construct]] internal method, throw a TypeErrorexception.
- Return the result of calling the [[Construct]] internal method on constructor, providing no arguments (that is, an empty list of arguments).
- The production MemberExpression : new MemberExpression Arguments is evaluated as follows:
- Let ref be the result of evaluating MemberExpression.
- Let constructor be GetValue(ref).
- Let argList be the result of evaluating Arguments, producing an internal list of argument values (11.2.4).
- If Type(constructor) is not Object, throw a TypeError exception.
- If constructor does not implement the [[Construct]] internal method, throw a TypeErrorexception.
- Return the result of calling the [[Construct]] internal method on constructor, providing the listargList as the argument values.
上面的意思就是先求NewExpression的值為ref蒿偎,然后在ref上調用內部的[[construct]]方法朽们,還是比較抽象是吧,stackoverflow上有人給出了new操作符的執(zhí)行流程诉位。
var _new = function(fn) {
var obj = Object.create(fn.prototype);
var result = fn.apply(obj);
return result != null && typeof result === 'object' ? result : obj;
};
看到了吧骑脱,和上面我們實驗的結果是一致的。Object.create 的只是fn的原型prototype苍糠,由于原型中丟失了constructor屬性叁丧,所以我們在訪問的時候就會出現(xiàn)找到了父親的constructor屬性。
-
再看_new中的fn.apply(obj)岳瞭,new Woman的時候fn是Woman構造函數(shù)呢還是Person構造函數(shù)呢拥娄,這個我們驗證一下.
function A(){ console.log('in A'); } -> undefined function B(){ console.log('in B'); } -> undefined B.prototype = new A(); -> in A -> A {} b = new B() -> in B -> B {}
符合預期。