js - 原型链

js - 原型链

码农世界 2024-05-19 前端 59 次浏览 0个评论

js - 原型链

  • 1. js原型链
  • 2. js原型链的应用
    • 2.1 js原型链继承
    • 2.2 利用js原型判断对象类型
    • 2.3 new的过程中发生了什么 / 手写new

      1. js原型链

      原型链由三个部分组成:构造函数、实例、原型对象。

      构造函数:由大写字母开头的函数。在创建构造过程中,js会自动为它添加prototype属性,值为拥有constructor属性原型对象,constructor属性值为该构造函数。只有函数才有prototye属性。

      实例:由构造函数new(实例化)得到。

      原型对象:构造函数会在创建的过程中自动创建。

      举个例子:

      function Person(){} // 构造函数
      var person = new Person() // 实例对象
      

      关系图如下:

      探讨原型链的过程

      我们接触到的js复杂数据类型的构造函数都是function类型,因为复杂数据类型的构造函数都是new Function创建的。

      typeof 能检测:

      1.基本数据类型(除了null) 、

      2.function类型、

      3.对象(但是不能区分是什么对象,比如数组和对象分不清)

      //函数对象  
      function Func1(){};   //函数声明
      var Func2 = function(){};  // 函数表达式(右边是匿名函数)
      var Func3 = new Function("n1,n2","return n1+n2");  // 函数对象(不推荐)
        
      console.log(typeof Func1);  //function  
      console.log(typeof Func2);  //function  
      console.log(typeof Func3);   //function  
      console.log(typeof Object);   //function  
      console.log(typeof Array);   //function  
      console.log(typeof String);   //function  
      console.log(typeof Date);   //function  
      console.log(typeof Function);   //function  
      

      A instanceof B 是探讨A的__proto__或是__proto__.proto__或是__proto.proto…是否等于B.prototype,如果是则返回true,否则返回false。

      Function instanceof Object //true 
      Object instanceof Function // true 
      // Function.__proto__.__proto__ === Object.prototype
      // Object.__proto__ === Function.prototype
      

      是不是感觉有些奇怪,因为Object是构造函数,继承了Function.prototype,Function也是对象,继承了Object.prototype。本质原因还是因为Function构造函数的构造函数是他本身,所以Function.__proto__也指向Function.prototype

      用原型链图表示:

      还有一个发现,就是原型链的尽头是null,就是说,当实例对象不停的.__proto__尽头是Object.prototype.proto(null)

      js内置对象包含Object、Function、Array、String、Boolean、Number、Date、RexExp。

      2. js原型链的应用

      2.1 js原型链继承

      原型链可以用比较小的内存来实现对象共享。只需在原型对象上添加方法(重写原型对象),子实例都可以引用。

      function Person() { } // 构造函数
      var person = new Person() // 实例对象
      Person.prototype.name = 'name';
      Person.prototype.get = () => {
          console.log('get')
      };
      person.get() // 'get'
      console.log(person.name); // 'name'
      

      缺点:实例对原型上的引用类型值进行篡改。

      Person.prototype.arr = ['a','b'];
      person.arr.push('c');
      console.log(Person.prototype.arr); // ['a','b','c']
      

      2.2 利用js原型判断对象类型

      typeof A:

      只能判断除了null之外的基础数据类型。

      复杂数据类型+null都会判断为object,函数会判断为function。

      A instanceof B:

      表示A是否是B这个构造函数的实例对象,A.proto… === B.prototype则成立。

      不能判断基本数据类型。

      console.log('111' instanceof String) // false
      console.log((new String('111')) instanceof String) // true
      

      A.constructor:

      利用原型链和原型,不能判断undefined和null。

      利用实例的自动查找原型对象上的constructor属性为构造函数。

      ‘1’.proto.constructor 为 ƒ String() { [native code] }

      console.log('1'.constructor);  // ƒ String() { [native code] }
      console.log((function a(){}).constructor); // ƒ Function() { [native code] }
      console.log(null/undefined.constructor);  // Uncaught TypeError: Cannot read properties of null (reading 'constructor')
      

      Object.prototype.toString.call ( A )

      可以检测所有数据类型。

      console.log(Object.prototype.toString.call(true)) // '[object Boolean]'
      console.log(Object.prototype.toString.call(undefined)) // '[object Undefined]'
      console.log(Object.prototype.toString.call(null)) // '[object Null]'
      console.log(Object.prototype.toString.call(Person)) // 自定义函数'[object Function]'
      

      2.3 new的过程中发生了什么 / 手写new

      new的过程中发生了什么

      先看两段代码帮助理解

      // 无return
      function Person1(name, age) {
          this.name = name;
          this.age = age;
      }
      // 有return 
      function Person2(name, age) {
          this.name = name;
          this.age = age;
          // 试一试返回基本数据类型和引用数据类型的区别(如return null时候,结果跟Person1一样)
          return { type: 'dog', owner: 'lihua' } 
      }
      var func = () => { console.log('my name is lihua') }
      Person1.prototype.getName = func;
      Person2.prototype.getName = func;
      var person1 = new Person1('lihua', 18);
      var person2 = new Person2('lihua', 18);
      //{ name: 'lihua', age: 18 } , () => {..} , {getName: ƒ, constructor: ƒ} 
      console.log(person1, person1.getName, person1.__proto__)
      //{ type: 'dog', owner: 'lihua' } , undefined , Object.prototype
      console.log(person2, person2.getName, person2.__proto__)
      

      所以new的过程中发生了以下四个步骤:

      1. 创建一个空对象
      2. 将空对象的__proto__指向构造函数的prototype
      3. 执行构造函数,改变this指向为创建出来的空对象
      4. 如果构造函数有返回值,对构造函数的返回值做判断,返回值为引用类型时,返回该对象,返回值是基本数据类型时是创建出来的空对象。

      手写new

      根据以上,手写new如下:

      function myNew(constructor, ...args) {
          var result = constructor(...args)
          // return 基本数据类型时
          if (typeof result !== 'object' || result === null) {
              let obj = {}                                        //创建空对象
              obj.__proto__ = constructor.prototype                // 空对象的__proto__指向构造函数的原型
              constructor.apply(obj, args)                         //改变this指向
              return obj
          }
          // return 引用数据类型时
          return result
      }
      

      测试一下

      function Constructor(a, b) {
          this.a = a;
          this.b = b;
          return a;
      }
      Constructor.prototype.getName = () => { return 'hihihi' }
      var test = myNew(Constructor, 1, 2)
      console.log(test, test.getName) // Constructor {a: 1, b: 2} () => { return 'hihihi' }
      

转载请注明来自码农世界,本文标题:《js - 原型链》

百度分享代码,如果开启HTTPS请参考李洋个人博客
每一天,每一秒,你所做的决定都会改变你的人生!

发表评论

快捷回复:

评论列表 (暂无评论,59人围观)参与讨论

还没有评论,来说两句吧...

Top