原型链简介
JavaScript不包含传统的继承,因此引入原型链模型模拟继承功能。
__proto__
属性
__proto__
属性是隐藏属性,不应该出现在日常的编码中,这里只是为了理解原型链提到这个属性。
下面是例子
var bar;
console.log(bar.attr);
//undefined
上面代码的工作原理是
- 寻找定义在
bar
上的attr属性,若找到则返回,若找不到,继续 - 寻找定义在
bar.__proto__
上找attr属性,若找到则返回,若找不到则继续 - 寻找定义在
bar.__proto__.__proto__
上的attr属性,若找到则返回,若找不到则继续 - 直到找到的
bar.__proto__....__ptoto__
为null,若还未找到,则返回undefined
理解JavaScript中了寻找属性的流程,我们来看实例化一个类时new做了些什么。
new
关键字与prototype
new一个对象的实际过程如下
var bar = new Bar();
// function new () {
// var bar = {};
// bar.__proto__ = Bar.prototype;
// Bar.apply(bar, arguments);
// return bar;
// }
- 将Bar的
prototype
对象直接赋值给bar的__proto__
- 以bar为this,apply运行Bar这个函数
这么做了的结果是**定义在Bar.prototype上的属性和方法将能被bar访问到!**比如:
function Bar () {
this.bar = 'bar';
}
Bar.prototype.attr = 'value';
Bar.prototype.getAttr = function() { return this.attr };
var bar = new Bar();
bar.getAttr();
// bar中不包含getAttr,但是bar.__proto__包含这个函数,执行成功。
// value
这里着重理解bar.__proto__ = Bar.prototype;
正因为在new的过程中,bar的__proto__
被赋值为Bar的prototype
,所以定义在Bar的prototype上的属性和方法能被bar访问到。这样,达到了Bar作为类,实例化了一个bar对象的目的。
PS: 这里的赋值实际上是引用,意思是bar的__proto__
和Bar的prototype
指向同一个对象,其中给一个改变了,另一个也跟着改变。要理解这个,需要看看JavaScript中引用和赋值的问题。
多级继承例子
下面介绍多级继承的例子
var Foo = function() {
this.foo = 'foo';
}
Foo.prototype.isFoo = true;
var Bar = function() {
Foo.apply(this, arguments);
this.bar = 'bar';
}
Bar.prototype.__proto__ = Foo.prototype;
Bar.prototype.isBar = true;
var bar = new Bar();
console.log(bar);
//{bar: 'bar'}
console.log(bar.foo);
//**bar.__proto__隐含了属性foo**
//'foo'
console.log(bar.isFoo);
//**bar.__proto__.__proto__隐含了属性isFoo**
//true
求补充,直接评论
Bar.prototype = new Foo();
书上说这样干不好,会生成一个实例。
Bar.prototype.__proto__ = Foo.prototype;
//不过没内部属性foo, 需要在Bar 里 call 一下.
Bar.prototype.isBar = true;
这样不好吧,这样一变影响原型链上所有。
一般原型链上只挂function。
我的理解是,就是要生成实例的,而且这里只在继承的时候生成一次不会影响性能哦……继承以后这是个Bar.prototype是Foo的实例,修改这个实例不影响Foo原来的东西,同时还保证了,这个子类永远会引用到Foo.prototype上的东西,达到继承效果。我知道的另一种写法是Bar.prototype = Foo.prototype,但是那样会很乱,改了Bar的prototype会影响到Foo,更推荐的写法是怎么写呢?
第二点我也不知道好不好,囧,不过在Bar.prototype上挂的东西是在实例里的隐藏属性…这个属性console不出来,但是能访问到,既然方法能挂,属性应该也没问题吧。
使用 zupu 查看两种方法不同:
var Foo = function() {
this.foo = 'foo';
}
Foo.prototype.getfoo = function(){
return this.foo;
}
// 方法一 Bar
var Bar = function() {
this.bar = 'bar';
}
Bar.prototype = new Foo();
var bar = new Bar();
//方法二 Bar2
var Bar2 = function() {
Foo.call(this);
this.bar2 = 'bar2';
}
Bar2.prototype.__proto__ = Foo.prototype;
var bar2 = new Bar2();
var test ={
foo:new Foo(),
bar:new Bar(),
bar2:new Bar2()
}
document.write(zupu(test));
结果: 第一种方法 Foo的内部属性 foo 也跑到原型链上了