这里有新鲜出炉的 Javascript 教程,程序狗速度看过来!
Javascript 是一种由 Netscape 的 LiveScript 发展而来的原型化继承的基于对象的动态类型的区分大小写的客户端脚本语言,主要目的是为了解决服务器端语言,比如 Perl,遗留的速度问题,为客户提供更流畅的浏览效果。
本文通过实例详细向我们分析了 javascript 原型链维护和继承的问题,十分的详尽,十分的全面,这里推荐给大家。
一. 两个原型
很多人都知道 javascript 是原型继承, 每个构造函数都有一个 prototype 成员, 通过它就可以把 javascript 的继承演义的美轮美奂了. 其实啊, 光靠这一个属性是无法完成 javascript 的继承. 我们在代码中使用的 prototype 完成继承在这里就不多说了. 大家可以查一下资料. 另外一个看不见的 prototype 成员. 每一个实例都有有一条指向原型的 prototype 属性, 这个属性是无法被访问到的, 当然也就无法被修改了, 因为这是维护 javascript 继承的基础.
- //构造器声明
- function Guoyansi(){ }
- function GuoyansiEx(){}
- //原型继承
- GuoyansiEx.prototype=new Guoyansi();
- //创建对象
- var g1=new GuoyansiEx();
- var g2=new GuoyansiEx();
上面的代码中的对象可以用下面的图来说明
二. 原型的维护
一个构造器产生的实例, 其 constructor 属性总是指向该构造器. 我们暂且认为该话是对的.
- function Guoyansi(){ }
- var obj1=new Guoyansi();
- console.log(obj1.constructor===Guoyansi);//true
其实构造器本身是没有 constructor 这个属性的, 那么这个属性是来自哪呢? 答案是: 来自原型. 因此得出下面的结论
- obj1.constructor===Guoyansi.prototype.constructor===Guoyansi
既然我们可以通过 constructor 来寻找构造器. 因此我们就可以进一步完善上面的图了.
- function GuoyansiEx(){}
- GuoyansiEx.prototype=new Guoyansi();
- console.log(GuoyansiEx.constructor===GuoyansiEx)//false
根据上图, 上面的结果应该是 true, 但为什么是 false 呢
现在做个分析. GuoyansiEx 的原型被 Guoyansi 的实例重写了, 那么 GuoyansiEx 的原型中的 constructor 自然也是来自 Guoyansi 的实例. 而 Guoyansi 实例中的 constructor 又是来自 Guoyansi.prototype. 而 Guoyansi.prototype 没有被重写, 所以 Guoyansi.prototype 的 constructor 指向 Guoyansi(构造函数);
根据以上分析得出下面的结论
- GuoyansiEx.constructor===Guoyansi.constructor===Guoyansi;
如果在开发过程中对于 Constructor 的指向要求非常精确的话, 可以做如下处理.
- /**方法一:**/
- function Guoyansi(){}
- function GuoyansiEx(){}
- GuoyansiEx.prototype=new Guoyansi();
- GuoyansiEx.prototype.constructor=GuoyansiEx;//重置constructor指向.
- /**
- 方法二
- **/
- function Guoyansi(){}
- function GuoyansiEx(){
- this.constructor=arguments.callee;
- }
- GuoyansiEx.prototype=new Guoyansi();
- /**
- 方法三
- **/
- function Guoyansi(){}
- function GuoyansiEx(){
- this.constructor=GuoyansiEx;
- }
- GuoyansiEx.prototype=new Guoyansi();
三. 看不见的原型有什么用呢
看得见的原型链我们可以对他操作来完成我们的继承, 那么这个看不见的原型链我们既看不见, 又无法操作. 要它有何用. 面向对象中继承有一个特性: 相似性. 子类与父类具有相似性. 因此在子类中你是无法用 delete 删除从父类继承而来的成员. 也就是说子类必须具有父类的特性. 为了维护这个特性, javascript 在对象的内部产生了一条我们看不见的原型属性, 并且不允许用户访问. 这样, 用户可以处于任何目的来修改 constructor, 而不会破坏子类拥有父类的特性. 简而言之: 内部原型是 javascript 的原型继承机制所需要的, 而外部原型是用户实现继承所需要的.
四. 火狐引擎 SpiderMonkey 中的__proto__
还是这段代码.
- function Guoyansi(){}
- Guoyansi.prototype.age=24;
- function GuoyansiEx(){}
- var obj1=new Guoyansi();
- GuoyansiEx.prototype=obj1;
- GuoyansiEx.prototype.constructor=GuoyansiEx;//重置constructor指向.
- var obj2=new GuoyansiEx();
我现在想要从 obj 开始向上访问父类 Guoyansi 的 prototype 的属性的 age. 思路是这样的. 第一步: obj2====>obj2.constructor.prototype 第二部: obj2.constructor.prototype===>GuoyansiEx.prototype; 第三部: GuoyansiEx.prototype===>obj1; 第四部: obj1.constructor====>Guoyansi 第五部: Guoyansi.prototype.age
写成这这样: console.log(obj2.constructor.prototype.constructor.prototype.age)//24; 最终的结果是 24. 最终的结果是 24. 可以正常执行, 但是在好多书上说 constructor 修改后, 级无法在找到父类中的原型了. 不知道是怎么回事.
在火狐中提够了一种更加简洁的属性._proto_ SpiderMonkey 中默认在任何创建的对象上添加了一个名为_proto_的属性, 该属性指向构造器所用的原型. 其实就是我们上面提到的不可见的原型链, 只不过是在这个地方变相的公开而已. 可以这样访问到 age console.log(obj2.__proto__.__proto__.age);//24 这样的确是成功的访问到了父类的原型属性, 但是这个属性只适用于火狐, 在其他浏览器中是会出错的. 在 E5 中对 Object 做出了扩展 Object.getPrototypeOf(), 可以访问到所有父类的原型了.
- function Guoyansi(){}
- Guoyansi.prototype.age=24;
- function GuoyansiEx(){}
- var obj1=new Guoyansi();
- GuoyansiEx.prototype=obj1;
- GuoyansiEx.prototype.constructor=GuoyansiEx;//重置constructor指向.
- var obj2=new GuoyansiEx();
- var proto=Object.getPrototypeOf(obj2);
- while(proto){
- console.log(proto.constructor);
- proto=Object.getPrototypeOf(proto);
- }
- console.log("object的原型"+proto);
结果是: GuoyansiEx Guoyansi Object object 的原型 null
个人觉得这些应该算是 javascript 面向对象的精髓之一了. 小伙伴们自己参考下,根据需求使用到自己的项目中去吧
来源: http://www.phperz.com/article/17/0417/274238.html