原型(prototype)

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

 var obj1 = {name:"obj"};
var obj2 = Object.create(obj1);
obj2.id = 1;//{id: 1, name: "obj"}
var obj3 = Object.create(obj2);
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。这就是一个原型链。仍旧以上面的代码为例,

 console.log(obj3.name);//obj
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值的结果都发生了变化。

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

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

 obj3.name ="obj3";//override the name
console.log(obj3.name);//obj3
obj1.name = "another name";
console.log(obj3.name);//obj3
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代表定义了某个属性。下面的代码对三个对象检测了三个属性。

 obj1.hasOwnProperty("name");//true
obj1.hasOwnProperty("id");//false
obj1.hasOwnProperty("type");//false obj2.hasOwnProperty("name");//false
obj2.hasOwnProperty("id");//true
obj2.hasOwnProperty("type");//false obj3.hasOwnProperty("name");//true
obj3.hasOwnProperty("id");//false
obj3.hasOwnProperty("type");//true

isPrototypeOf() 函数

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

 obj1.isPrototypeOf(obj2)//true
obj1.isPrototypeOf(obj3)//true
obj2.isPrototypeOf(obj3)//true
obj3.isPrototypeOf(obj3)//false

Object.prototype

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

 Object.prototype.myProperty = "myProperty";
var x = {};
console.log(x.myProperty);//myProperty
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都是构造函数。

 Date.prototype.showToday = "it is today";
Array.prototype.showLength = "my length is...";
Function.prototype.showParameters = "parameters...";
String.prototype.showContext = "context"; var a = new Date();
console.log(a.showToday);//it is today var b = [];
console.log(b.showLength);//my length is... var c = function(){};
console.log(c.showParameters);//parameters... var d = "";
console.log(d.showContext);//context

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

 String.prototype.sayHello = function(){
alert("Hello, I am a string!");
}
var s = "";
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

JavaScript面向对象编程(一)原型与继承的更多相关文章

  1. (二)Javascript面向对象编程:构造函数的继承

    Javascript面向对象编程:构造函数的继承   这个系列的第一部分,主要介绍了如何"封装"数据和方法,以及如何从原型对象生成实例. 今天要介绍的是,对象之间的"继承 ...

  2. Javascript面向对象编程:构造函数的继承

    今天要介绍的是,对象之间的"继承"的五种方法. 比如,现在有一个"动物"对象的构造函数. function Animal(){ this.species = & ...

  3. Javascript面向对象编程(二):构造函数的继承 作者:yuan一峰

    Javascript面向对象编程(二):构造函数的继承   作者: 阮一峰 日期: 2010年5月23日 这个系列的第一部分,主要介绍了如何"封装"数据和方法,以及如何从原型对象生 ...

  4. JavaScript 面向对象编程(三):非构造函数对象的继承

    JavaScript 面向对象编程(三):非构造函数对象的继承 一.什么是"非构造函数"的继承? 比如,现在有一个对象,叫做"中国人". var Chinese ...

  5. JavaScript 面向对象编程(二):继承

    Javascript面向对象编程(二):构造函数的继承 这个系列的第一部分,主要介绍了如何"封装"数据和方法,以及如何从原型对象生成实例. 今天要介绍的是,对象之间的"继 ...

  6. (三)Javascript面向对象编程:非构造函数的继承

    Javascript面向对象编程:非构造函数的继承   这个系列的第一部分介绍了"封装",第二部分介绍了使用构造函数实现"继承". 今天是最后一个部分,介绍不使 ...

  7. Javascript面向对象编程(三):非构造函数的继承(对象的深拷贝与浅拷贝)

    Javascript面向对象编程(三):非构造函数的继承   作者: 阮一峰 日期: 2010年5月24日 这个系列的第一部分介绍了"封装",第二部分介绍了使用构造函数实现&quo ...

  8. javascript 面向对象编程(工厂模式、构造函数模式、原型模式)

      javascript 面向对象编程(工厂模式.构造函数模式.原型模式) CreateTime--2018年3月29日17:09:38 Author:Marydon 一.工厂模式 /** * 工厂模 ...

  9. 【转】javascript面向对象编程

    摘要:本文本来是想自己写的,奈何花了好长时间写好之后忘记保存,还按了刷新键,一键回到解放前,索性不写了,所以本文是转载的. 面向对象编程是用抽象方式创建基于现实世界模型的一种编程模式,主要包括模块化. ...

随机推荐

  1. 常用批处理命令总结3之Find和FindStr

    原文:常用批处理命令总结3之Find和FindStr find 作用:从文件中收索字符串 格式:find 参数 "字符串" 路径\文件名 参数: /V 显示所有未包含指定字符串的行 ...

  2. Quartz使用-入门使用(java定时任务实现)

    注:这里使用的是Quartz1.6.5版本号(包:quartz-1.6.5.jar) //測试main函数 //QuartzTest.java package quartzPackage; impor ...

  3. IplImage 封装释放

    IplImage是openCV库中非常重要的一个结构体,库中的图像都是保存为这个结构体后再进行操作的,详细结构例如以下: </pre><pre> typedef struct ...

  4. 一些常用的jquery数字正则表达式

    使用 <script type="text/javascript"> function validate(){ var reg = new RegExp("^ ...

  5. 新服务器部署sqlserver之前的准备

    当你有一个新的服务器需要部署的时候,如果没有部署过的经验很可能会走很多误区,并且给以后的维护工作加大难度,我在这就把我部署服务器的一些经验跟大家分享一下. 1.登陆服务器以后先将物理盘按照64k为分配 ...

  6. 对于GetBuffer() 与 ReleaseBuffer() 的一些分析

    先 转载一段别人的文章 CString类的这几个函数, 一直在用, 但总感觉理解的不够透彻, 不时还实用错的现象. 今天抽时间和Nico一起分析了一下, 算是拨开了云雾: GetBuffer和Rele ...

  7. C# Winform 界面线程的Invoke死锁,以及Application.DoEvent的问题

    1.对于非界面线程来说,Invoke是把一个操作丢到界面线程的队列里,然后阻塞,等到这个操作被界面线程完成后,才继续后续操作.也就是说,Invoke是同步的. 问题来了,如果界面线程此时正在等待这个非 ...

  8. php学习之路:php在iconv功能 详细解释

    iconv函数库可以完毕各种字符集间的转换,是php编程中必不可少的基础函数库. 使用方法例如以下: $string = "亲爱的朋友欢迎訪问胡文芳的博客.希望给您带来一点点的帮助!&quo ...

  9. 你不明白 String 类别

    序 前几篇文章一直在研究 WEB 安全的问题,并且近期工作中也一直在解决这些安全漏洞.当然,兴许的博客中还会就这些暴露出来的问题谈谈我的解决方式,仅仅只是,在这篇博客中,我想谈点别的问题.并且对于我们 ...

  10. POJ2533 Longest Ordered Subsequence 【最长递增子序列】

    Longest Ordered Subsequence Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 32192   Acc ...