• New的原理


    常见考点

    • new 做了那些事?
    • new 返回不同的类型时会有什么表现?
    • 手写 new 的实现过程

    new 关键词的主要作用就是执行一个构造函数、返回一个实例对象,在 new 的过程中,根据构造函数的情况,来确定是否可以接受参数的传递。下面我们通过一段代码来看一个简单的 new 的例子

    function Person(){
       this.name = 'Jack';
    }
    var p = new Person(); 
    console.log(p.name)  // Jack
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这段代码比较容易理解,从输出结果可以看出,p 是一个通过 person 这个构造函数生成的一个实例对象,这个应该很容易理解。

    new 操作符可以帮助我们构建出一个实例,并且绑定上 this,内部执行步骤可大概分为以下几步:

    1. 创建一个新对象
    2. 对象连接到构造函数原型上,并绑定 this(this 指向新对象)
    3. 执行构造函数代码(为这个新对象添加属性)
    4. 返回新对象

    在第四步返回新对象这边有一个情况会例外:

    那么问题来了,如果不用 new 这个关键词,结合上面的代码改造一下,去掉 new,会发生什么样的变化呢?我们再来看下面这段代码

    function Person(){
      this.name = 'Jack';
    }
    var p = Person();
    console.log(p) // undefined
    console.log(name) // Jack
    console.log(p.name) // 'name' of undefined
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 从上面的代码中可以看到,我们没有使用 new 这个关键词,返回的结果就是 undefined。其中由于 JavaScript 代码在默认情况下 this 的指向是 window,那么 name 的输出结果就为 Jack,这是一种不存在 new 关键词的情况。
    • 那么当构造函数中有 return 一个对象的操作,结果又会是什么样子呢?我们再来看一段在上面的基础上改造过的代码。
    function Person(){
       this.name = 'Jack'; 
       return {age: 18}
    }
    var p = new Person(); 
    console.log(p)  // {age: 18}
    console.log(p.name) // undefined
    console.log(p.age) // 18
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    通过这段代码又可以看出,当构造函数最后 return 出来的是一个和 this 无关的对象时,new 命令会直接返回这个新对象而不是通过 new 执行步骤生成的 this 对象

    但是这里要求构造函数必须是返回一个对象,如果返回的不是对象,那么还是会按照 new 的实现步骤,返回新生成的对象。接下来还是在上面这段代码的基础之上稍微改动一下

    function Person(){
       this.name = 'Jack'; 
       return 'tom';
    }
    var p = new Person(); 
    console.log(p)  // {name: 'Jack'}
    console.log(p.name) // Jack
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    可以看出,当构造函数中 return 的不是一个对象时,那么它还是会根据 new 关键词的执行逻辑,生成一个新的对象(绑定了最新 this),最后返回出来

    因此我们总结一下:new 关键词执行之后总是会返回一个对象,要么是实例对象,要么是 return 语句指定的对象

    手工实现New的过程

    function create(fn, ...args) {
      if(typeof fn !== 'function') {
        throw 'fn must be a function';
      }
    	// 1、用new Object() 的方式新建了一个对象obj
      // var obj = new Object()
    	// 2、给该对象的__proto__赋值为fn.prototype,即设置原型链
      // obj.__proto__ = fn.prototype
    
      // 1、2步骤合并
      // 创建一个空对象,且这个空对象继承构造函数的 prototype 属性
      // 即实现 obj.__proto__ === constructor.prototype
      var obj = Object.create(fn.prototype);
    
    	// 3、执行fn,并将obj作为内部this。使用 apply,改变构造函数 this 的指向到新建的对象,这样 obj 就可以访问到构造函数中的属性
      var res = fn.apply(obj, args);
    	// 4、如果fn有返回值,则将其作为new操作返回内容,否则返回obj
    	return res instanceof Object ? res : obj;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 使用 Object.createobj 的proto指向为构造函数的原型
    • 使用 apply 方法,将构造函数内的 this 指向为 obj
    • create 返回时,使用三目运算符决定返回结果。

    我们知道,构造函数如果有显式返回值,且返回值为对象类型,那么构造函数返回结果不再是目标实例

    如下代码:

    function Person(name) {
      this.name = name
      return {1: 1}
    }
    const person = new Person(Person, 'lucas')
    console.log(person)
    // {1: 1}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    测试

    //使用create代替new
    function Person() {...}
    // 使用内置函数new
    var person = new Person(1,2)
    
    // 使用手写的new,即create
    var person = create(Person, 1,2)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    new 被调用后大致做了哪几件事情

    • 让实例可以访问到私有属性;
    • 让实例可以访问构造函数原型(constructor.prototype)所在原型链上的属性;
    • 构造函数返回的最后结果是引用数据类型。
  • 相关阅读:
    EasyExcel实现复杂导入 适用于导入1对N类型数据如组合商品,订单和订单明细等等
    Notion + CloudFlare + 域名搭建网站
    FPGA学习----Verilog HDL语法(2)
    java计算机毕业设计心理学网站源程序+mysql+系统+lw文档+远程调试
    windows下flutter的环境安装
    达梦数据库学习操作记录
    FPGA在汽车领域的应用简谈
    常用的二十种设计模式(上)-C++
    谈谈ORACLE应用数据同步器:Primavera Gateway (2/2)
    设计模式-行为型模式-中介者模式
  • 原文地址:https://blog.csdn.net/php_martin/article/details/125852307