`
jimichan
  • 浏览: 280997 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

详解javascript类继承机制的原理

阅读更多
 本文着重解析javascript类继承机制,让你从底层了解javascript是怎样实现“继承”这一概念的。
                                                                jimichan在javaeye博客发布,转载请说明。
   目前 javascript的实现继承方式并不是通过“extend”关键字来实现的,而是通过 constructor function和prototype属性来实现继承。首先我们创建一个animal
js 代码
 
  1.   var animal function (){   //这就是constructor function 了  
  2.    this .name = 'pipi';    
  3.    this .age = 10;      
  4.    this .height = 0;      
  5. }      
  6.  //建立一个动物的实例     
  7.  var  a1 =   new  animal ();   

构造函数与其他普通函数区别在于,1.构造函数里有 this关键字,2.调用构造函数是使用的new关键字。通过new运算符调用构造函数 animal 后,系统就会返回一个对象,这个对象就相当于
js 代码
  1. var  a1 = { name:'pipi' ,age:10,height:0 }  
  2.   
  3. //或者   
  4. var  a1 =  new  Object();  
  5. a1.name='pipi';  
  6. a1.age = 10;  
  7. a1.height = 0; 
等同这样的方式来产生js对象。

到这里我们知道如何在js中定义一个类了,接下来我们展示如何写一个cat

js 代码
 
  1. var  cat =  function (){    
  2. this .play =  function (){     
  3.  alert('cat play')    
  4.  }    
  5.  }    
  6.  cat .prototype = new  animal ();    
  7.  //prototype 属性指向一个对象 
  8. var c1 = new cat();

到这里,cat就继承了 animal 对象,类cat的一个实例对象c1拥有属性name,age,height,和方法play了。
那么
prototype起到了一个什么样的作用呢?
prototype就好比一个指针,它指向一个object,这个object就称为子类对象的原型。当cat的对象被创建的时候,由于cat的构造函数拥有prototype属性,那么cat的实例就会间接指向这个原型对象了(说成间接的是因为每个object都有一个 constructor 属性指向它的构造函数)。
那么问题来了,“当我们修改对象 c1 的name属性的时候,会不会修改它prototype的name属性值呢?”,答案是否定的。
接下来详细解析:
1.访问name属性: 首先当我们第一次访问c1.name的属性的时候,我们会得到值“pipi”,这个和我们预料中的一样。但是计算过程你未必知道。
它计算的过程是这样的:第一步:检查c1对象中是否有name属性,找到的话就返回值,没有就跳到第二步,显然没有找到,因为cat的构造函数中没有定义。第二步:当第一步没有找时,去间接访问
prototype对象所指向的object,如果在 prototype对象中 找到的name属性的话,就返回找到的属性值。如果还是没有找到的话,再去递归地寻找 prototype对象的 prototype对象(去找它的爷爷) ,一直到找到name属性或者没有prototype对象为止。如果到最后还是没有找到name属性的话就返回undefined。

2.设定name属性:当我们设定c1对象的name属性时,及调用 c1.name= ' new name';  这个过程就简单多了。首先检查是否对象已有该属性,若已存在则修改当前值,若不存在则为该对象新增一个属性并设定当前值。值得一提的是,在设定值的过程中没有去访问
prototype属性。

为了加深理解,我们再看一个 read-write-read 的过程,第一次read的时候,由于自己的对象没有name属性,那么就会返回的原型对象的name属性的值。第二步,写入name的值,同样没发现本身对象有name属性,那么就在本身对象上新建一个name属性,然后赋值。第三步,再次读取name属性,由于在第二步中已经新建了name属性,此时就返回在第二步中设定的值。值得一提的是,在这三步中没有改变原型对象的值。

好了,到此详细分析了 javascript对象是如果实现继承的,其实和其他的面向对象语言不一样的是,javascript的继承机制是对象的原型继承而不是类型继承。

呵呵,欢迎看完,有不对的地方欢迎大家讨论!



分享到:
评论
14 楼 userzx110 2012-12-31  
userzx110 写道
咖啡刀 写道
不错的帖子!!!
在你上面说的一个问题!!
"构造函数与其他普通函数区别在于,1.构造函数里有 this关键字,2.调用构造函数是使用的new关键字。"
我觉得这句话很值的商榷!!!
主要是构造函数里有 this关键字这句话,这个我看未必.一如果我选用空构造函数呢?
var a=function(){};
a.prototype.firstName="hello";
a.prototype.secondName="world";
var b=new a();
alert(b.firstName);

二如下形式构造函数:
var a=function(){
return {
firstName:"Holle",
secondName:"World"
}
}
var b=new a();
alert(b.firstName);
这个难道里面具有this关键字吗???

所以我觉得这里应该值得推敲!!


javascript 里的this比较乱,分清情况才能说明,不管是不是构造函数都有this
普通的函数里面的this 指向的是window ,而 构造函数的this是指向本身。(我正在理解)看资料好想this 有四种存在情况,现在我就继承的和普通的来说下

//js所处的环境就是window

      xxname = 'xxTest'; //也可以window.xxname='xxTest'; window可不写
        function xxPutong()
        {
            //这里的this 是指向window
            var xxname = 'xxPutongTest';
            return function ()
            {//传递this到这个匿名方法体内 this还是指向window
                alert(this.xxname);
            }
        }
        xxPutong()();
打印出来是:'xxTest'
但是当代码如下的时候:
  xxname = 'xxTest'; //也可以window.xxname='xxTest'; window可不写
        function xxPutong()
        {
            //这里的this 是指向window
            var xxname = 'xxPutongTest';
            return function ()
            {//传递this到这个匿名方法体内 this还是指向window
                alert(xxname);
            }
        }
        xxPutong()();
打印出来是:'xxPutongTest'
分析:当你直接写xxname的时候而不是this.xxname的时候,匿名function向外找,首先找到了xxPutong里面的xxname 所以就是'xxPutongTest'(我的理解)
xxname = 'xxTest';
        var xxJiCheng = function ()
        {//这里的this都是指向xxJiCheng
            this.xxname = 'xxJiCheng';
            this.getname = function ()
            {//传递this到这个匿名方法体内 this还是指向xxJiCheng
                alert(this.xxname);
            };

        };

        new xxJiCheng().getname();
这个时候得到的反而是内部:'xxJiCheng'
跟上面普通的funtion 同样是alert(this.xxname)但是相反的结果这个得到的是内部的
xxname = 'xxTest';
var xxJiCheng = function ()
        {//这里的this都是指向xxJiCheng
            this.xxname = 'xxJiCheng';
            this.getname = function ()
            {//传递this到这个匿名方法体内 this还是指向xxJiCheng
                alert(xxname);
            };

        };

这个时候得到的反而是外部:'xxTest',当没前缀,通过window.xxname里取得。
跟上面普通的funtion 同样是alert(this.xxname)但是相反的结果这个得到的是内部的 这里打快了应该是外部

13 楼 userzx110 2012-12-31  
咖啡刀 写道
不错的帖子!!!
在你上面说的一个问题!!
"构造函数与其他普通函数区别在于,1.构造函数里有 this关键字,2.调用构造函数是使用的new关键字。"
我觉得这句话很值的商榷!!!
主要是构造函数里有 this关键字这句话,这个我看未必.一如果我选用空构造函数呢?
var a=function(){};
a.prototype.firstName="hello";
a.prototype.secondName="world";
var b=new a();
alert(b.firstName);

二如下形式构造函数:
var a=function(){
return {
firstName:"Holle",
secondName:"World"
}
}
var b=new a();
alert(b.firstName);
这个难道里面具有this关键字吗???

所以我觉得这里应该值得推敲!!


javascript 里的this比较乱,分清情况才能说明,不管是不是构造函数都有this
普通的函数里面的this 指向的是window ,而 构造函数的this是指向本身。(我正在理解)看资料好想this 有四种存在情况,现在我就继承的和普通的来说下

//js所处的环境就是window

      xxname = 'xxTest'; //也可以window.xxname='xxTest'; window可不写
        function xxPutong()
        {
            //这里的this 是指向window
            var xxname = 'xxPutongTest';
            return function ()
            {//传递this到这个匿名方法体内 this还是指向window
                alert(this.xxname);
            }
        }
        xxPutong()();
打印出来是:'xxTest'
但是当代码如下的时候:
  xxname = 'xxTest'; //也可以window.xxname='xxTest'; window可不写
        function xxPutong()
        {
            //这里的this 是指向window
            var xxname = 'xxPutongTest';
            return function ()
            {//传递this到这个匿名方法体内 this还是指向window
                alert(xxname);
            }
        }
        xxPutong()();
打印出来是:'xxPutongTest'
分析:当你直接写xxname的时候而不是this.xxname的时候,匿名function向外找,首先找到了xxPutong里面的xxname 所以就是'xxPutongTest'(我的理解)
xxname = 'xxTest';
        var xxJiCheng = function ()
        {//这里的this都是指向xxJiCheng
            this.xxname = 'xxJiCheng';
            this.getname = function ()
            {//传递this到这个匿名方法体内 this还是指向xxJiCheng
                alert(this.xxname);
            };

        };

        new xxJiCheng().getname();
这个时候得到的反而是内部:'xxJiCheng'
跟上面普通的funtion 同样是alert(this.xxname)但是相反的结果这个得到的是内部的
xxname = 'xxTest';
var xxJiCheng = function ()
        {//这里的this都是指向xxJiCheng
            this.xxname = 'xxJiCheng';
            this.getname = function ()
            {//传递this到这个匿名方法体内 this还是指向xxJiCheng
                alert(xxname);
            };

        };

这个时候得到的反而是外部:'xxTest',当没前缀,通过window.xxname里取得。
跟上面普通的funtion 同样是alert(this.xxname)但是相反的结果这个得到的是内部的
12 楼 userzx110 2012-12-31  
kinggooo 写道
正好在看prototype所以搜到了楼主的代码。看了一下,自己也试验了。总结了一下也算稍微明白了其中的一些过程。补充几点东西把,经过试验,发现当你在New一个对象的时候,从这个操作的行为上来看,有2种可能的推断。第一种是他以你的构造函数也就是Cat对象为模版在内存里重新建立了一个新对象。并且将Cat中的属性注入到新的对象中,然后会将Cat的原型对象的construtor属性也注入到新对象中,因此你可以在新对象后面用“点”操作符点出construcor属性,它其实是指向Cat对象的。当你get新对象的属性时它首先在新对象内部寻找该属性,如果不存在,它会寻找constructor.prototype对象中是否有需要的属性,如果还是没有就继续递归直到根对象。而当你set的时候,因为c1.name被你动态的设进去了,所以再次get的时候自然是新对象本身的name属性,当然它是不会重写它原型对象中的name属性的。
第二种解释是,当你创造一个新对象时,它首先将prototype内的属性包括constructor属性copy到新对象里,然后对这个对象执行Cat里的this.name =xxx 这样新对象里的name属性自然被覆盖了,但是这种覆盖显然不会影响到原型的name,因为新对象本身只不过是原型的拷贝而已。其他的我想也没什么多说的了,如果有发现错误,可以在下面提出来,在讨论讨论。


看这个帖子已经很久没人回了:今天收到说明是第二种解释。
var animal = function ()  //var 定义 animalie会被即可的执行
        {   //这就是constructor function 了 
            this.name = 'pipi';
            this.age = 10;
            this.height = 0;
        }
        //alert(animal().name); //(错误语句)
        //建立一个动物的实例    
var a1 = new animal();
var cat = function ()
        {
            this.play = function ()
            {
                alert('cat play');
            };
            this.name = "xxPiPi";
        }
        cat.prototype = new animal();   //所以这里是覆盖
        var c1 = new cat();
        alert(c1.name);
这里打印出来的是 :pipi,说明
11 楼 liuxinsudi 2012-03-20  
题目挺大的,要努力才行啊
10 楼 lovelylife 2008-05-13  
我也学了点,自己尝试写了下,但是感觉不是很好
不知道我说的对不对, 现在流行的框架prototype和。。。
他们的继承做的还不错, 但是给我的理解是,copy, 并非像C++那样的继承, 写法上很复杂, 真正去用继承去做项目的人不多, 大批的人在谈论这个, 但是实际效果不是很好
9 楼 jimichan 2008-05-07  
to 咖啡刀 

感谢你的提醒。
那句话是表达的有问题。本意是指this关键字在构造函数里面会起到特定的作用。
构造函数不一定非要有this关键字。你可以试试在构造函数里面return一个非Function对象试试,js很有意思。
8 楼 kinggooo 2008-04-07  
正好在看prototype所以搜到了楼主的代码。看了一下,自己也试验了。总结了一下也算稍微明白了其中的一些过程。补充几点东西把,经过试验,发现当你在New一个对象的时候,从这个操作的行为上来看,有2种可能的推断。第一种是他以你的构造函数也就是Cat对象为模版在内存里重新建立了一个新对象。并且将Cat中的属性注入到新的对象中,然后会将Cat的原型对象的construtor属性也注入到新对象中,因此你可以在新对象后面用“点”操作符点出construcor属性,它其实是指向Cat对象的。当你get新对象的属性时它首先在新对象内部寻找该属性,如果不存在,它会寻找constructor.prototype对象中是否有需要的属性,如果还是没有就继续递归直到根对象。而当你set的时候,因为c1.name被你动态的设进去了,所以再次get的时候自然是新对象本身的name属性,当然它是不会重写它原型对象中的name属性的。
第二种解释是,当你创造一个新对象时,它首先将prototype内的属性包括constructor属性copy到新对象里,然后对这个对象执行Cat里的this.name =xxx 这样新对象里的name属性自然被覆盖了,但是这种覆盖显然不会影响到原型的name,因为新对象本身只不过是原型的拷贝而已。其他的我想也没什么多说的了,如果有发现错误,可以在下面提出来,在讨论讨论。
7 楼 咖啡刀 2008-04-02  
不错的帖子!!!
在你上面说的一个问题!!
"构造函数与其他普通函数区别在于,1.构造函数里有 this关键字,2.调用构造函数是使用的new关键字。"
我觉得这句话很值的商榷!!!
主要是构造函数里有 this关键字这句话,这个我看未必.一如果我选用空构造函数呢?
var a=function(){};
a.prototype.firstName="hello";
a.prototype.secondName="world";
var b=new a();
alert(b.firstName);

二如下形式构造函数:
var a=function(){
return {
firstName:"Holle",
secondName:"World"
}
}
var b=new a();
alert(b.firstName);
这个难道里面具有this关键字吗???

所以我觉得这里应该值得推敲!!
6 楼 kinggooo 2008-04-02  
正好在看prototype所以搜到了楼主的代码。看了一下,自己也试验了。总结了一下也算稍微明白了其中的一些过程。补充几点东西把,经过试验,发现当你在New一个对象的时候,从这个操作的行为上来看,有2种可能的推断。第一种是他以你的构造函数也就是Cat对象为模版在内存里重新建立了一个新对象。并且将Cat中的属性注入到新的对象中,然后会将Cat的原型对象的construtor属性也注入到新对象中,因此你可以在新对象后面用“点”操作符点出construcor属性,它其实是指向Cat对象的。当你get新对象的属性时它首先在新对象内部寻找该属性,如果不存在,它会寻找constructor.prototype对象中是否有需要的属性,如果还是没有就继续递归直到根对象。而当你set的时候,因为c1.name被你动态的设进去了,所以再次get的时候自然是新对象本身的name属性,当然它是不会重写它原型对象中的name属性的。
第二种解释是,当你创造一个新对象时,它首先将prototype内的属性包括constructor属性copy到新对象里,然后对这个对象执行Cat里的this.name =xxx 这样新对象里的name属性自然被覆盖了,但是这种覆盖显然不会影响到原型的name,因为新对象本身只不过是原型的拷贝而已。其他的我想也没什么多说的了,如果有发现错误,可以在下面提出来,在讨论讨论。
5 楼 jimichan 2007-09-04  
呵呵,是的,谢谢提醒,一会补上
4 楼 simbel 2007-09-04  
引用
var = function(){  //这就是constructor function 了

是否掉了animal?
3 楼 jimichan 2007-09-04  
引用
不错,引用一下,能否讲一下原型模式呢?

javascript的原型机制和设计模式中的原型模式不一样,原型模式中使用对象clone技术,它是完全拷贝一个备份,而js的原型机制则是在运行时动态访问上层(或多层之上)的原型对象。原型模式最多只能使用到上一层对象的拷贝值。
2 楼 goalbell 2007-09-04  
不错,引用一下,能否讲一下原型模式呢?
1 楼 jimichan 2007-09-03  

相关推荐

    javascript原型继承工作原理和实例详解_.docx

    ### JavaScript原型继承工作原理及实例详解 ...理解JavaScript中的原型继承机制对于深入掌握这门语言至关重要,特别是在进行高级编程任务时。希望本文能帮助您更好地理解和应用JavaScript中的原型继承。

    详解Javascript继承的实现

    本文将深入探讨JavaScript继承的实现方式,以及其中的问题和解决方案。 首先,我们来看混合方式的实现,这种方式结合了原型链和对象冒充。在JavaScript中,构造函数是用于创建特定类型对象的函数。例如,`Employee`...

    JavaScript继承的特性与实践应用深入详解

    在实际应用中,JavaScript的继承机制不仅适用于简单的类层次结构,还能处理复杂的继承场景,如多重继承和组合继承。多重继承是指一个对象可以继承多个父对象的属性和方法,而在JavaScript中可以通过原型链的叠加实现...

    JavaScript工作原理及对象详解.docx

    JavaScript是一种广泛应用于Web开发的动态编程语言,它的工作原理...理解对象的创建、访问和删除机制对于掌握JavaScript编程至关重要。通过深入学习这些概念,开发者能够更好地构建和维护复杂的JavaScript应用程序。

    javascript原型继承工作原理和实例详解

    JavaScript中的原型继承是基于原型链的一种继承机制,它是JavaScript实现面向对象编程的关键概念之一。在JavaScript中,每个对象都有一个内部属性`[[Prototype]]`(通常通过`__proto__`或`Object.getPrototypeOf()`...

    《JavaScript基础与案例开发详解》

    - **原型链**:JavaScript通过原型链实现继承机制。每个对象都有一个指向其原型对象的内部链接,原型对象包含了可以被共享的方法和属性。 - **构造函数与原型**:通过构造函数可以创建对象的实例,原型对象用于为...

    JavaScript 继承 封装 多态实现及原理详解

    3. 多态:在JavaScript中,可以通过函数重写(function overriding)或者条件判断,结合继承机制来实现方法的多态。通过将具有相同功能的方法在子类中重新实现,以适应不同子类的具体需求。 JavaScript中的继承封装...

    最好的javascript学习教程-JavaScript使用详解

    7. 类与类式继承:ES6引入了class语法糖,提供了更接近传统面向对象编程的写法,但JavaScript的继承机制仍然是基于原型。 四、数组和集合操作 8. 高级数组操作:包括map、filter、reduce、forEach等,这些方法能...

    JavaScript原型继承和原型链原理详解

    原型继承和原型链原理详解中也提到了借用构造函数(也称伪造对象或经典继承),这种方法通过在子类型构造函数的内部调用超类型构造函数,从而继承超类型的实例属性。但这种模式无法实现超类型原型中的方法继承,并且...

    Javascript原型链的原理详解

    总结来说,JavaScript的原型链是一种基于原型的继承机制,通过`__proto__`属性和`Object.prototype`形成链式结构,允许对象间共享属性和方法,从而实现继承。理解这个概念对于深入学习JavaScript的面向对象编程至关...

    【JavaScript源代码】JavaScript继承的三种方法实例.docx

    ### JavaScript继承的三种方法实例详解 #### 一、概述 在JavaScript中,虽然原生语言层面没有提供传统意义上的“类”这一概念,但它通过构造函数和原型链等机制实现了类的功能,尤其是继承这一核心概念。继承是...

    JavaScript 内存机制分析

    ### JavaScript内存机制详解 #### 简介 在探索JavaScript内存机制之前,我们先了解一些基础知识。每一种编程语言都有其独特的内存管理机制。例如,C语言提供了低级别的内存管理功能,如`malloc()`和`free()`等函数...

    基于JavaScript实现继承机制之原型链(prototype chaining)的详解

    如果用原型方式重定义前面例子中的类,它们将变为下列形式: 代码如下:function ClassA() {} ClassA.prototype.color = “blue”;ClassA.prototype.sayColor = function () { alert(this.color);}; ...

    JavaScript 继承机制的实现(待续)

    ### JavaScript继承机制详解 #### 一、对象冒充方法实现继承 **原理**: JavaScript中的对象冒充是一种实现继承的方式,其核心思想是利用构造函数内部的`this`关键字指向新创建的对象实例,并通过调用另一个构造...

    JavaScript实战.pdf 中文完整版

    7. 类与模块:在ES6中,了解类的概念及其与传统构造函数的关系,以及模块系统(import和export)的应用。 三、高级JavaScript特性 8.闭包:理解闭包的定义,以及它在内存管理、数据封装和异步编程中的重要性。 9. ...

    详解javascript new的运行机制

    理解`new`运算符的工作原理有助于我们更好地掌握JavaScript中的对象创建和继承。`new`操作符在创建实例时,不仅提供了属性和方法的共享(通过原型链),还确保了每个实例都有自己的属性副本(通过`this`的绑定)。...

    JavaScript是如何实现继承的(六种方式)_.docx

    在面向对象编程语言中,继承是一种非常重要的机制,它允许我们定义一个类去继承另一个类的特性(属性和方法)。这种特性使得代码复用变得可能,提高了软件开发的效率。在JavaScript中,由于其动态性质,继承的实现...

    pro javascript techniques.pdf Modern JavaScript

    - **原型链**:理解JavaScript的对象继承机制,学习如何通过原型链实现对象之间的继承关系。 - **异步编程**:掌握Promise、async/await等现代JavaScript异步编程技术,提高代码的可读性和可维护性。 - **模块化...

Global site tag (gtag.js) - Google Analytics