原型(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. 利用EntityFramework获得双色球数据库

    原文 利用EntityFramework获得双色球数据库 双色球想必大家都很熟悉了,尽管屡买屡不中,但还是会买.以前就想过利用双色球的走势图得到双色球的数据库,至于得到数据库干什么倒没想过,不过对以往 ...

  2. inux平台的C与C++

    课堂里学不到的C与C++那些事(一) 首先,声明一下这是一个系列的文章.至于整个系列有多少篇,笔者也不知道,不知道有多少篇,也不知道多久会更新一篇.反正只有一个原则,写出来的文 章能见得人才会公布出来 ...

  3. struts2文件下载 <result type="stream">

    <!--struts.xml配置--> <action name="download" class="com.unmi.action.DownloadA ...

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

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

  5. phpstorm集成phpunit

    1.下载phpunit.phar,将该文件放到某个工程中 2.File > Settings > Languages & Frameworks > PHP > PHPU ...

  6. 安装Codeception框架

    安装Codeception框架 打开终端,进入项目根目录: composer require "codeception/codeception:*" 安装完成,在vendor目录会 ...

  7. Android学习笔记:Home Screen Widgets(1):大约Widget

    什么是Home Screen Widgets Home screen Widget即称为小工具或者中文小工具,是显示在主页上的views,通过后台进程更新view的数据. Android由AppWid ...

  8. 快速构建Windows 8风格应用29-捕获图片与视频

    原文:快速构建Windows 8风格应用29-捕获图片与视频 引言 本篇博文主要介绍Windows 8中相机的概念.捕获图片与视频的基本原理.如何实现捕获图片与视频.相机最佳实践. 一.相机 关于相机 ...

  9. Web神器WebStorm 8.0测试版发放(慧都独家)

    WebStorm 8.0测试版的发放,标志着WebStorm规划构建的发展成熟. 此次WebStorm 8.0测试版的主要变化是支持高级的AngularJS和集成Spy-js JavaScript跟踪 ...

  10. xfire集成spring构建webservice

    前言:xfire.spring都是比较流行的技术,这里就不再赘述他们各自的优点:本文着重介绍xfire和spring的整合,不会做太深入的探究. 服务端 1. web.xml配置 spring配置部分 ...