原型链梳理
原型链与类
最近参加58到家面试,一面就被pass了。一道原型链的题直接卡住了。回来仔细想想,题目一点也不复杂,只是平时不关注罢了。受《你不知道的js》一书影响,基本上没有用原型实现过继承,所以不会也是有一部分原因的(借口)。
原型链的构成
原理链是JS的特殊属性:prototype。由于使用Object.create或者使用new时会将函数的prototype上的方法和属性挂载在新生成对象上。所以:
function Foo (name) {
this.name = name
}
Foo.prototype.getName = function () {
return this.name
}
var obj = new Foo('xxx')
obj.name // xxx
例子中是最简单的方法。来看下constructor:
- constructor指向了构造函数,不管是prototype下的constructor或者是constructor属性。
- new的时候会执行constructor。而我们知道所以的函数非prototype属性和方法是不能进入原型链,所以是不是挂载在prototype下的constructor会执行呢?看下面的代码:
function Foo(name){this.name = name}
function Bar (name) { this.name = name + 'xx'}
Foo.prototype = {
constructor: Bar,
getName: function () {return this.name}
}
var obj = new Foo('22')
obj.getName() // 22
var Func1 = Object.create(Foo)
typeof Func1 // object
看到没?自己定义的constructor在实际new的时候根本没有用到!!而create只是原型指向Foo,生成的是对象,而不是函数。
所以,constructor是挂载到生成的对象里面的__proto__里面的。随便new一个函数,发现proto里面的constructor都是指向的这个函数。指定是无效的。
来看下难倒我的题目:
function Foo (name) {this.name = name}
Foo.prototype = {
constructor: Foo,
getName: function () {return this.name}
}
var obj = new Foo('xx')
obj.__proto__
obj.__proto__.__proto__
obj.__proto__.__proto__.__proto__
// 结果
obj.__proto__ === Foo.prototype
obj.__proto__.__proto__ === Object.prototype
obj.__proto__.__proto__ === null
为什么会让我迷惑呢?其实还是因为显式的写了constructor。这个constructor写不写根本就没有不影响构建,也不影响生成的对象。看下之前我们交叉写constructor:
function Foo(name){this.name = name}
function Bar (name) { this.name = name + 'xx'}
Foo.prototype = {
constructor: Bar,
getName: function () {return this.name}
}
var obj = new Foo('22')
obj.__proto__ === Bar.prototype // false
obj.__proto__.constructor === Bar // true
obj.__proto__ === Foo.prototype // true
obj.__proto__.__proto__ === Object.prototype // true
从例子中可以看出,我们为Prototype设置的constructor属性是被保留的,指向了Bar函数(非prototype)。这样赋值有什么用呢?其实还是有用处的,调用super时,使用调用原型连上一层的constructor,这样就可以自定义了。
用原型类模拟类
好吧,这个我没用过。在好多书中也不推荐这种方式。现在简单研究下。
function Foo (name) {this.name = name}
function Bar () {}
Bar.prototype = Foo.prototype
var obj = new Bar()
obj.__proto__.__proto__ === Object.prototype
obj.__proto__ === Foo.prototype
可以看出,Bar的prototype指向的是Foo的prototype。而__proto__下面的constructor却不包含Bar,如果设置
Bar.prototype.constructor = Bar
obj.__proto__ === Bar.prototype// true
obj.__proto__.constructor === Bar
obj.__proto__.__proto__ === Object.prototype
则Bar的prototype的一下层直接指向了Object。这和我们想要的继承不一样。因此我们定义一个中间函数。将中间函数的prototype指向Foo,再new一下,挂载prototype。
function Foo () {}
Foo.prototype.getName = function () {}
var F = function () {}
F.prototype = Foo.prototype
function Bar () {}
Bar.prototype = new F()
Bar.prototype.constructor = Bar
var obj = new Bar()
obj.__proto__.__proto__ === Foo.prototype // true
可以看出,这样写法下,我们的Proto发生就有三级了,实现了继承。用它写个extend函数:
function extend (target, from) {
var F = function () {}
F.prototype = from.prototype
target.prototype = new F()
target.prototype.constructor = target
return target
}
function Foo () {}
Foo.prototype.getName = function () {return 1}
var Bar = extend(function () {}, Foo)
var obj = new Bar()
obj.getName() // 1
// 被覆盖
function Bar2 () {}
Bar2.prototype.getName = function () {return 2}
extend(Bar2, Foo)
var obj2 = new Bar2()
obj2.getName()
看来出,之前定义的Bar2已经被覆盖了。可以想其他办法。

浙公网安备 33010602011771号