引言
在这篇文章——继承和原型链中我们学习了prototype和__proto__的概念,我觉得这样能够更好的理解new的实现吧。
在StackOverflow 上的一个被广泛认可的解释:
__proto__
is the actual object that is used in the lookup chain to resolve methods, etc.
(真正用来查找原型链去获取方法的对象。)
prototype
is the object that is used to build __proto__
when you create an object with new
.
(用new创建对象时用来构建__proto__的对象)
new 发生了什么?
我们经常用到new Fun()
,new即为新的,那么他怎么就new了呢?让我们看看到底怎么个事儿。
function Person(name,num) {
this.name = name;
this.num=num;
}
console.log( new Person('Bryant',24));
我们可以看见,在node环境和浏览器控制台都输出了一个Person对象,同时呢,创建一个新的实例对象(并赋给了shooter)还会引用构造函数Person(),并且还继承有Person的方法shoot(),所以,shooter确实是Person的实例对象。
那么这其中发生了什么呢?
从上面的例子中我们可以发现:
- 他返回了一个对象,我们暂且将它标为obj,
- obj还继承了Person的方法
- 还有一个大家发现没:obj也有了name和num。
如果我们来手搓一个new 怎么实现呢? 想想:
-
首先我们要创造一个空的对象obj{}。
-
我们还要让obj继承Person的方法。
-
我们知道js会隐式地声明未有的属性,复制的话太费力,所以可能某种方法使用了Person的构造函数,但是那个this却指向了obj。然后查到了确实有这么几种方法。到这儿来
-
最后呢,我们还要返回这个obj。
好了 可以搓了
-
我们创建一个ourNew(){}方法,然后有一个我们需要new的对象Fun,然后它会有其它的参数name、num等,所以我们用动态参数来传值
-
同时创建一个空对象obj
outNew(Fun,...args){
}
-
设置obj的原型链指向Fun的原型对象,以此来达到继承Fun的方法。(有诸多方法,为提升性能)
obj.__proto__=Object.create(Func.prototype)
便于代码的可读性。let newObj = Object.create(Func.prototype)
与上一步结合起来,据说可以兼容IE。obj.__proto__=Fun.prototype
(Object.create(Func.prototype)会创建一个新的对象来赋给obj.proto)这样省去了不必要的中间对象。
-
改变this指向,在obj上挂载Fun属性。此过程会调用构造函数。将Fun中的this指向obj 并将args作为参数一一传进去。
Fun.apply(obj,args)
适用于动态传递参数数量的情况(例如,当参数存储在数组中时)。Fun.call(obj,args)
适用于参数数量已知且固定的情况。Fun.bind(obj,args)
适用于需要在稍后执行或在不同上下文中多次调用的情况。
-
返回对象obj
- return obj
ourNew(Fun,...args){
let obj = Object.create(Fun);
Fun.apply(obj,args)
return obj
}
你不会以为就这么简单吧
其实呢 就是这么简单,原理就是如此,但是还有很多我们可优化的地方。
-
构造函数会不会有返回值呢?返回的是一个String、number值呢?
-
Fun一定会是个方法体吗?
- . . .
/**
* 模拟 new 操作符的行为,创建一个构造函数的实例
* @param {Function} Constructor - 要实例化的构造函数
* @param {...any} args - 传递给构造函数的参数
* @returns {Object} - 返回构造函数的实例
*/
function ourNew(Constructor, ...args) {
// 检查第一个参数是否是函数
if (typeof Constructor !== 'function') {
throw new Error('第一个参数必须是构造函数');
}
// 创建一个新的空对象
const obj = {};
// 将新对象的原型设置为构造函数的原型
// 这样新对象可以访问构造函数原型链上的属性和方法
obj.__proto__ = Object.create(Constructor.prototype);
// 调用构造函数,并将 this 绑定到新创建的对象上
const result = Constructor.apply(obj, args);
// 如果构造函数返回的是对象或函数,则返回该结果
// 否则返回新创建的对象
const isObject = typeof result === 'object' && result !== null;
const isFunction = typeof result === 'function';
return isObject || isFunction ? result : obj;
}
// 测试用的构造函数
function Constructor(name) {
this.name = name;
// 返回一个函数用于测试
return function() {
console.log('返回引用数据类型');
};
}
// 在构造函数的原型上添加一个方法
Constructor.prototype.sayName = function() {
console.log(`My name is ${this.name}`);
}
// 创建一个 Constructor 实例
const me = ourNew(Constructor, 'kailin');
// 调用实例方法
me.sayName();
// 输出实例对象
console.log(me);
不出意外的话、应该要出意外了
大家可以找找什么原因,思考一下
总结
我们理解了 new
操作符的实现原理,通过一个简单的示例说明了在 JavaScript 中使用 new
操作符创建对象的过程,并且展示了如何手动实现 new
操作符的功能,自此相信各位能够更好地理解js底层中的逻辑,也因此有所体会甚至升华,指尖弹出更多的创意。
本文仅个人理解,若有纰漏之处还请指正,一同学习。若有同仁见此句,厚脸求个免费的赞 (^▽^)