原型与原型链

原型

查漏补缺

  • 构造函数:调用 new 关键字来创建的函数对象,该函数对象称为实例对象(构造函数通常需要大驼峰式命名)

      // 构造函数User
      function User(name, age) {this.name = name;this.age = age;
      }  //  创建一个User的实例对象user1
      var user1 = new User('原型', 18);复制代码

    var obj = {};使用语法结构创建的对象obj,等同于var obj = new Object(); 即obj的构造函数为Object

    数组同理

  • 函数原型(原型对象):所有函数都带有一个属性 prototype 指向该函数的原型对象,(默认情况下,prototype是一个 Object对象 即原型对象)

    • 原型对象(prototype)下有一个 constructor 属性,指向构造函数的本身

        User.prototype.constructor === User复制代码
    • 原型对象(prototype)下有一个 __proto__ 属性,指向 Object 的原型对象

        User.prototype.__proto__ === Object.prototype复制代码
  • 隐式原型(__proto__):每个 实例对象 都有一个私有属性 __proto__ ,该属性指向它的 构造函数 的原型对象

    •   // 实例对象user1的隐式原型 指向它构造函数User的函数原型
        user1.__proto__ === User.prototype复制代码

      ES6开始,可以通过Object.getPrototypeof(object)返回指定对象的原型(即对象的prototype属性的值),同样的也可以通过Object.setPrototypeOf(obj, prototype)设置

        Object.getPrototypeOf(user1) === User.prototype  Object.getPrototypeOf(user1) === user1.__proto__  // 不推荐使用__proto__复制代码
    • 构造函数的隐式原型指向Function的函数原型

        User.__proto__ === Function.prototype复制代码

原型链

原型链中的 注意点:

  • Function 的原型对象 和 隐式原型 都指向Function的原型对象。
      Function.prototype === Function.__proto__复制代码
  • Object.prototype表示Object的原型对象,而该对象的隐式原型为null
      Object.prototype.__proto__ === null复制代码
  • 几乎所有 JavaScript 中的对象都是位于原型链顶端的 Object 的实例。(函数原型默认情况下就是对象,所以 函数原型 的 隐式原型(__proto__) 为Object的原型)
      User.prototype.__proto__ === Object.prototype  Function.prototype.__proto__ === Object.prototype复制代码

基于原型链的继承

当访问一个对象的属性时会进行以下过程

  1. 首先查看该对象上是否有对应属性
  2. 查看该对象的 隐式原型(__proto__)是否有对应属性
  3. 再查看该对象的 隐式原型 的隐式原型 层层向上,直到搜索到或者到原型链的末尾(Object.prototype.__proto__ 为 null 即为末尾)

举个例子

  1. 属性继承
  //  User构造函数 
  function User(name, age) {this.name = name;this.age = age;this.value = 1;
  }  //  创建一个User的实例对象user1
  var user1 = new User('原型链', 18);  console.log(user1.value); // 自身拥有该属性,且值为1,输出1
  console.log(user1.newValue); // 自身无该属性且隐式原型上也没有,再往上搜索隐式原型的隐式原型,直到搜索到原型链末尾都没有,所以输出undefined 
  // tips:这里user1.__proto__为user1的隐式原型,在往上查找隐式原型user1.__proto__.__proto__为Object.prototype(Object的原型)在往上查找隐式原型就为null了
  User.prototype.newValue = 2; // 在隐式原型上添加一个newValue属性
  console.log(user1.newValue); // 自身无该属性,但是隐式原型上有该属性,所以输出1复制代码

函数同样也是对象,当继承的函数被调用时,this指向的时当前继承的对象,而不是继承的函数所在的原型对象。且继承的函数可以被重写

  var user1 = {name: 'oldName',say: function(){      return 'hello, i am ' + this.name
    }
  };  console.log(user1.say()); //  hello i am oldName
  var user2 = Object.create(user1);  // user2是一个继承自user1的对象,为一个空对象没有自身属性

  console.log(user2.say()); //  这里user2自身没有属性,所以通过隐式原型查找到name属性为oldName。输出hello i am oldName 
  user2.name = "newName"; //  为user2设置自身的name属性
  console.log(user2.say()); // 这里user2的自身有name属性,所以this指向自身的name属性,输出hello i am newName复制代码

性能

  • 在原型链上查找属性比较耗时,不利于性能优化。(例如访问不存在的属性时,会遍历整个原型链)

    可以通过hasOwnProperty()方法来判断,若为真则返回true

    obj.hasOwnProperty("key");

    Object.keys()也不会遍历原型链