new 到底都做了什么事情

1,925 阅读4分钟

简介

最近由于换了工作,都没有时间写文章,今天好不容易抽出一点时间,就来好好介绍一下 new 运算符吧,虽然这个东西我们平时都很常见,但是它到底都做了什么事情呢,让我们来好好探究一下。

以下内容参考了《你不知道的JavaScript(上卷)》里第2章的new绑定。

new 运算符的作用

先来一段官方的介绍,new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。是不是感觉很抽象,说的直白点,就是用来创建不同类型的对象。不然只能通过字面量创建对象,想想就很可怕,当然字面量创建的对象底层也是通过 new Object()来实现的。让我们放个代码让我们更清楚一点。

const obj1 = {
    name: 'xm',
    age: 18,
}
console.log(obj1.constructor.name);// Object

function Person (name,age){
    this.name = name;
    this.age = age
}
const p = new Person("xm",18);
console.log(p.constructor.name);// Person

const a = [];
console.log(a.constructor.name);// Array
const b = Array();
console.log(a.constructor.name);// Array

对于我来说,new 最大的作用不仅仅只是创建对象,更重要的是让我们可以创建各种不同类型的对象。因此对象的多样性,才有了继承,才有了各种奇奇怪怪的操作。纯个人见解,如果有更好的,欢迎大家能提出自己的意见。

new 到底做了什么

我们平时都是通过 new 函数来创建一个自定义对象,那到底 new 函数执行和普通的函数执行到底有什么不同呢,现在就开始从一行代码开始入手:

function print (name){
    console.log(this);
    this.name = name;
}

let a = print('xm'); // Window
let b = new print('xm'); // print {}
console.log(a);// undefined
console.log(b);// print {}
// 返回的对象类型为print
console.log(b.constructor.name);// print
// 证实了new调用时的this指向这个新创建的对象
console.log(b.name);// xm

让我们来找找共同点和不同点。
共同点:new 函数执行和普通函数执行都执行了函数体内的代码。
不同点:
1.普通函数执行返回undefined,new 函数调用返回一个print类型的对象。
2.普通函数直接调用 this 指向 Window,new 函数调用 this 指向这个新创建的对象。

好像应该讲完了,但其实还有一种特殊情况,请看接下去的代码。

function print (name){
    console.log("打印",this);
    this.name = name;
    return {
        b: 1,    
    };
}

let a = new print("xm");// 打印 print {name: "xm"}
console.log(a);// {b: 1}

function print1 (name){
    console.log("打印",this);
    this.name = name;
    return 1;
}

let b = new print1("xm");// 打印 print1 {name: "xm"}
console.log(b);// print1 {name: "xm"}

是不是很神奇,当函数有返回值,并且返回的结果是一个对象,这时候 new 函数调用返回的结果是这个函数体的返回对象。

现在应该没了吧,够详细了吧。其实它还有一个小操作,差点我都把它给忽略了,那就是新创建的对象的隐式原型指向该函数的原型,这一步是为了继承构造函数原型上的属性和方法。大家千万要记住!!!

模拟实现

这里我就直接写代码了,面试有时候也会让你们模拟实现 new。一定要仔细看,然后在自己理解的基础上敲一遍。

function myNew (fn,...args){
    // 1.创建一个js对象,并且将该js对象的隐式原型指向构造函数的原型
    const newObj = Object.create(fn.prototype);
    // 2.调用函数,把函数的this绑定给这个新对象
    const result = fn.apply(newObj,args);
    // 3.判断函数是否返回有返回对象,如果有就返回该函数的返回对象,否则返回新对象
    return result && typeof result === "object" ? result : newObj;
}

是否正确就交给你们来测了。

小结

现在应该差不多没了,让我们总结一下,new 调用函数到底发生了什么。 使用 new 来调用函数时,会自动执行下面的操作:
1.创建一个空对象。
2.该对象的隐式原型指向该函数的原型。
3.这个新对象会绑定到函数调用的this。
4.如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。

结语

最后再来说说普通函数和构造函数的区别吧。老实说除了首字母大小写的区别之外(标识作用,有助于我们理解函数的作用),没有其他区别。实际上并不存在所谓的构造函数,只有对于函数的构造调用。 呼,没想到我们平时很常见的 new 运算符也有这么大的学问,希望在座的各位也能不忘初心,坚持梦想。那么今天的唠嗑到此结束,大家晚安。