js的原型链

摘要:
在本文开始之前,我了解了js中的原型链和原型对象。人原型say=function(){console.log('你好,Person原型对象定义了一个public say方法,但这个示例恰好是在添加原型方法之前完成了实例对象的构造。Person.prototype是:Person.proto原型指向一个空对象{}。它指向的{}对象中没有say方法。

开篇

之前对js中的原型链和原型对象有所了解,每当别人问我什么是原型链和原型对象时,我总是用很官方(其实自己不懂)的解释去描述。有一句话说的好:如果你不能把一个很复杂的东西用最简单的话语描述出来,那就说明你没有真正的理解。最近正在读《Javascript高级程序设计》,书中对原型对象和原型链的描述让我受益匪浅,下面仅用一个对比性的例子来说明。

我们经常会这么写

    function Person () {
        this.name = 'John';
    }
    var person = new Person();
    Person.prototype.say = function() {
        console.log('Hello,' + this.name);
    };
    person.say();//Hello,John

上述代码非常简单,Person原型对象定义了公共的say方法,虽然此举在构造实例之后出现,但因为原型方法在调用之前已经声明,因此之后的每个实例将都拥有该方法。从这个简单的例子里,我们可以得出:
原型对象的用途是为每个实例对象存储共享的方法和属性,它仅仅是一个普通对象而已。并且所有的实例是共享同一个原型对象,因此有别于实例方法或属性,原型对象仅有一份。所有就会有如下等式成立:

                            person.say == new Person().say

可能我们也会这么写

    function Person () {
        this.name = 'John';
    }
    var person = new Person();
    Person.prototype = {
        say: function() {
            console.log('Hello,' + this.name);
        }
    };
    person.say();//person.say is not a function

很不幸,person.say方法没有找到,所以报错了。其实这样写的初衷是好的:因为如果想在原型对象上添加更多的属性和方法,我们不得不每次都要写一行Person.prototype,还不如提炼成一个Object来的直接。但是此例子巧就巧在构造实例对象操作是在添加原型方法之前,这样就会造成一个问题:
var person = new Person()时,Person.prototype为:Person {}(当然了,内部还有constructor属性),即Person.prototype指向一个空的对象{}。而对于实例person而言,其内部有一个原型链指针proto,该指针指向了Person.prototype指向的对象,即{}。接下来重置了Person的原型对象,使其指向了另外一个对象,即
Object {say: function}
这时person.proto的指向还是没有变,它指向的{}对象里面是没有say方法的,因为报错。
从这个现象我们可以得出:
在js中,对象在调用一个方法时会首先在自身里寻找是否有该方法,若没有,则去原型链上去寻找,依次层层递进,这里的原型链就是实例对象的__proto__属性

若想让上述例子成功运行,最简单有效的方法就是交换构造对象和重置原型对象的顺序,即:

    function Person () {
        this.name = 'John';
    }
    Person.prototype = {
        say: function() {
            console.log('Hello,' + this.name);
        }
    };
    var person = new Person();
    person.say();//person.say is not a function

一张图让你秒懂原型链

js的原型链第1张
 

其实,只需要明白原型对象的结构即可:

    Function.prototype = {
        constructor : Function,
        __proto__ : parent prototype,
        some prototype properties: ...
    };

总结:函数的原型对象constructor默认指向函数本身,原型对象除了有原型属性外,为了实现继承,还有一个原型链指针__proto__,该指针指向上一层的原型对象,而上一层的原型对象的结构依然类似,这样利用__proto__一直指向Object的原型对象上,而Object的原型对象用Object.__proto__ = null表示原型链的最顶端,如此变形成了javascript的原型链继承,同时也解释了为什么所有的javascript对象都具有Object的基本方法。



文/Pursue(简书作者)
原文链接:http://www.jianshu.com/p/aa1ebfdad661
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

免责声明:文章转载自《js的原型链》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇关于一个简单接口的高并发测试与优化记录vba Excel连接数据库下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Xcode插件管理

 在使用Xcode的时候,公司同事使用/// 和//TODO 就能打出很多注释信息。虽然他们帮忙给我也装了,但是我却不知道怎么弄的。今天在家无聊,过来自己实践了一把。  so easy。 1.我使用的是Package Manager for Xcode。这时,我们要使用要将它下载下来,并且装入到Xcode里面。 原本以为会很繁琐,结果一句话就搞定了。 官网...

[转载]java中Statement详细用法

1、创建 Statement 对象建立了到特定数据库的连接之后,就可用该连接发送 SQL 语句。Statement 对象用 Connection 的方法 createStatement 创建,如下列代码段中所示:Connection con = DriverManager.getConnection(url, "sunny", "");Statement...

Canopy聚类算法

一、概念     与传统的聚类算法(比如K-means)不同,Canopy聚类最大的特点是不需要事先指定k值(即clustering的个数),因此具有很大的实际应用价值。与其他聚类算法相比,Canopy聚类虽然精度较低,但其在速度上有很大优势,因此可以使用Canopy聚类先对数据进行“粗”聚类,得到k值后再使用K-means进行进一步“细”聚类。这种Can...

时序图学习4_组成元素之生命线和激活(控制焦点)

注:本文中的时序图均使用Astah工具制作。 一、生命线(Lifeline) 生命线代表时序图中的对象在一段时期内的存在。 时序图中每个对象和底部中心都有一条垂直的虚线,这就是对象的生命线,对象间的消息存在于两条虚线间。 生命线是一个时间线, 从时序图顶部一直到底部都存在, 其长度取决于交互的时间。 二、激活(Activation) 代表生命线上的窄矩...

使用 powershell 的 grep 过滤文本

  使用 powershell 的 grep 过滤文本 有个log文件,大小在4M左右,要求找出里面耗时超过100s 的记录。首先想到了强大的 grep ,那么就搞起。 先在网上找一下资料,这篇文章,有几种方式: 第一种: Get-content somefile.txt|findstr "someregexp" Get-content可以换成cat,P...

面向对象基础——索引器

  C#中的string是可以通过索引器来访问对象中的字符,但却不能修改字符的值。   我们来看string中关于索引器的定义,如下图。   上图中索引器如同属性一样,具有get方法,却没有set方法,所以这就是为什么C#中的string类型的变量都是只读的。      现在让我们来编写属于自己的索引器: 1 class Program 2...