许多人一直对JS的prototype继承机制不明了,到底在创建一个类的实例的时候,解析器为我们做了些什么呢?
首先,我们来看一个例子:
function class1() {
this.name = "my name";
}
function class2() {
this.age = 123;
}
class2.prototype = new class1();
var obj = new class2();
alert(obj.name);
alert(class2.prototype.name);
delete class2.prototype;
alert(obj.name);
alert(class2.prototype);
alert(class2.prototype.name);
结果将是
firefox "my name" "my name" "my name" "[object object]" undefined
IE "my name" "my name" "my name" undefined 出错
从最后两个结果来看,Firefox下的prototype是可以删除的,但它却是一定会存在的,在做delete(class2.prototype)的时候,Firefox将prototype变成一个普通的Object。
而IE对此的处理方式有所不同,当删除class2.prototype的时候,它就真真正正被删掉了,所以会得到undefined,并且最后一行会出错。
可能就有人会有疑问了,我们都已经把class2.prototype给删除了,虽然Firefox和IE处理方式不同,但确确实实肯定不会存在class2.prototype.name这个变量了,但为什么在删除之后,class2的实例obj的name值会存在?这就是我们要说的重点之一了。
接着看下边几行代码,请使用Firefox来运行。
function class1() {
this.name = "my name";
}
function class2() {
this.age = 123;
}
class2.prototype = new class1();
var obj = new class2();
alert(obj.name);
alert(obj.__proto__.name);
delete class2.prototype;
alert(obj.name);
alert(obj.__proto__.name);
obj.name = "new name";
alert(obj.name);
alert(obj.__proto__.name);
delete obj.name
alert(obj.name);
alert(obj.__proto__.name);
在我们指定的firefox下运行结果是: "my name" "my name" "my name" "my name" "new name" "my name" "my name" "my name"
是不是觉得结果好奇怪?__proto__这又是什么变量呢?为什么在delete(obj.name)之后,原先这个值为"new name"的属性没消失,但却随着这行代码变成了"my name"
稍加分析,其实这个__proto__对象,就是原先那个class2.prototype,而且尽管class2.prototype被清掉了,这个obj.__proto__对象却依然存在!
我们立刻就可以想到,JS里一个类的实例在实例化完毕之后,就与原先的类的关系断开了。当它在本身找不着一个属性的时候,会尝试去__proto__里找,如果找着就把它当成本身的属性了。
这是根据Firefox提供出来的这个__proto__对象推断出来的,那IE又是怎么样的呢?我们继续看下边简化了的代码:
function class1() {
this.name = "my name";
}
function class2() {
this.age = 123;
}
class2.prototype = new class1();
var obj = new class2();
alert(obj.name);
alert(obj.__proto__);
delete class2.prototype;
alert(obj.name);
obj.name = "new name";
alert(obj.name);
delete obj.name
alert(obj.name);
在看过Firefox下的运行结果后,我们可以很欣喜地看到IE其实处理方式应该是一致的,在删除obj.name之后,它的值确实像Firefox一样,"new name"变成了"my name",但它并没有给我们提供__proto__这样的对象。
于是,我很肯定但有点武断地得出了以下推论:
在运行"new xxx()"的时候,解析器会创建一个空的Object,然后为它创建了一个名字可能为__proto__(为什么要用可能二字?因为IE不知道起的是什么名,但Firefox确实用了这个名字)的属性,然后将xxx类的prototype赋值给它。接下来,进入类本身,相当于xxx.call(obj)的方式把新的对象当成this一行一行执行下去,然后返回该对象。
从这里也可以看出JS里根本没有类这种东西,或者说没有真正的类,它纯属模拟出来的,这也是为什么类的定义跟函数的定义都用了function这个关键词,事实上类的定义就是一个函数,它负责将传进来的this对象根据函数的逻辑给它增加了属性以及方法,跟其它高级语言里的类的定义是完全不一样的。