Max_Mac原创,转载请注明出处。
前言:以下的例子的运行结果都来自chrome。
先看几个例子:
例子1:
var A=function(){};
A.prototype.test=function(){};
var a1=new A();
var a2=new A();
a1.test="123";
alert(a1.test);//"123"
alert(a2.test);//"function(){}"
例子2:
var A=function(){};
A.prototype.test=function(){};
var a1=new A();
var a2=new A();
a1.test.text="123"";
alert(a1.test.text);//"123"
alert(a2.test.text);//"123"
alert(A.prototype.test.text);//"123"
例子3:
var A=function(){};
var B=function(){};
A.prototype.text="abc";
B.prototype.text="bcd";
B.prototype=A.prototype;
A.prototype.text="cba";
alert(B.prototype.text);//"cba",这个例子看似简单,但可以说明B继承A时,为什么不可以B.prototype=A.prototype
例子4:
Function.prototype.text="function";
var d=function(){};
alert(d.prototype.text);//"undefined"
alert(d.text);//"function"
alert(d.__proto__.text);//"function"
Function.prototype.text="function2";
alert(d.__proto__.text);//"function2",这个比较特殊
结合例子1、2、4可以得出结论,function A(){};var a=new A()进行了如下的操作:
a.__proto__={};
for(var name in A.prototype){
a.__proto__[name]=A.prototype[name];
}
回看这篇文章,这段理解是错误的。例子1、2、4并不能得出这样的结论。之所以得出这样错误的结论可能是由于1产生的误导(误认为修改a1.__proto__中的test,并没有影响a2.__proto__),事实上a1.__proto__和a2._proto__都指向A.prototype,而a1.test="123";这样的语句实际为a1对象添加了新变量test并覆盖了a1.__proto__.test,并非是修改修改a1.__proto__中的test。以下代码可以证明:
var A=function(){};
A.prototype.test="1";
var a1=new A();
alert(a1.test);//"1"
A.prototype.test="2";
alert(a1.test);//"2"
A.prototype.newProp="hello";
alert(a1.newProp);//"2"
所以下面一段理解也是错误的,图也是错误的。《前端开发必须知道的JS(一) 原型和继承》这篇文章讲解的很透彻。(本段写于2017年7月31日)
现在要使B继承A,可以定义function B(){};B.prototype=new A();var b=new B(),同样可知:
b.__proto__={};
for(var name in a){
b.__proto__[name]=a[name]//其中b.__proto__.__proto__=a.__proto__
}
A、B、a和b之间的关系图如下:
新图如下:(修改于2017年7月31日)
看了以上的关系图有哪些启发呢?
1.toString是A.prototype中的一个引用类型的对象(函数),a.toString以及b.toString都是引用了这个对象,如果将来还有C继承B,c.toString也将引用这个对象;
2.Name是A.prototy中的一个基本类型的变量,每一个A的实例a都有不同的拷贝,而B的实例b.name取决于B.prototype(A的实例a);
3.A的代码中为a赋值的anum如果是一个引用类型的对象,每一个B的实例b都是指向这个对象;
再探讨一下Object.prototype:
__defineGetter__: function __defineGetter__() { [native code] }
__defineSetter__: function __defineSetter__() { [native code] }
__lookupGetter__: function __lookupGetter__() { [native code] }
__lookupSetter__: function __lookupSetter__() { [native code] }
constructor: function Object() { [native code] }
hasOwnProperty: function hasOwnProperty() { [native code] }
isPrototypeOf: function isPrototypeOf() { [native code] }
propertyIsEnumerable: function propertyIsEnumerable() { [native code] }
toLocaleString: function toLocaleString() { [native code] }
toString: function toString() { [native code] }
valueOf: function valueOf() { [native code] }
要分清楚A.prototype、A.__proto__ 、A.prototype.__proto__、a.__proto__、a.__proto__.__proto__:
A.prototype.__proto__===Object.prototype
A.__proto__==Function.prototype(是==,而不是===)
a.__proto__===A.prototype
a.__proto__.__proto__===Object.prototype
A.__proto__===Function.prototype
验证方法很简单:
var A=function(){};
alert(A.__proto__===Function.prototype)
其中比较特殊的的是Function.prototype,这是一个函数对象(而不是一个普通对象)function Empty(){},而所有函数的__proto__也是这个函数对象。
原本想再写一些关于为什么javascript使用基于prototype的继承,以及这样的继承有哪些优缺点。不过这样的论题似乎有点大,需要从很多方面去思考和探讨这个问题,所以暂时就不写了,等以后思考的多了,再细细把它写下来。