前言
工作中,很多场景都需要使用new
运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。那么不管是直接{}
还是new constructor[([arguments])]
本质上,都是在使用new
运算符来初始化实例。
这里我们先来复习下构造函数的一些基本知识。以对后面的问题做铺垫。
构造函数
构造函数包含实例成员和静态成员,各自的含义为:
- 实例成员: 实例成员就是在构造函数内部,通过this添加的成员。
- 静态成员: 在构造函数本身上添加的成员。
这俩哥们的区别:
- 实例成员只能通过实例化的对象来访问。
- 静态成员只能通过构造函数来访问
我们来通过以下代码认识了解。
function Father(name,age) {
// 实例成员
this.name = name;
this.age = age;
}
// 静态成员
Father.sex = '男';
let sun = new Father('大兵',18);
console.log(sun); // Father {name: "大兵", age: 18}
console.log(sun.sex); // undefined 实例无法访问sex属性
console.log(Father.age); // undefined 构造函数无法直接访问实例成员
console.log(Father.sex); // 男 构造函数可直接访问静态成员
那么,为何创建的实例无法访问构造函数的静态成员?这里先放一放,我们稍后解答。
构造函数创建对象
首先,先通过构造函数创建一个对象。
function Father(name) {
this.name = name;
}
let son = new Father('Lisa');
console.log(son); //Father {name: "Lisa"}
此时,son就是一个新对象,也是创建的实例。
创建对象内部步骤
new
一个新对象的过程中,内部发生的步骤:
- 创建一个空对象 son
{}
; - 为 son 链接到原型链
son.__proto__ = Father.prototype
; - 重新绑定this,使构造函数的this指向新对象
Father.call(this)
; - 为新对象属性赋值(如果有必要)
son.name
; - 返回新对象;
手动实现
试着来自己实现一个 new
:
function myNew(constructor, ...args){
const obj= {};
obj.__proto__ = constructor.prototype;
const ret = constructor.call(obj, ...args);
return ret instanceof Object ? ret : obj;
}
应用:
myNew(Father, 'Lisa') // 输出 {name: 'Lisa'}
new的实例无法访问构造函数的静态成员解析
回到最开始提到的问题,为何new的实例无法访问构造函数的静态成员呢?其实看一看new
的内部实现就清楚了。
导致new的实例无法访问构造函数的静态成员的关键一步是,实例没有继承或者无法访问到构造函数的静态成员。
在new
的内部实现中,实例只继承了构造函数的原型。看这一步:
obj.__proto__ = constructor.prototype;
再进一步验证,Father.prototype.sex
打印的结果为 undefined
,所以就导致了在前面实例 sun
,sun.sex
的结果为 undefined
。
好了,以上就是本次关于new
运算符的相关知识总结,完结撒花🌸🌸🌸~。