• Web程序员应该知道的Javascript prototype原理


    有同事问了我几个和Javascript的类继承的小问题,我在也不太理解的情况下,胡诌了一通. 回来以后有些内疚, 反省一下, 整理整理Javascript的prototype的原理, 自己清楚点, 也希望对别人也有帮助.

    首先js里面没有类这回子事情, 虽然class是js的保留字, 但是到现在也没派上任何用场. 在js里面, 几乎所有的东西都是对象, 函数也是对象, 所以函数可以赋值给别的变量. 另外js对象也可以用数组的方式访问, 也就是说访问obj1.something, 也可以写成obj1['something'].

    在js里虽然没有类, 但是有一个在类编程里常用的的概念, Constructor构造器, 或者叫构造函数. 语法上Constructor跟普通的js函数没有区别, 但是Constructor前面加上new关键字就可以产生新的对象.

    那么”new Constructor()”这样调用时到底做了什么事情呢?
    1. js运行环境首先创建一个空对象
    2. 把this变量指向这个对象
    3. 把__proto__指向这个构造器的prototype属性
    4. 通过this把属性和方法加在这个对象上
    5. 最后会把this指向的对象return出来(当然你也可以显示的return别的对象,这是情况跟我讨论的有点不一样)

    这里有2个概念比较重要, 第一是Constructor的prototype属性, 它是Constructor的一个保持共享属性和方法的对象. 二通过该Constructor生成的对象的__proto__属性, 它指向了那个prototype对象. 对象的Constructor可以通过该对象的constructor属性获得.

    我们定义一个简单的Constructor Demo(), 然后通过他生成一个aDemo对象, 在Chrome的console里把aDemo打出来看到的结果是这样的,这是我们能很清楚的看出这种关系.

    01
    02
    03
    04
    05
    06
    function DemoA(){
      this.title = "this is a demo constructor";
      this.f1 = function(){}
    }
    var aDemo = new DemoA();
    console.log(aDemo);

    jsproto1

    换一种写法,

    01
    02
    03
    04
    05
    06
    function DemoB(){
      this.title = "this is a demo constructor";
    }
    DemoB.prototype.f2 = function () {};
    var bDemo = new DemoB();
    console.log(bDemo);

    jsproto2
    其实跟前面没多大区别, 不过这一次f2()不在创建出来的对象上, 而是在对象的__proto__指向的构造器DemoB上. 这样的好处是当使用DemoB创建很多对象时, 比较节省内存.

    js的prototype搜索链 prototype Chain
    js的运行环境是这样使用构造器的prototype的. 当我们对第一个对象调用其方法时, js运行环境首先看, 这个对象本身有没有这个方法, 如果有就直接调用. 如果没找到, 就查找这个对象的__proto__属性指向的对象(就是前面说的该对象的构造器的prototype属性), 如果有该方法就调用, 没有继续查找__proto__属性指向对象的__proto__属性指向的对象, 以此类推. 直到最后, 找到js所有的对象都会指向默认指向的Object(算是js的根对象吧). 所以如果我们给Object上增加个方法, 所以的js对象都能使用.

    由于js有对对象函数的prototype chain搜索的特性, 我们可以利用这点来模拟类似其他语言里类(class)的效果. 如:

    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    function DemoA(){
      this.title = "this is a demo constructor";
      this.f1 = function(){}
    }
     
    function DemoB(){
      this.title = "this is b demo constructor";
      this.f2 = function(){}
    }
    DemoB.prototype = new DemoA();
     
    var bDemo = new DemoB();
    console.log(bDemo);

    jsproto3
    这时我们通过bDemo对象, 不但能调用bDemo上的DemoB的f2()方法, 也能__proto__同对对prototype的搜索, 在bDemo上直接调用DemoA的f1()方法.

    由于这种写法在语法上太啰嗦, 不容易理解, 我可以定义一个这样的函数.

    01
    02
    03
    function inherit(Child, Parent) {
      Child.prototype = new Parent();
    }

    这样我们前面的例子可以写成这样, 效果是一样的:

    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    function DemoA(){
      this.title = "this is a demo constructor";
      this.f1 = function(){}
    }
     
    function DemoB(){
      this.title = "this is b demo constructor";
      this.f2 = function(){}
    }
     
    inherit(DemoB, DemoA);
     
    var bDemo = new DemoB();
    console.log(bDemo);
     
  • 相关阅读:
    angular9的学习(十)
    本周学习总结
    本周学习总结
    angular9的学习(九)
    本周学习总结
    Web地图呈现原理
    小程序Canvas性能优化实战
    地图SDK全新版本v4.3.0上线
    硬核干货来了!手把手教你实现热力图!
    地图SDK全面升级 – 数十项新功能及优化等你来体验
  • 原文地址:https://www.cnblogs.com/huangye-dream/p/3579771.html
Copyright © 2020-2023  润新知