相信大家都知道JS原型链最大的特点就是动态的继承。若果调用的属性或者方法在实例中没有找到的话就会动态的向它的上一层原型查找。这个特点在实践中也会经常运用到。我们再说说在运用原型链动态继承时应该注意的一些地方。
首先举一个例子:
<span style="color:#006600;">function demo(){}
demo.prototype.TJoe = 18;
var test = new demo();
alert(test.TJoe);</span>
实例test中并没有TJoe,所以会向上查找,得到结果:
然后加入一句覆盖掉原来的属性的语句
<span style="color:#009900;">function demo(){}
demo.prototype.TJoe = 18;
demo.prototype = {CSDN : 666}; //覆盖语句
var test = new demo();
alert(test.TJoe);</span>
这时候动态的去查找原型得到的结果:
这时候没有找到TJoe于是返回了undefined。我们稍稍做一点变动:
<span style="color:#33cc00;">function demo(){}
demo.prototype.TJoe = 18;
var test = new demo();
demo.prototype = {CSDN : 666}; //覆盖语句移到了实例创建之后
alert(test.TJoe);</span>
会怎么样呢?
依然得到了原先的TJoe。这就奇怪了,只是改变了覆盖语句的位置,接过缺大相径庭,到底是怎么回事?好吧,我们一点点来说。
首先,什么是原型
在JavaScript 中,每当定义一个对象(函数)时候,对象中都会包含一些预定义的属性。其中函数对象的一个属性就是原型对象 prototype。
这里图会比较说明问题
对象或函数一级级继承构成了原型链,如果原型被更改,那之后继承于它的都会受到影响。
好的,回过头来看看刚才的例子。实例test的原型真的被更改了吗?这里我们要着重的区分__proto__和prototype。
JS在创建对象的时候都会有__proto__这个内置属性,它用于指向原型对象prototype。所以,我们之前改掉的是对象的那个原型,而实例的__proto__依然指向着原来的原型,所以输出实例时其指向的依然是原先的原型。
搞清这个问题后我们在说说实现继承的方法。
1.直接写
test.prototype = new demo();
这样做调用了构造函数,实现了继承。但是,这时候的test还没有实例化,demo中这时候传什么进来都不太好。
2.利用ECMA5的Object.create
test.prototype = Object.create(demo.prototype);
这里利用Object.create创建了一个空的对象并指向了demo.prototype,而且test.prototype也有自己的空的对象,比较完美。
但是也有问题就是Object.create是ECMA5中的,支持ie9+,所以我们得想个办法在低版本的ie下也能支持它。
于是我们模拟一个Object.create:
<span style="color:#33cc00;">if(!Object.create){
Object.create = function (proto){
function F(){}
F.prototype = proto; //赋值为原型的对象
return new F;
};
}</span>
这样就更完美了~
Go for it~