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);
在上面的代码中,我们首先定义了一个构造函数,它的作用是给每个实例对象添加一个name
和age
属性。然后使用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
,它有两个属性name
和age
,并且定义了一个原型上的属性sayInfo
。然后我们定义了一个构造函数Student
,它继承了Person
的属性和方法,又自己有一个属性grade
。最后通过new
运算符创建了一个Student
实例对象stu
,调用stu.sayInfo()
方法,会打印出:“我叫Tom,我今年19岁”,说明Student
实例对象stu
继承了Person
的sayInfo
方法。
五、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
语法糖定义了两个类,分别是Person
和Student
。Person
类有两个属性name
和age
,和一个方法sayInfo
。Student
类继承了Person
类,并且新增了一个属性grade
。最后通过new
运算符创建了一个Student
实例对象stu
,调用stu.sayInfo()
方法,会打印出:“我叫Tom,我今年19岁”,说明Student
实例对象stu
继承了Person
的sayInfo
方法。