别再蒙了! new操作符工作原理全面解析

new的作用 ✨✨✨

  1. 通过new操作符创建一个构造函数实例。
  2. 首选初始化一个空的obj对象
  3. 实例对象的__proto__指向构造函数的prototype
  4. 构造函数中的this指向实例对象
  5. 如果构造函数有返回值并且是引用类型值,则返回此返回值;如果没有返回值或者返回值为基础类型值,则返回obj对象。

实现myNew()函数

js 复制代码
function myNew(Con, ...args) {
  let obj = {}; // 创建一个空对象
  Object.setPrototypeOf(obj, Con.prototype);  // 将obj的__proto__设置为Con.prototype,接入原型链
  let result = Con.apply(obj, args); // 将构造函数中的this指向obj,并将构造函数参数传递给构造函数
  return result instanceof Object ? result : obj; // 判断构造函数是否返回值是否为引用类型值如果是则返回,否则返回实例对象obj
}

function Fn() {
  this.name = "zhangsan";
  this.age = 18;
}

let obj = myNew(Fn);
console.log(obj); // Fn { name: 'zhangsan', age: 18 }

手动中断原型链问题

思考一下下面代码的打印的值应该是什么?

js 复制代码
function Fn(name, age) {
  this.name = name;
  this.age = age;
}
Fn.prototype = null;
const obj = new Fn("zhangsan", 22);
console.log(obj.__proto__ == Object.prototype);
console.log(obj.__proto__ == Fn.prototype); 

可能有人会感觉应该是false 和 true ,但是在控制台输出的却是true 和 false,这就让人很疑惑了。为什么将Fn.prototype = null并且Object.setPrototypeOf(obj,Fn.prototype)两者应该相等才对啊!!!为什么obj.__proto__ = Object.prototype呢?

通过查阅资料有以下解释:

对象的 __proto__ 属性在查找上溯原型链的特性,是 JavaScript 中原型继承的核心机制之一。 这一机制可以在 ECMAScript 语言规范中找到相关描述:

ECMAScript® 2022 Language Specification - 9.1.1 Ordinary Object Internal Methods and Internal Slots When a constructor creates an object, that object implicitly references the constructor's "prototype" property for the purposes of resolving property references. The [[Prototype]] link is set at the time of object creation. The value of the object's [[Prototype]] internal slot is retrieved from the constructor's "prototype" property only once, at object creation time. If the value of the constructor's "prototype" property is modified after the object is created, the object's [[Prototype]] is not updated to reflect the change.

这里明确指出,对象的 [[Prototype]] 内部属性(在 JS 中为 __proto__ 属性)是在对象创建时从构造函数的 prototype 属性获取的。

9.1.2 Object Internal Methods and Internal Slots The value of the [[Prototype]] internal slot of an object may be accessed at run-time using the Object.getPrototypeOf abstract operation. Setting the [[Prototype]] internal slot of an object at run-time is accomplished using the Object.setPrototypeOf abstract operation. The [[Prototype]] internal slot of an object may also be exposed indirectly to ECMAScript code using the accessors Object.prototype.__proto__ or Object.prototype.isPrototypeOf.

这里说明了 [[Prototype]] 可以通过 Object.getPrototypeOf 来获取,也提到了 __proto__ 的访问器的作用。

8.3.1 [[GetPrototypeOf]] () > The [[GetPrototypeOf]] internal method of a native ECMAScript object O performs the following steps:

  1. Return ? O.[GetPrototypeOf].
  2. If O does not have a [[Prototype]] internal slot, then a. Return null.
  3. Let proto be the value of O's [[Prototype]] internal slot.
  4. If proto is not null, return proto.
  5. Return null.

这里详细描述了 [[GetPrototypeOf]] 的执行步骤,其中第 4 步表明如果当前对象的 [[Prototype]] 不为 null,则会返回继续上溯原型链。 所以 ECMAScript 规范中确实明确了对象 __proto__ 属性在找不到当前构造函数的 prototype 时,会继续在原型链上逐级查找的机制,这也保证了原型链不会因某个构造函数的 prototype 被修改或删除而中断。

通过上面的解释我们可以得出当手动中断构造函数的原型链时Fn.prototype = null,其构造函数实例化出来的实例对象的obj.__proto__不会指向Fn.prototype。而是根据js运行机制按照默认原型链想上寻找最近的原型并将其赋值给obj.__proto__。为了保证原型链的完整性。

由此我们可能会想到如果将Fn.prototype 设置为其他基本类型呢结果会是怎么样呢?那让我们来试一下吧!!!这里就展示Symbol的结果为true 和 false,其他的大家可以自行测试。 所以如果将构造函数的prototype设置为基本数据类型,实例对象的原型会指向对象的原型Object.prototype,😊😎 OK

js 复制代码
function Fn(name, age) {
  this.name = name;
  this.age = age;
}
let str = "";
let num = 111;
let bol = true;
let bigNum = BigInt(12345678901234567890);
let symbol = Symbol();
let und = undefined;
let nul = null;
Fn.prototype = symbol;
const obj = new Fn("zhangsan", 22);
console.log(obj.__proto__ == Object.prototype); // true
console.log(obj.__proto__ == Fn.prototype); // false

问题又来了!😁如果将构造函数的原型赋值为引用类型值呢?这个应该都知道哦😉我们来试一下。

js 复制代码
function Fn(name, age) {
  this.name = name;
  this.age = age;
}
let str = new String();

Fn.prototype = str;
const obj = new Fn("zhangsan", 22);
console.log(obj.__proto__); // String {''}
console.log(obj.__proto__ == Object.prototype); // false
console.log(obj.__proto__ == Fn.prototype); // true

从结果中可以知道,此时完全符合预期哦😉

OK,今天你学fei了吗?😁😎😊

相关推荐
几何心凉22 分钟前
openGauss:多核时代企业级数据库的性能与高可用新标杆
前端·数据库·数据库开发
AiXed2 小时前
PC微信协议之AES-192-GCM算法
前端·数据库·python
AllData公司负责人2 小时前
实时开发平台(Streampark)--Flink SQL功能演示
大数据·前端·架构·flink·开源
小满zs3 小时前
Next.js第五章(动态路由)
前端
清沫3 小时前
VSCode debugger 调试指南
前端·javascript·visual studio code
一颗宁檬不酸3 小时前
页面布局练习
前端·html·页面布局
zhenryx4 小时前
React Native 自定义 ScrollView 滚动条:开箱即用的 IndicatorScrollView(附源码示例)
javascript·react native·react.js·typescript
金木讲编程5 小时前
Claude、Agent与Copilot协作生成Angular应用
前端·ai编程
振华OPPO5 小时前
Vue:“onMounted“ is defined but never used no-unused-vars
前端·javascript·css·vue.js·前端框架
欧雷殿6 小时前
在富阳银湖成立地域化的软件研发团队
前端·程序员·创业