javascript精髓篇之原型链维护和继承.

一.两个原型

很多人都知道javascript是原型继承,每个构造函数都有一个prototype成员,通过它就可以把javascript的继承演义的美轮美奂了.
其实啊,光靠这一个属性是无法完成javascript的继承.
我们在代码中使用的prototype完成继承在这里就不多说了.大家可以查一下资料.
另外一个看不见的prototype成员.
每一个实例都有有一条指向原型的prototype属性,这个属性是无法被访问到的,当然也就无法被修改了,因为这是维护javascript继承的基础.


1  //构造器声明
2 function Guoyansi(){ }
3 function GuoyansiEx(){}
4 //原型继承
5 GuoyansiEx.prototype=new Guoyansi();
6 //创建对象
7 var g1=new GuoyansiEx();
8 var g2=new GuoyansiEx();

上面的代码中的对象可以用下面的图来说明

二.原型的维护
一个构造器产生的实例,其constructor属性总是指向该构造器.我们暂且认为该话是对的.

1 function Guoyansi(){ }
2 var obj1=new Guoyansi();
3 console.log(obj1.constructor===Guoyansi);//true

其实构造器本身是没有constructor这个属性的,那么这个属性是来自哪呢?
答案是:来自原型.
因此得出下面的结论

1 guo.constructor===Guoyansi.prototype.constructor===Guoyansi

既然我们可以通过constructor来寻找构造器.因此我们就可以进一步完善上面的图了.

1 function GuoyansiEx(){}
2 GuoyansiEx.prototype=new Guoyansi();
3 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的指向要求非常精确的话,可以做如下处理.

/**方法一:**/
1 function Guoyansi(){}
2 function GuoyansiEx(){}
3 GuoyansiEx.prototype=new Guoyansi();
4 GuoyansiEx.prototype.constructor=GuoyansiEx;//重置constructor指向.


1  /**
2 方法二
3 **/
4 function Guoyansi(){}
5 function GuoyansiEx(){
6 this.constructor=arguments.callee;
7 }
8 GuoyansiEx.prototype=new Guoyansi();


1  /**
2 方法三
3 **/
4 function Guoyansi(){}
5 function GuoyansiEx(){
6 this.constructor=GuoyansiEx;
7 }
8 GuoyansiEx.prototype=new Guoyansi();

三.看不见的原型有什么用呢?
看得见的原型链我们可以对他操作来完成我们的继承,那么这个看不见的原型链我们既看不见,又无法操作.要它有何用.
面向对象中继承有一个特性:相似性.子类与父类具有相似性.因此在子类中你是无法用delete删除从父类继承而来的成员.也就是说子类必须具有父类的特性.
为了维护这个特性,javascript在对象的内部产生了一条我们看不见的原型属性,并且不允许用户访问.这样,用户可以处于任何目的来修改constructor,
而不会破坏子类拥有父类的特性.
简而言之:内部原型是javascript的原型继承机制所需要的,而外部原型是用户实现继承所需要的.

四.火狐引擎SpiderMonkey中的__proto__

还是这段代码.


1  function Guoyansi(){}
2 Guoyansi.prototype.age=24;
3 function GuoyansiEx(){}
4 var obj1=new Guoyansi();
5 GuoyansiEx.prototype=obj1;
6 GuoyansiEx.prototype.constructor=GuoyansiEx;//重置constructor指向.
7 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(),可以访问到所有父类的原型了.


 1 function Guoyansi(){}
2 Guoyansi.prototype.age=24;
3 function GuoyansiEx(){}
4 var obj1=new Guoyansi();
5 GuoyansiEx.prototype=obj1;
6 GuoyansiEx.prototype.constructor=GuoyansiEx;//重置constructor指向.
7 var obj2=new GuoyansiEx();
8 var proto=Object.getPrototypeOf(obj2);
9 while(proto){
10 console.log(proto.constructor);
11 proto=Object.getPrototypeOf(proto);
12 }
13 console.log("object的原型"+proto);

结果是:GuoyansiEx
Guoyansi
Object

object的原型null

个人觉得这些应该算是javascript面向对象的精髓之一了.

javascript精髓篇之原型链维护和继承.,布布扣,bubuko.com

时间: 2024-10-03 15:01:45

javascript精髓篇之原型链维护和继承.的相关文章

javascript高级篇——理解原型和作用域【连载中...】

说明: 该教程绕开了javascript的一些基本的语法知识,直接讲解javascript中最难理解的两个部分,也是和其他主流面向对象语言区别最大的两个部分--原型和作用域,当然,肯定少不了原型链和作用域链.帮你揭开javascript最神秘的面纱. 为什么要偏偏要讲这两个知识点? 这是我在这么多年学习javascript的经历中,认为最难理解.最常犯错的地方,学习这两个知识点,会让你对javascript有更深层次的理解,至少理解了原型和作用域,就不能再算是javascript菜鸟了.另外,这

javascript 重难点(原型链 this) 理解总有一个过程,不要急,循序渐进!

开始补充: 1. 将函数定义作为对象的属性,称之为对象方法.2. this的指向是由它所在函数调用的上下文决定的(语境),而不是由它所在函数定义的上下文决定的.3. 因为当一个函数作为函数而不是方法来调用的时候,this指向的是全局对象.   Window对象 一:原型链: 1._proto_     这个属性 二:this: 1.this指的是,调用函数的那个对象! 2.this 指 global对象 . 三: javascript中的global对象        1. window属于DO

javascript学习笔记11(原型链)

<script type="text/javascript">    /**     * js实现继承的第一种方式是基于原型链的方式     */    function Parent() {        this.pv = "parent";    }    Parent.prototype.pp = "ok";    Parent.prototype.showParentValue = function() {        a

javascript 创建对象及对象原型链属性介绍

我们知道javascript里定义一个普通对象的方法,如: let obj = {}; obj.num = 1; obj.string = 'string'; obj.func = function(){alert('func')}; obj.arr = ['x','y']; console.log(obj.num); //1 console.log(obj.string); // "string" console.log(obj.func); //function(){alert('

明白JavaScript原型链和JavaScrip继承

原型链是JavaScript的基础性内容之一.其本质是JavaScript内部的设计逻辑. 首先看一组代码: 1 <script type="text/javascript"> 2 function parent(){ 3 this.name="cehis"; 4 } 5 6 function son(){ 7 this.age=11; 8 } 9 10 function sub(){ 11 this.sex="nan"; 12 }

JavaScript 随笔2 面向对象 原型链 继承

第六章 面向对象的程序设计 1.创建对象的几种方式 A)工厂模式 function CreatObj(name,sex,age){ this.name=name; this.sex=sex; this.age=age; } 缺点:虽然可以批量创建对象,却不能知道对象的类型 只知道他是Object类型: B)构造函数 function Person(name,sex){ this.name=name; this.sex=sex; this.sayName=function(){ alert(thi

javascript之对象,原型链

对象 ECMAScript是高度抽象的面向对象语言,处理对象的.同样也有基本类型,但是他们在需要的情况下,也可以转型成对象. 定义: 对象是拥有一系列属性以及唯一的原型对象.那原型对象可能是对象,也可能为空. 让我们看一个基本对象的例子.对象的原型是通过内部的[[Prototype]]来引用的.然而,我们将会用 __<internal_property>__ 下化线符号替代双中括号,那么对于原型对象: __proto__. 对于下面代码: var foo = { x: 10, y:20 };

JavaScript原型,原型链 ? Js的继承?

什么是js原型:函数的一个属性,在函数的创建过程中由js编译器自动添加.具体来说,就是我们经常使用的,prototype属性,这个属性怎么得来的呢? 当生产一个function对象的时候,就可以看到它了. js functiion对象的创建-1 要使用function对象,必须先创建一个对象,有几种方式: 1.var A = function(){};//字面量,匿名函数,推荐 2.function A(){};//函数声明 3.var A = new Function(//这里是函数体)//实

[js高手之路]从原型链开始图解继承到组合继承的产生

于javascript原型链的层层递进查找规则,以及原型对象(prototype)的共享特性,实现继承是非常简单的事情 一.把父类的实例对象赋给子类的原型对象(prototype),可以实现继承 1 function Person(){ 2 this.userName = 'ghostwu'; 3 } 4 Person.prototype.showUserName = function(){ 5 return this.userName; 6 } 7 function Teacher (){}