Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

原型

1. 对原型的理解

  • 当我们在 js 中创建一个函数 A(就是用 function 声明一个函数), 那么浏览器会在内存中创建一个对象 B。这个对象 B 就是函数 A 的原型对象,简称函数的原型,要通过 A.prototype 才能访问。

  • 【函数 A 中】默认会有一个属性 prototype 指向了对象 B

    • ( 即:prototype 属性的值是对象 B )。

    • 【原型对象 B 中】默认会有一个属性 constructor 指向了函数 A

      • ( 即:constructor 属性的值是函数 A )
    • 我们可以自己对这个原型对象进行重新选择,赋值替换掉原来的对象 B,相当于手动改变 A 的原型

      • 比如:A.prototype = { }
      • 但别忘了在 B 中添加 constructor 属性
  • 当我们用函数 A 去new一个实例 C 时(当作构造函数来用)

    • 【实例 C 中】默认会有一个不可访问的属性[[prototype]]指向了这个对象 B
      • 在个别浏览器中能用实例C.__proto__去访问,但注意 ie 不行,所以别在代码中用这玩意儿
      • ES5 提供了Object.getPrototypeOf(实例C)方法返回指定对象的原型(即内部[[prototype]]属性的值)
  • 原型对象的用途是为每个实例对象存储共享的方法和属性,是一个普通对象而已。并且所有的实例是共享同一个原型对象,因此有别于实例方法或属性。原型对象仅有一份,而实例有很多份,且实例属性和方法是独立的。

2. 对原型链的理解

  • 每个被 new 出来的实例对象都有一个私有属性(称之为__proto__

  • 这个属性的值,就是它的构造函数(可以从__proto__.constructor中获得)的原型对象(prototype

  • 然后这个原型对象的值,有两种情况

    • 若不是Object.prototype,那就回到第一步,把原型对象当成是被 new 出来的实例,继续向上找
    • 若是Object.prototype,代表追溯快到终点,这时候再走一步,把这个对象当成是被 new 出来的实例,那它的__proto__就是 null,而 null 的出现就是这个原型链中的最后一个环节
  • Object 对象站在几乎所有 JavaScript 对象的原型链顶端

  • 对于一个类的实例

    • 要用instanceof判断是否为某一个类
    • 不能用typeof,否则一律返回 function,因为类本质就是函数,只是语法糖而已

3. js 对作用域链的理解

  • js 是基于词法作用域的

    • 即: 作用域链的查找顺序是按照函数“定义”时的位置决定的,而不是被调用的位置
  • 所以下面的代码

    var name = "global_name";
    var type = "global_type";
    
    function foo() {
      var name = "foo_name";
      console.log(name);
      console.log(type);
    }
    
    function bar() {
      var name = "bar_name";
      var type = "bar_type";
      foo();
    }
    
    bar();
    
    • 将会打印出 foo_name 和 global_type,而不会经过 bar 的作用域
  • 词法作用域也叫静态作用域,因为是函数声明时就确定了的

    • 与之相对的是动态作用域,它的作用域链查找顺序是按照函数“被调用”时的位置决定的,也就是基于调用栈