全面解析new运算符

发布时间:2023-05-21

new运算符是JavaScript中的重要概念之一,是我们在编程、开发中经常会使用到的。在本文中,我们将从多个方面对new运算符进行详细的讲解,希望能给大家带来更深入的理解。

一、new 运算符的基本原理

new 运算符用于创建一个实例对象,先创建一个空对象,然后调用实例对象的构造函数,将空对象作为this传入,执行构造函数里面的逻辑,同时this指向空对象,返回一个这个空对象的引用,这个引用就是new出来的实例对象。 下面是一个简单的演示,展示new运算符的基本原理:

function Person(name, age) {
  this.name = name;
  this.age = age;
}
let person = new Person('Tom', 18);
console.log(person); // Person {name: "Tom", age: 18}

在上面的例子中,我们先定义了一个构造函数 Person,然后用new运算符创建一个实例对象 person。执行 new Person('Tom', 18) 时,JavaScript 引擎会先创建一个空对象,然后把这个空对象作为参数调用 Person 函数,并且在执行 Person 函数时将this指向新建的这个空对象,这样就可以给空对象添加属性了,最后将这个对象返回。

二、new 运算符与Object.create()的差别

很多人在理解new运算符的时候会与Object.create()搞混,这里我们来看一下他们的区别。 Object.create()也是用来创建对象的方法,但它创建的对象是以指定对象为原型创建的。这个对象的原型链上将会包含指定对象,如果指定对象为null,那么它将创建一个没有原型的对象。 下面是一个简单的演示:

let person1 = Object.create(null);
let person2 = new Object();
console.log(person1.__proto__); // undefined
console.log(person2.__proto__); // {}

在上面的代码中,我们通过Object.create(null)来创建了一个没有原型的对象,然后通过person1.__proto__可以看到它的值为undefined;而通过new Object()创建的person2对象,它的原型链上包含了一个空对象{}

三、new 运算符和构造函数

前面我们已经提到new 运算符用于创建一个实例对象,它的实际操作是先创建一个空对象,再调用实例对象的构造函数,将空对象作为this传入,执行构造函数里面的逻辑,同时this指向空对象,返回一个这个空对象的引用。 构造函数是用来创建对象的函数,可以带参数,也可以不带参数。它的内部实现逻辑就是new 运算符的逻辑,只是在“实例对象”的创建部分略有不同。构造函数在被new调用时,系统会自动创建一个空对象并将其传入构造函数,并且在构造函数中使用的this指向这个新创建的空对象。最后,将该对象返回作为new表达式的值,从而完成了“实例对象”的创建。

function Person(name, age) {
    this.name = name;
    this.age = age;
}
let person = new Person("Tom", 18);
console.log(person);

在上面的代码中,我们首先定义了一个构造函数,它的作用是给每个实例对象添加一个nameage属性。然后使用new运算符来创建一个实例对象person

四、new 运算符与继承

在JavaScript中,对象之间的继承可以通过原型实现。通过定义对象的原型,可以让这个对象从原型对象中继承属性和方法。可以先通过new运算符创建一个父类实例对象,再把这个实例对象作为子类的原型。这样一来,子类就可以从父类上继承到属性和方法。

function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.sayInfo = function () {
    console.log("我叫" + this.name + ",我今年" + this.age + "岁");
}
function Student(name, age, grade) {
    this.name = name;
    this.age = age;
    this.grade = grade;
}
Student.prototype = new Person();
let stu = new Student("Tom", 19, 1);
stu.sayInfo(); // 我叫Tom,我今年19岁

在上面的例子中,我们先定义了一个构造函数Person,它有两个属性nameage,并且定义了一个原型上的属性sayInfo。然后我们定义了一个构造函数Student,它继承了Person的属性和方法,又自己有一个属性grade。最后通过new运算符创建了一个Student实例对象stu,调用stu.sayInfo()方法,会打印出:“我叫Tom,我今年19岁”,说明Student实例对象stu继承了PersonsayInfo方法。

五、new 运算符和class语法

在ES6中,通过类与继承的语法糖,我们可以更方便地定义构造函数,并且支持继承。使用class语法糖,可以更加直观地展示出继承关系。

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    sayInfo() {
        console.log(`我叫${this.name},我今年${this.age}岁`);
    }
}
class Student extends Person {
    constructor(name, age, grade) {
        super(name, age);
        this.grade = grade;
    }
}
let stu = new Student("Tom", 19, 1);
stu.sayInfo(); // 我叫Tom,我今年19岁

在上面的代码中,我们通过class语法糖定义了两个类,分别是PersonStudentPerson类有两个属性nameage,和一个方法sayInfoStudent类继承了Person类,并且新增了一个属性grade。最后通过new运算符创建了一个Student实例对象stu,调用stu.sayInfo()方法,会打印出:“我叫Tom,我今年19岁”,说明Student实例对象stu继承了PersonsayInfo方法。