这是我参与8月更文挑战的第28天,活动详情查看:8月更文挑战
其实new操作符做的事情很简单,先来看看MDN定义
new运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。
官方话语,总是那么男爵😕,可以分为两部分:
- 创建一个用户定义的对象类型的实例;
- 创建一个构造函数的内置对象的实例。
创建一个用户定义的对象类型的实例
这个说的是new一个我们自定义的构造函数(不通过new操作符调用时,和普通函数一样)会返回一个对象类型的实例
// 自定义的构造函数
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
// new操作符调用时
const p1 = new Person('zs', 18, 'male')
// 返回一个对象,赋值给p1
p1 = {name: "zs", age: 18, sex: "male"}
可以看到原型对象上的
constructor属性指向的构造函数是Person(感觉像是废话😂),关于原型链还是想打个广告,可以看看我之前写的两篇文手撕祖传原型链图(上)、手撕祖传原型链图(下),相信会有所收获。
特征一
回到标题含义:我使用new操作符操作我自己创建的Person(构造)函数,结果就是我的p1变量确实等于一个对象了,而且我自定义的Person(构造)函数是没有返回值的。再来看定义:new 运算符创建一个用户定义的对象类型的实例,是new操作符在使用过程中进行了新建对象的操作,并且是执行函数体的第一行就已经创建了一个新对象(这是第一个特征)
特征二、三
再来看看p1的结果p1 = {name: "zs", age: 18, sex: "male"},返回一个对象我们已经知道了,为什么这个对象会有值呢?原来啊,new操作符在执行构造函数中,把函数体内的this指向改变为这个新建的空对象上去了,并且是执行函数体的第一行就已经改变了this的指向(这是第二个特征)
看图上我打印的一个log,显示这个this确实是指向了新创建的对象,有兴趣的朋友打开控制台输入调试看看,代码我贴在下面
// 步骤一
function Person(name, age, sex) {
console.log('this指向:', this) // 执行中,在此时已创建好新对象,已更改this指向为新对象
debugger
this.name = name; // 新对象 的 name = 传入的name
this.age = age; // 新对象 的 age = 传入的age
this.sex = sex; // 新对象 的 sex = 传入的sex
}
// 步骤二
const p3 = new Person('zs', 18, 'male')
并且还会对这个新对象创建__proto__连接到这个构造函数的原型对象上(这是第三个特征)
可以看到新创建的对象p4的[[prototype]]连接到的是Person的原型对象,在此再推荐一下关于原型链的两篇文手撕祖传原型链图(上)、手撕祖传原型链图(下) 😂
特征四
最后一个特征就是:当我的(构造)函数体内有返回值时,会有两种情况:
-
返回一个基本数据类型
返回基本数据类型时,例如字符串、数字、布尔值、undefined、null这些,不会改变构造函数返回对象的结果,相当于这个返回值对构造函数的返回值没有影响(但是你不要把
return放在前面了,return结束函数体运行的能力依然存在) -
返回一个复杂数据类型
例如Date、Array、Object、Map、Set等等,这些返回值会去改变这个构造函数的输出!!
-
示例一
看上图,这里返回的一个自己创建的空对象,结果就是实例对象
p7直接返回了空对象,并且这个空对象的原型对象并不是指向Person的原型对象,简直就是陪了夫人又折兵 -
示例二
这就能说明返回对象时(万物皆对象),构造函数就会返回这个对象,并不会去帮你返回new操作符创建好的新对象,而是返回return后面的对象
-
创建一个构造函数的内置对象的实例
这个就和自定义的构造函数流程一模一样了,不需要单独去理解,特征完全一样
小结
再来回顾下new创建构造函数时的四个特征吧:
-
创建一个新对象
-
构造函数执行中,
this的指向为这个新创建的对象 -
会对这个新对象进行
[[prototype]]连接,新对象的__proto__属性指向构造函数的prototype属性,即指向原型对象 -
函数体的返回值
- 没有返回值时,默认返回上面新创建的对象;
- 有返回值时,返回基本数据类型时,不影响;返回复杂数据类型(对象)时,这会构造函数就不会返回上面新创建的对象了
多去控制台敲代码才能提升自己的理解噢~ ✍