本文深入探讨JavaScript中实现new操作的两种主要方式:传统构造函数调用与Object.create。通过对比分析,揭示Object.create如何基于原型创建对象,并解析其内部实现原理。文章详细讲解了原型链查找机制、__proto__与prototype的关系,以及为何在特定场景下Object.create能避免实例间状态污染,帮助开发者理解对象创建的底层逻辑。
前言
当你使用 new 关键字实例化一个构造函数时,底层究竟发生了哪些步骤?
“众所周知”的三步:
首先创建一个空对象,将其引用赋给 this,并使其继承函数的原型;接着通过 this 将属性和方法添加至该对象;最后返回指向新对象的 this,即生成的实例。
在 JavaScript 中,这一过程通常表现为以下代码逻辑:
function Mynew(parent,...rest){
let obj={};
obj.__proto__=parent.prototype;
let res=parent.apply(obj,rest);
return typeof res=="object"?res:obj;
}
上述提到的“空对象”仅为简称。事实上,即便定义一个
{},它也会从Object.prototype上继承大量方法和属性。 《JavaScript语言精髓与编程实践》中提出了一个“更加空白的对象”概念:它存在两种情况 ——Constructor.prototype值为 null;或者Object.getPrototypeOf(obj)值为 null。
但在实际开发中,我们常看到另一种实现写法:
function Mynew_2(parent,...rest){
let child=Object.create(parent.prototype);
let result=parent.apply(child,rest);
return typeof result=="object"?result:child;
}
这两种实现方式的差异无疑激发了我的探究兴趣!
Object.create() 是如何实现的?
让我们通过一段代码来观察:
var Best1=function(){
this.a=2;
}
var o1=Object.create(Best1);
var o2=Object.create(Best1.prototype);
console.log(o1.a); // undefined
console.log(o2.a); // undefined

以 o1 为例,可以看到 Object.create() 失去了对原对象属性的访问权限,而 o2 ……同理。
再来看另一段代码:
var Best=function(){
this.a=2;
}
Best.prototype.a=3;
var o1=Object.create(Best);
var o2=Object.create(Best.prototype);
console.log(o1.a); // undefined
console.log(o2.a); // 3

至此,我似乎明白了什么,又似乎仍感困惑……
我决定进行如下测试:
let b=new Best();
console.log(b);

console.log(Best.prototype);

恍然大悟!
原来我们平时所说的“当读取实例属性时,如果找不到,就会查找与对象关联的原型中的属性;如果还找不到,就去找原型的原型,直到最顶层(__proto__ 为 null)为止”,实际上是指“一直顺着 __proto__ 向上查找”(注意:不经过 prototype!)。
那么,__proto__ 和 prototype 之间究竟存在何种联系?
console.log(b.__proto__);
console.log(Best.prototype);

哦!这就是我们常听到的“实例的 __proto__ 等于对象的 prototype”吧。
现在回过头来,打印一下 o2 这个对象:
console.log(o2);

你有没有想到什么?
根据上述描述,o2 确实是 Best 的实例!
因此,create() 函数实际上返回了一个对象的实例?
但 o1 仍然“不为所动”!

因此我们可以推测:在 Object.create() 函数中,应该是获取对象的原型并以实例形式返回:
Object.create=function(o){
let F=function(){};
F.prototype=o;
return new F();
}
为何这里要用函数再 new 的方式?而不是直接用对象去接收?

这与“为什么 Vue 中 data 是一个函数而不是直接的对象”本质上是同一个问题:JavaScript 中的对象是引用类型,若在一个实例中改变某个元素的值,其他实例的值也会随之改变!
而通过 create 函数则不会发生这种情况:

总结
到此这篇关于 JavaScript 中实现 new 的两种方式的讲解就介绍到这了,更多相关 JS 实现 new 的方式内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!