如果我说你对 new 操作符可能不是很了解。很多人可能会很不屑,不就是 new 一个构造函数吗,来我给你背一下 new 的过程,巴拉巴拉巴拉……
然鹅 new 操作符真的那么简单吗?我们看下面的几个问题:
- new 一个构造函数发生了什么?
- 如果我们的构造函数是一个箭头函数,得到的结果是什么?
- 如果构造函数 return 一个普通对象,new 此构造函数得到的实例对象是什么?隐式原型指向什么?
- 如果构造函数 return 一个基本类型的值,此时 new 此构造函数得到的实例对象是什么?隐式原型指向什么?
- 如果构造函数 return 一个Array 类型的值,此时 new 此构造函数得到的实例对象是什么?隐式原型指向什么?
- 如果构造函数 return 一个基本包装类型的值(Number、String、Boolean),此时 new 此构造函数得到的实例对象是什么?隐式原型指向什么?
- 如果构造函数 return null 呢,此时 new 此构造函数得到的实例对象又是什么?隐式原型指向什么?
是不是有点怀疑自己了。
如果你可以全部正确回答,那可以关掉本篇博客去看其他知识点了,因为你对 new 已经了解的很透彻了,但是如果你一知半解,请继续往下看,看完本文之后相信你对 new 会有一个更深入的了解。
首先第一个问题,new 一个构造函数发生了什么?
Q:new 一个构造函数发生了什么?
老生常谈,很多人都可以说的很详细。以下是 MDN 给出的答案
- 创建一个空的简单JavaScript对象(即{});
- 链接该对象(即设置该对象的构造函数)到另一个对象 ;
- 将步骤1新创建的对象作为this的上下文 ;
- 如果该函数没有返回对象,则返回this。
我们用代码分解一下以上步骤
// 示例代码
function Parent (name) {
this.name = name
}
Parent.prototype.getName = function() {
return this.name
}
const people = new Parent('张三')
上面的代码看起来很简单,其实却做了很多的隐式操作,const people = new Parent('张三')
其实相当于做了如下操作
// 首先会创建一个对象,并且将此对象的隐式原型对象(__proto__)指向原型函数的显示原型对象(prototype)
const obj = Object.create(Parent.prototype)
// 将新创建的对象作为 this 的上下文
this = obj
// 执行构造函数中的代码
this.name = name
// 如果构造函数返回一个对象,则得到一个对象,否则得到我们隐式创建的对象
if (此构造函数返回一个对象) {
return 这个对象
} else {
return obj
}
如果我们的构造函数是一个箭头函数呢?
Q:如果我们的构造函数是一个箭头函数,得到的结果是什么?
// 示例代码
const Parent = (name) => {
this.name = name
}
Parent.prototype.getName = function() {
return this.name
}
const people = new Parent('张三')
首先提示不可以给 undefined 设置 getName
好,那我们去掉原型的设置
结果提示 Parent 不是一个构造函数
???,why?
我们看一下普通函数和箭头函数的区别
可以看到箭头函数是没有显示原型的,同样我们可以在 MDN 中找到答案。
箭头函数不能用作构造器,和 new一起用会抛出错误。
箭头函数没有prototype属性。
箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this。
由于 箭头函数没有自己的this指针,通过 call() 或 apply() 方法调用一个函数时,只能传递参数(不能绑定this),他们的第一个参数会被忽略。
等 ……
所以我们的第二个问题也有了答案
A:箭头函数是不可以做为构造函数的,因为箭头函数没有 this,且与普通函数相比缺少了 prototype,和 new 操作符一起使用会报错。
那如果我们的构造函数 return 一个普通对象呢?
Q:如果构造函数 return 一个普通对象,new 此构造函数得到的实例对象是什么?隐式原型指向什么?
// 示例代码
function Parent (name) {
this.name = name
return { like: '游戏', name: '李四' }
}
Parent.prototype.getName = function() {
return this.name
}
const people = new Parent('张三')
如上示例,我们看一下 people 是什么
为什么会这样呢?观察 MDN 文档中 new 的过程
如果该函数
没有返回对象
,则返回this。
所以这个问题我们也有了答案
A:如果构造函数返回一个对象,得到的实例对象就是我们构造函数 return 的实例对象本身,实例对象的原型也和构造函数的原型一致。
如果构造函数 return 一个基本类型
Q:如果构造函数 return 一个基本类型的值,此时 new 此构造函数得到的实例对象是什么?隐式原型指向什么?
// 示例代码
function Parent (name) {
this.name = name
return `李四爱玩游戏`
}
Parent.prototype.getName = function() {
return this.name
}
const people = new Parent('张三')
此时,我们得到的实例对象又是什么样子的呢?
可能有些同学已经猜到了,我们会得到构造函数隐式创建的对象。
还是可以在那句话中找到线索
如果该函数
没有返回对象
,则返回this。
我们得到结论:
A:如果构造函数 return 的值是一个基本类型,那我们得到的实例对象是构造函数隐式创建的对象。隐式原型指向构造函数的显示原型。
如果构造函数 return 一个 Array 类型
Q:如果构造函数 return 一个 Array 类型的值,此时 new 此构造函数得到的实例对象是什么?隐式原型指向什么?
// 示例代码
function Parent (name) {
this.name = name
return [ '李四', '游戏' ]
}
Parent.prototype.getName = function() {
return this.name
}
const people = new Parent('张三')
我们在控制台输出一下结果,看实例对象是什么
这里不难理解为什么会得到数组,因为 Array 本身就是 Object 的子集,JS 里数组是用 Object 实现的。
A:如果构造函数 return 一个数组,实例对象就是我们 return 的数组,隐式原型指向也与其相同。
如果构造函数 return 一个基本包装类型的值
Q:如果构造函数 return 一个基本包装类型的值(Number、String、Boolean),此时 new 此构造函数得到的实例对象是什么?隐式原型指向什么?
// 示例代码
function Parent (name) {
this.name = name
return new String(`李四爱玩游戏`)
}
Parent.prototype.getName = function() {
return this.name
}
const people = new Parent('张三')
看到这里相信大家都已经知道答案了,得到的实例对象就是我们 return 的值
也不难理解,基本包装类型也是 Object 的子集,也是一个对象。也不难推导出,其他的像 Date、BigInt 等也是与此相同。
A:如果构造函数 return 一个基本包装类型,实例对象就是我们 return 的值,隐式原型指向也与其相同。
如果构造函数 return null
Q:如果构造函数 return null 呢,此时 new 此构造函数得到的实例对象又是什么?隐式原型指向什么?
// 示例代码
function Parent (name) {
this.name = name
return null
}
Parent.prototype.getName = function() {
return this.name
}
const people = new Parent('张三')
这里为什么会单独把 null 拿出来呢?因为 null 在 js 里的位置很尴尬,它是一种基本类型的值,但是 typeof null 的结果却是 object,所以为了更清晰,我们单独测试一下他的结果,如下
可以看到 typeof 的检测结果对其本质没有影响,还是和基本类型得到的结果一致
A:如果构造函数 return 的值是 null ,typeof 不影响其结果,和基本类型得到的结果相同
总结
到此我们把所有的情况都测试了一遍,是不是对 new 有了更清晰的认识呢
最后我们回顾上面的问题,发现其实可以总结为三种情况
- 箭头函数是特殊的函数,不可以使用 new 操作符。
- 如果构造函数返回基本类型的值,则我们得到的实例对象是构造函数隐式创建的对象,隐式原型指向构造函数的显式原型。这里我们观察没有 return 值的构造函数,发现其实就相当于 return undefined,所以也可以归类为返回基本类型得值
- 如果构造函数返回的是一个对象类型的值(包括 Object 的子集,Array、Number 等)则实例对象是我们 return 的值,且隐式原型的指向与 return 的值的隐式原型相同
到此,我们详细解析了 new 一个构造函数的过程