您的位置:首页 > Web前端 > JavaScript

Effective Javascript 阅读笔记(6)-Javascript 原型链继承

2018-01-10 14:56 281 查看
向很多面向对象的语言一样,javascript支持继承。但是又不像一些传统的语言,javascript继承机制是基于原型,而不是类。简单白话理解即:B如果想继承A的属性和方法,只需要把A的一个实例给B的原型链就可以了。


原型链

原型有几个独立,但是相关的访问器。

1. C.prototype 用于建立由new C()创建的对象的原型

 每一个函数都有一个天生自带的属性:prototype(普通函数也有,只不过没有什么意义)

只有在构造函数模式中,才能发挥效果,prototype属性存储的是一个对象数据类型的值

 这个对象中有一个自己特殊的属性:constructor,其有指回函数自己

2. obj._proto_ 获取obj对象的原型对象的非标准方法,每一个对象数据类型(或实例)天生自带的属性

3. Object.getPrototypeOf(obj)是ES5中用来获取obj对象的原型对象的标准方法

4. Object.hasOwnProperty(obj)用来检查某一个属性是否属于某一个对象的私有属性

这样理解起来比较抽象,下面通过下面的例子进行深入的理解

例一
    function User(name, passwordHash) {
        this.name = name;
        this.passwordHash = passwordHash;
    }
    User.prototype.toString = function(){
        return "[User" + this.name + "]";
    };
    User.prototype.checkPassword = function(password){
        return hash(password) === this.passwordHash;
    };
    var u = new User('test', '97qhjshy8274q298');




通过图片我们可以深入的对以上代码进行分析

 User 函数自带有一个默认的prototype属性

 添加了两个方法toString和checkPassword到 User.prototype

 使用new创建User的实例时,产生的对象u相应的会得到自动分配的原型对象,该原型对象存储在User.prototype

我们可以验证一下,对象u自动分配的原型对象是否存储在User.prototype中。ES5中的函数Object.getPrototypeOf(obj)用于检索现有的对象原型。
    Object.getPrototypeOf(u) === User.prototype;//true


由此可以验证以上的说法。

从图中我们还可以看出,实力对象u有一个自带的属性_proto_,这个属性指向 User.prototype

我们可以来验证一下
    u._proto_ === User.prototype;//true


  由此可说明。这也表明了它们之间的继承关系。

  例如当创建u之后,u会在带有u.name ,u.passwordHash的私有属性,但当我们调用u.checkPassword的时候也是可以成功获取到的,这是因为当我们调用一个实力对象的某一属性时,首先在自己的私有属性上找有没有,如果有的话就执行私有的,如果没有就通过__proto__找到他所属的类的原型链上找定义的公有属性。这便是原型链的继承关系。下面我们通过另一个例子辅助理解。


继承

例二
  function A(x) {
         this.x1 = x;
         this.get1 = function() {
             return this.x1;
         };
         A.prototype.sum = function() {
             console.log("ok")
         };
     };
     function B(x) {
         this.x2 = x;
         this.get2 = function() {
             return this.x2 + this.x2;
         };
     };
     B.prototype.sum = function() {
         console.log("no");
     };
     B.prototype = new A(1);
     var b = new B(2);
     b.sum();//ok-->相当于b.__proto__.__proto__.sum();
     console.log(B.prototype.__proto__ == A.prototype); //true




创建一个A函数,有私有属性x1,get1

给函数A 添加了一个方法sum到 A.prototype

创建一个B函数,有私有属性x2,get2

给函数B 添加了一个方法sum到 B.prototype

创建了一个实力对象new A(1)

把new A(1)赋值给B.prototype,即B.prototype不再是之前的,而是指向了new A(1)

创建了一个B的实例对象b

获取b.sum,b中私有属性并没有sum函数,则通过b的_proto_寻找其原型链new A(1),然而new A(1)也没有此函数,则继续通过new A(1)的_proto_寻找其原型链A,所以最终结果返回OK


原型中存储方法

javascript 完全可以不用原型进行编程

例三
   function User(name, passwordHash) {        this.name = name;        this.passwordHash = passwordHash;        this.toString = function(){        return "[User" + this.name + "]";        };        this.checkPassword = function(password){        return hash(password) === this.passwordHash;        };    }

以上的程序跟例一的行为几乎是一样的,但是当我们构造多个实例时,一个重要的区别就暴漏出来了。
    var u1 = new User('test1', '97qhjshy8274q298');
    var u2 = new User('test2', '97qhjshy8274q298');
    var u3 = new User('test3', '97qhjshy8274q298');


如果用例三创建三个实例,则原型个结构图如下



由于每个实例中都包含toString和checkPassword,而不是通过原型创建的,所以会有六个函数对象。

如果用例一创建三个实力,则原型图如下

从原型结构图中我们可以看到,toString和checkPassword方法只被创建了一次,对象实例间通过原型进行共享。



将方法存储在原型中,使其可以被所有的实例使用,而不需要存储方法实现的多个副本,也不需要给每个实例对象增加额外的属性。你可能认为将方法存储在实例对象中会会优化方法查找的速度,例如u3.toString()方法,不需要搜索原型链来查找toString的实例。然而,现代的javascript 引擎深度优化了原型查找,所以将方法复制到实例对象并不一定保证明显的速度提升。而且实例方法肯定会占用更多的内存。

所以说将方法存储在原型中优于存储在实例对象中。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: