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

原型链对于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. 再学IHanlder 类----------------关于Asp.net与iis原理网上看博客收获写一个验证码用一般处理程序记的好长时间前就写过不过现在再看有点不一样的感觉

    建一个web网站 新建一般处理程序直接贴代码: using System;using System.Collections.Generic;using System.Linq;using System ...

  2. [51nod1065]最小正子段和

    题意:求一个序列中大于0的最小子段和. 解题关键: 先求出前缀和和,对于每个位置求某个位置到当前位置和大于1的和的最小值.然而这是复杂度是O(n^2)的.其实可以通过排序优化到O(nlogn).对前缀 ...

  3. nginx是如何处理一个请求的(包含https配置)

    配置https首先要有ssl证书,这个证书目前阿里有免费的,但如果自己做实验,也是可以自签证书,只不过不受信 openssl genrsa -des3 -out server.key 1024     ...

  4. [51nod] 1289 大鱼吃小鱼 堆栈-模拟

    有N条鱼每条鱼的位置及大小均不同,他们沿着X轴游动,有的向左,有的向右.游动的速度是一样的,两条鱼相遇大鱼会吃掉小鱼.从左到右给出每条鱼的大小和游动的方向(0表示向左,1表示向右).问足够长的时间之后 ...

  5. 【转】mysql中set autocommit=0与start transaction的关系

    在mysql中用户的任何一个更新操作(写操作)都被视为一个事务,set autocommit=0指事务非自动提交,自此句执行以后,每个SQL语句或者语句块所在的事务都需要显示"commit& ...

  6. 浅淡Java多线程

    工作中一直忙着实现业务逻辑,多线程接触得不多.对多线程的认知,一直停留在Thread和Runnable上.最近心血来潮,找了几本多线程的书,不看不知道,一看吓一跳.原来我对多线程的理解是多么的肤浅.记 ...

  7. 洛谷 P5162 WD与积木【多项式求逆】

    设f[i]为i个积木能堆出来的种类,g[i]为i个积木能堆出来的种类和 \[ f[n]=\sum_{i=1}^{n}C_{n}^{i}g[n-i] \] \[ g[n]=\sum_{i=1}^{n}C ...

  8. CF961G Partitions(第二类斯特林数)

    传送门 对于每一个元素,我们只要能求出它的出现次数\(sum\),那么每个元素的贡献都是一样的,最终的答案为\(sum\times \sum_{i=1}^n w_i\) 那么分别讨论 如果这个元素自己 ...

  9. python 之 time模块、datetime模块(打印进度条)

    6.9 time 模块 方法 含义 备注 time.time() 时间戳 1561013092.997079 time.strftime('%Y-%m-%d %H:%M:%S %p') 结构化时间st ...

  10. JQuery Easyui/TopJUI 基本树形表格的创建

    <table data-toggle="topjui-treegrid" data-options="id:'menuTg', idField:'id', tree ...