蘑小De菇

个人技术博客

hi,我是蘑小De菇,一名前端开发者。


记录个人对技术的理解和开发过程中遇到的问题,欢迎了解更多。

理解原型

原型

1. 理解原型

函数在创建时,都会添加一个prototype属性指向它的原型对象,这个原型对象会自动添加一个constructor属性指向这个构造函数,每次通过new调用构造函数创建一个新实例时,实例的隐式原型[[prototype]]指针会指向构造函数的原型对象,如:Person.prototype。浏览器会暴露一个__proto__ 来访问隐式原型[[prototype]]

构造函数、原型和实例的关系: 每个构造函数都有一个原型对象prototype,原型prototype有一个constructor属性指回构造函数,而实例有一个内部指针__proto__指向原型

实例和构造函数没有直接联系,而是和构造函数的原型有直接联系

function Person() {} //声明之后就有一个与之关联的原型对象,可通过Person.prototype访问

let person1 =  new Person
let person2 =  new Person

/*
 * 一条原型链
 */ 

// 实例对象的__proto__指向构造函数的原型对象
console.log(person1.__proto__ == Person.prototype) // true

// 构造函数原型的__proto__指向Object构造函数的原型
console.log(Person.prototype.__proto__ == Object.prototype) // true

// Object原型的__proto__指向null
console.log(Object.prototype.__proto__ == null) // true

// 构造函数 的原型constructor 指向构造函数
console.log(person1.__proto__.constructor == Person)

// 构造函数的多个实例共享构造函数的原型
console.log(person1.__proto__ == person2.__proto__)

/*
 * 是否包含指定构造函数的原型:
 */ 
console.log(person1 instanceof Person); // true 
console.log(person1 instanceof Object); // true 
console.log(Person.prototype instanceof Object); // true

总结

  • 大部分原型都会终止于Object的原型对象,Object的原型对象指向null
  • 实例通过__proto__ 指针指向构造函数的原型对象prototype,proto 就是隐式原型[[prototype]]
  • 构造函数通过prototype属性指向原型
  • 同一个构造函数创建多个实例,多个实例共享这个构造函数的原型

20210902100026

2. 原型的两个判断方法

  • isPrototypeOf(实例) 查看实例的__proto__是不是指向这个原型

          Person.prototype.isPrototypeOf(person1) // true
    
  • Object.getPrototypeOf() 返回传入对象的__proto__指向的原型对象

          Object.getPrototypeOf(person1) == person1.__proto__ == Person.prototype
    

    20210902102632

3. 原型层级

在通过对象访问属性时,先在对象实例本身查找,找不到会通过__proto__指针进入到原型对象,通过原型链一级一级往上查找,直到找到返回或到原型链顶端查找结束

  • 实例属性和原型属性名称一致,实例属性会覆盖原型属性,但是不会更改原型属性,通过delete删除掉实例属性,原型属性还是会通过原型链被找到

      function Person() {}
    
      Person.prototype.name = "limei"
    
      let person1 = new Person
    
      console.log(person1.name) // limei
    
      person1.name = "hanmeimei"
    
      console.log(person1.name) // hanmeimei
    
      /**
       * {
       * name: hanmeimei
       * __proto__: {
       * name:limei
       * constructor:Person
       * __proto__:Object
       * }
       * }
      */
      console.log(person1) 
    
      delete person1.name
    
      console.log(person1.name) // limei
    
      /**
       * {
       * __proto__: {
       * name:limei
       * constructor:Person
       * __proto__:Object
       * }
       * }
      */
      console.log(person1) 
    
        
    

4. 判断属性归属

  • hasOwnPropertyOf() 判断属性在实例上还是在原型上
person1.hasOwnPropertyOf("name") // 判断name属性是不是在实例person1上

5. 原型和in操作符

  • for-in 循环

    • 循环返回可枚举属性
  • in操作符

    • 无论属性在实例上还是原型上都会返回true

        // 判断某个属性在不在原型上
        function hasPrototypeProperty(object,name) {
            return !object.hasOwnPropertyOf(name) && (name in object)
        }
      

6. 重写原型

对象字面量重写

Person.prototype = {
    name: "Nicholas", 
    age: 29, 
    job: "Software Engineer", 
    sayName() { 
    console.log(this.name); 
    } 
};

Person.prototype.constructor == Object

相当于把Person.prototype 重新赋值了一个新对象,这样会导致一个问题,就是原本的prototype对象有一个constructor属性指向Perosion,现在指向了Object

解决办法

// 恢复 constructor 属性
Object.defineProperty(Person.prototype, "constructor", { 
    enumerable: false,  // 不可枚举
    value: Person 
});

7. 原型的动态性

实例的__proto__ 指针指向原型对象,而不是保存了副本,当原型对象有更改会立即反应到所有实例对象上

let friend = new Person(); 
Person.prototype.sayHi = function() { 
 console.log("hi"); 
}; 
friend.sayHi(); // "hi",没问题!

但是重写原型后,实例的__proto__ 指针依旧指向最初的原型,所以不会动态同步

function Person() {} 
let friend = new Person(); 
Person.prototype = { 
    constructor: Person, 
    name: "Nicholas", 
    age: 29, 
    job: "Software Engineer", 
    sayName() { 
    console.log(this.name); 
    } 
}; 

friend.sayName(); // 错误

20210902143730

8. 原生原型

原生引用类型的构造函数

  1. Function
  2. Object

8. 原型的问题

  1. 弱化构造函数传递参数的能力
  2. 所有实例在原型上取得相同的属性值。更改属性值同步所有实例。

9. 原型链

我们都知道,当我们用构造函数创建一个实例的时候,实例的__proto__指向构造函数的原型prototype,如果构造函数的原型是另一个类型的实例,这就意味着这个原型内部也有一个__proto__ 指向另一个原型。这样就在实例和原型之间构造了一条原型链。

  • 所有引用类型都继承Object,任何函数的默认原型都是一个Object实例,这个实例的内部指针__prot__ 指向Object.prototype

    function SuperType() {
      this.property = true;
    }
    SuperType.prototype.getSuperValue = function() {
      return this.property;
    };
    function SubType() {
      this.subproperty = false;
    }
    // 继承SuperType
    SubType.prototype = new SuperType();
    SubType.prototype.getSubValue = function () {
      return this.subproperty;
    };
    let instance = new SubType();
    console.log(instance.getSuperValue()); // true
下一篇

理解继承