javascript的继承机制和原型链是非常重要的概念。一直以来都觉得掌握的不够深入彻底,如果有那么几个月不写不用就总是会忘记。所有把平时在项目中对这些机制的理解都记录下来供自己未来查阅和同行们指正。
先看一个简单的例子,这个例子我把输出的结果都放到注释里面方便查看.
function l(l) {//这是方便在浏览器里面查看结果的一个log函数. console.log(l) } function D(ufo) { this.ufo = ufo; } var d1 = new D("d1"); l("来看看类的情况:") l(typeof D.prototype)//这是一个object.所以大家才可以随便往里面新增方法和属性. l(D.prototype)//如果没有手动修改.默认就是D{}这个object.也就是一个空的D对象. l(D.__proto__)//这个返回的是function Empty() {} .好像类或者任意function的__proto__总是返回Empty这个function.这个到底是做什么的呢?我现在还没搞明白.希望有同行指教. l(typeof D.constructor)//return function. l(D.constructor);//所有类的constructor都是function Function() { [native code] } .也就是Function.这个是本地方法.或许是c语言或者c++写的. l("来看看实例的情况:") l(d1.prototype);//return undefined .实例是没有prototype的. l(d1.__proto__)//看下面的代码你会发现他和类的prototype是一个东西. l(typeof d1.constructor)//return function l(d1.constructor)//实例的function就有点意思了也就是D这个function的定义本身.也就是构造器本省.去new的时候就会执行这个构造器. //我们看看这这两个属性是否一样 l(D.prototype==d1.__proto__)//return true:类D的prototype指针和实例d1的__proto__指针指向同一个object
通过上面大概能对prototype和__proto__还有constructor有一点点印象.但是实际情况有时候根据复杂一些.比如加入继承机制以后.他们就发生了明显的变化.再看看一个更加复杂的例子.里面加入了3层继承:
function l(l) { console.log(l) }//这里是为了在浏览器里面的调试封装的一个log函数.后面的l调用均代表此function function A(){ this.a="a" } function B() { this.b = "b" } B.prototype=new A() function C(name, age) { this.name = name; var _age = age; this.get1 = function() { console.log(name + " " + age) }; this.get2 = function() { console.log("ufo:" + name + age + "--" + _age) } } C.prototype = new B() function D(ufo) { C.call(this, "ufo", 1000) this.ufo = ufo; } D.prototype = new C(); var d1 = new D("d1"); var d2= new D("d2");
代码的继承轨迹: D>C>B>A.使用类D实例化了d1和d2,我们仔细查看d1和d2的内存数据结构:
可以看到类C相对于类D都是依照原型链存在的.方法get1和get2都是在__proto__原型的C层级.因此如果我们运行:
l(d1.get1==d2.get1)//这里的结果是true.
不难理解.因为原型的内存是共享的.所以结果是true.
那我们修改代码如下:
function l(l) {
console.log(l)
}
function A(){
this.a="a"
}
function B() {
this.b = "b"
}
B.prototype=new A()
function C(name, age) {
this.name = name;
var _age = age;
this.get1 = function() {
console.log(name + " " + age)
};
this.get2 = function() {
console.log("ufo:" + name + age + "--" + _age)
}
}
C.prototype = new B()
function D(ufo) {
C.call(this, "ufo", 1000);//我们增加了这一句
this.ufo = ufo;
}
D.prototype = new C();
var d1 = new D("d1");
var d2= new D("d2");
l(d1.get1==d2.get1);//现在变成了false
现在在D的构造器里面我们去运行了C的构造器.并且传入了this.这样的就提升了C内部的get1和get2方法的位置.我们再一次看看内存数据结构.
内存数据显示.原型链的get1和get2还在那里.因为function在javascript里面是独立存在的.她不属于任何一个class或者object.完全不像java c#.所有通过call或者apply可以在任何你希望的地方去运行function.并且在第一个参数传入你希望的object.他就会修改运行的上下文环境.比如这里传入this.那么构造器function C就被作为D的一个内存函数被运行了一遍.里面的逻辑也会像普通函数一样作用于D.这样D的内部也就产生了get1和get2.当你调用get1和get2的时候就不会去去调用原型链的get1和get2了.这个时候实例化的d1和d2都是独立的object.在内存里面分别各自维护了一份get1和get2的内存区域.所有上面的程序返回结果为false.