zoukankan      html  css  js  c++  java
  • JavaScript面向对象编程(一)原型与继承

    原型(prototype)

    JavaScript是通过原型(prototype)进行对象之间的继承。当一个对象A继承自另外一个对象B后,A就拥有了B中定义的属性,而B就成为了A的原型。JavaScript中定义了Object.create()方法。该方法创建一个对象,将方法的第一个参数作为所创建对象的原型(这个方法支持可选的第二个参数,用于对对象的属性进行描述,详细可以参考《JavaScript:The Definitive Guide》)。如下面代码所示:

    1 var obj1 = {name:"obj"};
    2 var obj2 = Object.create(obj1);
    3 obj2.id = 1;//{id: 1, name: "obj"}
    4 var obj3 = Object.create(obj2);
    5 obj3.type = "type"; //{type: "type", id: 1, name: "obj"}

    其中obj1是obj2的原型。所以obj2就可访问obj1中定义的name属性。在代码的第三行给obj2添加了一个自己的属性id并且赋值为1。这样obj2的值就是{id: 1, name: "obj"}。同理,obj2又作为obj3的原型,这样obj3就可访问obj2中定义的属性id以及obj2从obj1中继承的属性name。代码的第五行给obj3添加了自己的属性type并赋值为"type"。所以obj3的值就是{type: "type", id: 1, name: "obj"}。

    原型链(prototype chain)

    原型链是访问对象属性的机制。当我们访问一个对象的属性时,先从对象本身定义的属性找,如果找不到,再从对象的原型找,如果再找不到就从原型的原型找,依次类推,直到在Object。prototype都找不到的话,就返回undefined。这就是一个原型链。仍旧以上面的代码为例,

    1 console.log(obj3.name);//obj
    2 console.log(obj3.othername);//undefinded

    当要访问obj3的name属性时,先从obj3自己定义的属性中寻找。而obj3只定义了type属性,那么就从原型链的下一个节点,即obj2中寻找。而obj2只定义了id属性,所以再向下一个节点,从obj2的原型寻找,即obj1。obj1中有定义了name属性,那么就返回obj1的name属性值,即"obj"。这个搜索的原型链就是obj3->obj2->obj1->Object.prototype。而整个原型链都没有定义othername这个属性,所以访问它的结果就是undefined。

    必须注意的是,这个name属性的值是保存在obj1中的,因此当obj1的name值改变之后,从所有继承于它的对象中name的值都要改变(前提是这些对象没有自己定义name属性)。而且这个是动态的,在对象生成之后改变其原型的属性值也会产生效果。原因是name属性是从其原型中访问的。看下面代码,我们更新了obj1.name的值,然后发现从继承于obj1的对象访问name值的结果都发生了变化。

    1 obj1.name="new name";
    2 console.log(obj2.name);//new name
    3 console.log(obj3.name);//new name

    对象可以通过同名自有属性覆盖所继承的非只读属性。(属性的只读性在此文中暂不讨论,会在后续文章中讨论)。看下面代码:

    1 obj3.name ="obj3";//override the name
    2 console.log(obj3.name);//obj3
    3 obj1.name = "another name";
    4 console.log(obj3.name);//obj3
    5 console.log(obj2.name);//another name

    因为obj3自己定义了name属性,那么从obj3访问name属性时,就不必再从原型链的后续节点中寻找。所以obj1的name值改变对obj3.name没有影响。反观obj2,因为它没有自定义name属性,所以访问obj2.name时会继续搜寻原型链。当搜寻到obj1时找到了name属性,所以就访问到了obj1的name属性的值。

    hasOwnProperty()函数

    hasOwnProperty()可以判断对象是否定义了某个属性,而不是从其原型继承过来或者其原型链根本没有这一属性。该函数的参数是属性名,返回true代表定义了某个属性。下面的代码对三个对象检测了三个属性。

     1 obj1.hasOwnProperty("name");//true
     2 obj1.hasOwnProperty("id");//false
     3 obj1.hasOwnProperty("type");//false
     4 
     5 obj2.hasOwnProperty("name");//false
     6 obj2.hasOwnProperty("id");//true
     7 obj2.hasOwnProperty("type");//false
     8 
     9 obj3.hasOwnProperty("name");//true
    10 obj3.hasOwnProperty("id");//false
    11 obj3.hasOwnProperty("type");//true

    isPrototypeOf() 函数

    通过isPrototypeOf() 函数可以判断一个对象是否处于另外一个对象的原型链之中。需要注意的是,并不一定是直接继承的,在原型链中即可,看代码的第2行。

    1 obj1.isPrototypeOf(obj2)//true
    2 obj1.isPrototypeOf(obj3)//true
    3 obj2.isPrototypeOf(obj3)//true
    4 obj3.isPrototypeOf(obj3)//false

    Object.prototype

    前文有提到,在搜索原型链的过程中,如果在Object.prototype都搜索不到的话,那么就会返回undefined。因为所有的对象都从Object.prototype中继承属性和方法(当然可以用自定义的同名属性或方法覆盖)。所以,给Object.prototype添加属性或方法时,可从所有的对象中访问。

    1 Object.prototype.myProperty = "myProperty";
    2 var x = {};
    3 console.log(x.myProperty);//myProperty
    4 console.log(obj3.myProperty);//myProperty

    代码中新定义了一个对象直接量x,它不定义任何自有的属性。但是可以从x访问myProperty原因是Object.prototype中有定义。从MDN中摘录一句话”All objects in JavaScript are descended from Object Object; all objects inherit methods and properties from Object.prototypeObject.prototype”。Object是构造函数。使用new Object()构造的对象都以Object.prototype为原型。

    new关键字的作用初步总结下来就是:new Func()生成一个对象,用跟在new后面的函数Func对所生成的对象进行初始化,并且将Func.prototype作为所生成对象的原型。所以,所有的对象不论是用构造函数 new Object()构造的,或者是使用直接量初始化的,都从Object.prototype中继承属性和方法。同理,数组以Array.prototype为原型。函数以Function.prototype为原型。日期对象以Date.prototype为原型。字符串以String.prototype为原型。其中,Array、Function、Date和String都是构造函数。

     1 Date.prototype.showToday = "it is today";
     2 Array.prototype.showLength = "my length is...";
     3 Function.prototype.showParameters = "parameters...";
     4 String.prototype.showContext = "context";
     5 
     6 var a = new Date();
     7 console.log(a.showToday);//it is today
     8 
     9 var b = [];
    10 console.log(b.showLength);//my length is...
    11 
    12 var c = function(){};
    13 console.log(c.showParameters);//parameters...
    14 
    15 var d = "";
    16 console.log(d.showContext);//context

    在Object、Array、Function、Date和String中可以添加方法和属性,以达到扩展功能的效果。比如我们给字符串添加一个sayHello方法。

    1 String.prototype.sayHello = function(){
    2     alert("Hello, I am a string!");
    3 }
    4 var s = "";
    5 s.sayHello();

    后续的文章JavaScript面向对象编程(二)构造函数和类会对new 关键字和构造函数继续讨论。

    参考文章:

    1)《JavaScript:The Definitive Guide》第六章

    2)MDN https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype

  • 相关阅读:
    组成原理(九):数值表示
    组成原理(八):DMA方式
    组成原理(七):程序查询及中断方式
    组成原理(六):I/O概述
    组成原理(五):缓存,辅存
    组成原理(四):存储器扩展,汉明码,访存提速
    组成原理(三):存储器概述,RAM,ROM
    组成原理(二):总线
    组成原理(一):入门
    django_python3_pycharm_"font&&size"
  • 原文地址:https://www.cnblogs.com/zhenchaoni/p/JavaScript_prototype.html
Copyright © 2011-2022 走看看