JS中new之后发生了什么?以及如何手动实现一个new?

4,764 阅读2分钟

构造函数和普通函数没有什么本质的区别,唯一的区别有两个:

函数首字母大写,这个区别只是约定俗成的,便于区分。你实在要小写定义构造函数也完全没问题,所以这个区别可以忽略。
构造函数的调用需要用new操作符,而普通函数的调用又分很多种,但是都不会用到new操作符。所以,构造函数和普通函数的区别就在这个new操作符里,现在让我们来好好研究一下这个new操作符。

用new操作符创建对象发生的事情:

  • 创建一个新对象;
  • 将构造函数的作用域赋给这个对象(因此this就指向了这个对象);
  • 执行构造函数中的代码(为这个对象添加属性和方法,以及执行构造函数中其他的代码);
  • 把这个新对象返回;
    注意:原本的构造函数是window对象的方法,如果不用new操作符而直接调用,那么构造函数的执行对象就 是window,即this指向了window。现在用new操作符后,this就指向了新生成的对象。理解这一步至关重要。
执行构造函数中的代码
function Person() {
        // this.age = 22;
        window.age=23;
        this.name='tom'
        let name = 'tony';
        console.log(name);
      }
     let p = new Person();  //自动执行构造函数中的代码,输出 tony
     console.log(p.age) // undefined
     console.log(window.age) // 23
     console.log(p.name) // tom
将构造函数的执行对象赋给新生成的这个实例。再结合上一段里说的,自动执行构造函数里的this.name = "张三";就相当于是执行p.name = "张三";这里this就是指向新创建的对象p,并给p赋值属性和方法。
这是前面的三个步骤,最后一步是返回这个创建的对象,但是我们并没有看到return相关的代码。
如果被调用的函数没有显式的 return 表达式(仅限于返回对象),则隐式的会返回 this 对象 - 也就是新创建的对象。
function Person(){
      this.name = "Tim";
      return {};
}
var p = new Person();
console.log(p.name) // undefined;
实际上 new Person()相当于是如果没有返回任何东西,就默认返回return this,this即当前创建的对象。
function Person(){
      this.name = "Tim";
      return this;
}
var p = new Person();
console.log(p.name)// Tim;
那么最后实现一个简单的new也容易了。如下图代码:
        // 创建构造函数
      function Person(name,age){
        this.name = name;
        this.age = age;
      }
      // 定义自己实现的类似new方法,
      function create(fn,...args){
        let obj = {};
        fn.call(obj,...args);
        return obj;
      }
      // 使用自己定义的方法创建对象,第一参数是构造函数,相当于模板。从第二个参数起,为对象的属性值。
     let obj = create(Person,'张三',18);
      console.log(obj)  // {name: "张三", age: 18}