夜深风竹敲秋韵,万叶千声皆是恨。

原型链对于JavaScript来说是个很核心的概念。JavaScript不是基于类模板的面向对象语言;反而,它的面向对象机制是基于原型的。我们不可能说某个对象属于什么类,但却可以得到某个对象的原型对象。原型对象相当于一个父级代理,当属性在某个对象中找不到时,就会委托该对象的原型去查找。

原型链的基础

JavaScript的每个对象,都可以有一个隐式的链接(名为__proto__),指向它的原型对象。这次,我冒天下之大不韪(__proto__是私有属性,不能直接对它进行操作),显示地定义几个对象和它们的原型关系。

a = {k1: 'a1'};
b = {k1: 'b1', k2: 'b2'};
c = {k1: 'c1', k2: 'c2', K3: 'c3'}; a.__proto__ = b;
b.__proto__ = c;
c.__proto__ = undefined;

上面的例子中,我们首先定义了三个对象a,b,c。接下来构造它们的原型关系。其中a的原型是b,b的原型是c,c没有原型。a、b、c形成了一条链式结构,这条链式结构在c处终止。这样一条链式结构就是原型链。

原型链的作用主要在于对象的取值操作。当我们根据属性名从对象中取值时,首先会在当前对象中查找。如果在当前对象中查找不到,就会上升到该对象的原型中继续查找。如果仍然查找不到,就会继续上升到原型的原型……这个过程会一直持续下去,直到在某一次查找到或者原型链终止。所以下面的返回结果是显然的:

a.k1 //=> 'a1'
a.k2 //=> 'b2'
a.k3 //=> 'c3'
a.k4 //=> undefined

然而对象的设值和删值就不会参考原型链了,它只是对当前对象的操作。下面的例子具有一定的启发性:

delete a.k2 //无效,a中没有k2属性
a.k2 //=> 'b2'
a.k2 = 'a2' //只会影响对象a,不会影响对象b
a.k2 //=> 'a2'
delete a.k2 //有效,删除a的k2属性
a.k2 //=> 'b2' a的k2属性被删除,b的k2属性暴露了出来

原型链的作用

原型链的作用主要有以下两个方面。

将共享属性和方法放到原型中去

如果多个对象共享一些属性和方法,那就让这些对象指向同一个原型,在原型中定义这些属性和方法。这样共享的属性和方法只用在原型处一次定义,而无需在每个对象中重复定义。

继承

当对象a想要继承b的属性和方法时,只需简单地将b定义为a的原型即可。

关于继承,还有一点补充。除了原型继承之外,将对象b的属性和方法拷贝到a中去也能实现继承。这里b的属性和方法就直接存在于a中,而不是通过原型获取。

构造器函数与原型链

构造器函数可以帮助我们构建原型链。构造函数的特色如下:

//一般构造器函数首字母大写
function Foo(name) {
this.name = name;
//一般不用返回任何值
}

构造器函数中,this绑定的是一个新建的对象,并且函数默认会返回这个对象。当定义构造器函数时,不要显示地返回一个值,除非你知道自己是在做什么。

每个函数都有一个名为prototype链接,它指向一个对象。当函数作为普通函数调用时,这个链接没什么用处。只有当它作为一个构造器函数调用时,它才与原型链构成联系。其实很简单,构造器函数新建的对象,其原型就是该函数的prototype链接的对象。所以一定有下面的关系:

new Foo().__proto__ === Foo.prototype;

关于属性共享和继承的策略,可以对应到构造器函数中去。由于Foo.prototype就是new Foo()的原型,所以将共享属性和方法放到Foo.prototype中去就可以了。

function Foo(name) {
this.name = name; //对象的示例属性要绑定到this上
} //对象的共享属性和方法绑定到Foo.prototype上
Foo.prototype.attr = 'attr';
Foo.prototype.foo = function() {};

继承的实现其实有很多种方式,但很难找出直接的方式。例如我们有构造器函数A,B,C,现在想要C继承自B,B继承自A,可以通过下面的方式实现:

function A() {}
function B() {}
function C() {} A.prototype = new B();
B.prototype = new C();

不是很直观,但确实做到了继承。老实说,这不是最好的方式,因为中间对象采用new B()和new C()的方式构造,将B和C的实例变量引入到了原型链中来了。

ES5中有关原型链的函数

为了行文上的方便,我在上面多处提到了__proto__。但是不要通过__proto__隐式链接去处理原型,__proto__不是JavaScript的标准,不同浏览器对于它们有不同的解释。也就是说,我们不能像下面这样做:

a.__proto__ = b; //=>不能像这样构造原型关系
a.__proto__; //=>不能像这样得到对象的原型

ES5(EMCAScript5)中Object对象增加了两个新的方法,分别是create和getPrototypeOf,分别实现上面的两种效果。上面的例子就可以像下面这样改写了。

a = Object.create(b);     //返回一个以b作为原型的新对象
Object.getPrototypeOf(a); //返回a的原型对象

方法参考:

(四)我的JavaScript系列:原型链的更多相关文章

  1. javaScript系列 [04]-javaScript的原型链

    [04]-javaScript的原型链 本文旨在花很少的篇幅讲清楚JavaScript语言中的原型链结构,很多朋友认为JavaScript中的原型链复杂难懂,其实不然,它们就像树上的一串猴子. 1.1 ...

  2. JavaScript的原型链继承__propt__、prototype、constructor的理解、以及他们之间相互的关系。

    回想自己已经工作了有一段时间了,但是自己对JavaScript的原型链.和继承的理解能力没有到位,最近他们彻底的整理并且复习了一遍. 本案例中部分文案来自网络和书籍,如有侵权请联系我,我只是把我的理解 ...

  3. Javascript的原型链图

    90%的前端或者js程序员或者老师们对Javascript懂得不比这个多 给手机看的 但是这个图里的所有褐色单向箭头链就是Javascript的原型链(颜色标注对理解js原型链很关键) 这图中的各个_ ...

  4. javascript prototype原型链的原理

    javascript prototype原型链的原理 说到prototype,就不得不先说下new的过程. 我们先看看这样一段代码: <script type="text/javasc ...

  5. javaScript(原型链)

    在了解javaScript的原型链之前,我们得先来看一下原型是什么. 在javaScript中,所有的函数都会有着一个特别属性:prototype(显示原型属性):当我们运行如下代码时: functi ...

  6. 理解JavaScript的原型链

    1. 什么是对象 在JavaScript中,对象是属性的无序集合,每个属性存放一个原始值.对象或函数. 1.1 创建对象 在JavaScript中创建对象的两种方法: ① 字面上: var myObj ...

  7. JavaScript扩展原型链浅析

    前言 上文对原型和原型链做了一些简单的概念介绍和解析,本文将浅析一些原型链的扩展. javaScript原型和原型链 http://lewyon.xyz/prototype.html 扩展原型链 使用 ...

  8. javascript的原型链那些事

    如果你对javascript的原型链还有任何疑问,请看这篇文章 进入主题 前言 原型链的规则不百分百适用于所有情况 显式原型:prototype,是一个对象{} 隐式原型:__proto__,是一个对 ...

  9. javascript基础学习系列-原型链模式

    1.demo代码如下: 2.画图如下: 3.规则: 1)每一个函数数据类型(普通函数/类)都有一个天生自带的属性:prototype(原型),并且这个属性是一个对象数据类型的值 2)并且prototy ...

  10. javascript总结系列49:javaScript教程:原型链不可变

    <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8& ...

随机推荐

  1. MSSQl分布式查询(转)

    MSSQlServer所谓的分布式查询(Distributed Query)是能够访问存放在同一部计算机或不同计算机上的SQL Server或不同种类的数据源, 从概念上来说分布式查询与普通查询区别 ...

  2. css3中变形处理

    transfrom功能 在css3 中可以使用transfrom功能实现文字或图像的旋转,缩放,倾斜,移动等变形处理 deg是css3中使用的一种角度单位. 旋转: 使用rotate方法,在参数中加入 ...

  3. SVN&nbsp;被防火墙阻止的解决方法

    SVN 被防火墙阻止的解决方法: 1. 进入WIN7的防火墙,看到有的SVN服务是被阻止的,专用的和公用的要设置为允许被防火墙阻止的解决方法" TITLE="SVN 被防火墙阻止的 ...

  4. 1.14不使用回车键来读取n个字符

    read是一个重要的bash命令,它用于从键盘或标准输入中读取文本.可以使用read以交互的形式读取来自用户的输入,不过read能做的远不止这些.很多编程语言的输入库都是从键盘读取输入,且只有回车键按 ...

  5. Vector源码剖析

    参考:http://blog.csdn.net/ns_code/article/details/35793865

  6. C# 获取外网IP和运营商和城市

    /// <summary> /// 获取客户端外网IP,省份,城市,运营商 /// 2012年12月18日 15:07 /// </summary> public class ...

  7. 51Nod - 1154 回文串划分(最少回文串dp)

    回文串划分 有一个字符串S,求S最少可以被划分为多少个回文串. 例如:abbaabaa,有多种划分方式.   a|bb|aabaa - 3 个回文串 a|bb|a|aba|a - 5 个回文串 a|b ...

  8. 程序员必备,C#各类项目、开源项目插件资料收藏

    一.AOP框架     Encase 是C#编写开发的为.NET平台提供的AOP框架.Encase独特的提供了把方面(aspects)部署到运行时代码,而其它AOP框架依赖配置文件的方式.这种部署方面 ...

  9. org.apache.commons.httpclient和org.apache.http.client区别(转)

    官网说明: http://hc.apache.org/httpclient-3.x/ Commons HttpClient项目现已结束,不再开发.它已被其HttpClient和HttpCore模块中的 ...

  10. 2.关键字global,nonlocal

    count=0 def func(): print(count) count+=1 func() UnboundLocalError: local variable 'count' reference ...