【js 基础系列】没有那么简单的 new 操作符

如果我说你对 new 操作符可能不是很了解。很多人可能会很不屑,不就是 new 一个构造函数吗,来我给你背一下 new 的过程,巴拉巴拉巴拉……

然鹅 new 操作符真的那么简单吗?我们看下面的几个问题:

  1. new 一个构造函数发生了什么?
  2. 如果我们的构造函数是一个箭头函数,得到的结果是什么?
  3. 如果构造函数 return 一个普通对象,new 此构造函数得到的实例对象是什么?隐式原型指向什么?
  4. 如果构造函数 return 一个基本类型的值,此时 new 此构造函数得到的实例对象是什么?隐式原型指向什么?
  5. 如果构造函数 return 一个Array 类型的值,此时 new 此构造函数得到的实例对象是什么?隐式原型指向什么?
  6. 如果构造函数 return 一个基本包装类型的值(Number、String、Boolean),此时 new 此构造函数得到的实例对象是什么?隐式原型指向什么?
  7. 如果构造函数 return null 呢,此时 new 此构造函数得到的实例对象又是什么?隐式原型指向什么?

是不是有点怀疑自己了。

如果你可以全部正确回答,那可以关掉本篇博客去看其他知识点了,因为你对 new 已经了解的很透彻了,但是如果你一知半解,请继续往下看,看完本文之后相信你对 new 会有一个更深入的了解。

首先第一个问题,new 一个构造函数发生了什么?

Q:new 一个构造函数发生了什么?

老生常谈,很多人都可以说的很详细。以下是 MDN 给出的答案

  1. 创建一个空的简单JavaScript对象(即{});
  2. 链接该对象(即设置该对象的构造函数)到另一个对象 ;
  3. 将步骤1新创建的对象作为this的上下文 ;
  4. 如果该函数没有返回对象,则返回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 有了更清晰的认识呢

最后我们回顾上面的问题,发现其实可以总结为三种情况

  1. 箭头函数是特殊的函数,不可以使用 new 操作符。
  2. 如果构造函数返回基本类型的值,则我们得到的实例对象是构造函数隐式创建的对象,隐式原型指向构造函数的显式原型。这里我们观察没有 return 值的构造函数,发现其实就相当于 return undefined,所以也可以归类为返回基本类型得值
  3. 如果构造函数返回的是一个对象类型的值(包括 Object 的子集,Array、Number 等)则实例对象是我们 return 的值,且隐式原型的指向与 return 的值的隐式原型相同

到此,我们详细解析了 new 一个构造函数的过程

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值