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

向很多面向对象的语言一样,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 引擎深度优化了原型查找,所以将方法复制到实例对象并不一定保证明显的速度提升。而且实例方法肯定会占用更多的内存。

所以说将方法存储在原型中优于存储在实例对象中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值