前言
JavaScirpt中new关键字,是JavaScript的通配符,作用于构造函数,用于创建一个实例化对象。在面向对象编程,继承机制等都能找到new 关键字的身影。下文,对new 底层实现机制进行剖析,再手写一个new功能相同的函数,加深对new 的理解。
明白new 底层
-
- 创建一个新的对象
function Person(name) {
this.name = name;
}
const awei = new Person('awei辣');
const bwei = new Person('awei辣');
console.log(awei);
console.log(bwei == awei);
- 当我们使用
new
关键字调用Person 构造函数
时,JavaScript 创建了一个新的对象。 - 提问:明明两个传入相同属性值的wei对象,
bwei == awei
为什么输出false?- 因为
==
比较对象地址,因此,我们了解到bewi对象并不是调用awei在栈内存里面的引用,而是在堆内存创建拥有name='awei辣'属性的新对象。
- 因为
- 2.绑定this
function Person(name) {
this.name = name;
}
Person.prototype.sayName = function () {
console.log(this.name);
}
const awei = new Person('张三');
const bwei = new Person('李四');
console.log(awei.name); // 张三 调用属性
bwei.sayName() // 李四 调用新增方法
``
构造函数this绑定到新创建的对象,并且可以在构造函数内部对新对象进行属性和方法添加,例如`Person.prototype.sayName `
- 3.设置原型链
```js
function Person(name) {
this.name = name;
}
const awei = new Person('张三');
console.log(awei.name); // 张三 调用属性
new关键字不仅创建新对象,还将该对象的内部 [[Prototype]]
链接到了 Person.prototype
。这使得新对象能够继承来自 Person.prototype
的所有属性和方法。
- 3.返回处理值
前三项完成后,new将创建的对象返回wei,使得指向该栈内存的新对象引用。
了解完new 底层进行的四项操作后,下面写个函数实现一样的操作。
手撕 new
先总结使用 new 操作符 底层的机制 :
- 创建新对象:JavaScript 引擎首先创建了一个全新的空对象
{}
。- 设置原型链:接着,该新对象的内部属性
[[Prototype]]
(可通过__proto__
访问)被设置为Person.prototype
,建立了新对象与构造函数之间的原型链连接。- 绑定
this
:然后,构造函数内的this
被绑定到了新创建的对象上,使得我们可以在构造函数内部对新对象进行属性和方法的添加。- 返回新对象:最后,如果构造函数没有显式返回另一个对象,则默认返回新创建的对象。
- 实现源码如下(简单版objectFactory函数)
function Person(name,age) {
this.name = name;
this.age = age;
}
Person.prototype.sayName = function () {
console.log(this.name);
}
// 实现一个objectFactory 函数,创建一个对象,注意:不能使用new 关键字
function objectFactory(Fun,...args) {
let obj = {};
obj.__proto__ = Fun.prototype;
Fun.apply(obj,args);
return obj;
}
let awei = objectFactory(Person,'张三',18);
console.log(awei.name);
awei.sayName();
- 简洁版
function objectFactory(Constructor, ...args) {
const obj = Object.create(Constructor.prototype); // 使用 Object.create 设置 __proto__
Constructor.apply(obj, args); // 调用构造函数并传递参数
return obj;
}
这里使用ECMAScript里面Object.create 来设置__proto__,实现比较好的兼容和性能。同时,也使用剩余参数语法(...args)传入数组属性作为参数。
- 手写new关键字(高配版,objectFactory函数),附带注释
function objectFactory() {
const obj = new Object(); // 空对象
// [].shift 数组调用shift方法,调用.call 方法改变this指向,指向类数组arguments
const Constructor = [].shift.call(arguments);
// apply 参数一个传入this指向,一个是数组
Constructor.apply(obj,arguments);
// 手动实现__proto__ 指向Person.prototype,让实例化对象可以访问构造函数里面的方法
obj.__proto__ = Constructor.prototype;
return obj;
}
提问:
- 1.为什么高配版更值得面试官考察?
- 2.为什么使用shift(这个函数每次将数组第一个元素删除)?
- 3.一样实现this指向变化,使用call(arguments)为啥不使用bind或是apply
(欢迎评论区留下你的答案)
看下面图片,说明我们的手写new关键字成了。sayName 方法都可以调用。
总结
成长之路还是要persistently work ,赶快去试着自己手写一下吧~~
欢迎评论区留言讨论