别再蒙了! 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了吗?😁😎😊

相关推荐
XTransfer技术几秒前
RN也有微前端框架了? Xtransfer的RN优化实践(一)多bundle架构
前端·react native
Mintopia几秒前
Next 全栈之 API 测试:Supertest 与 MSW 双雄记 🥷⚔️
前端·javascript·next.js
泽泽爱旅行2 分钟前
awk 语法解析-前端学习
linux·前端
鹏多多11 分钟前
纯前端人脸识别利器:face-api.js手把手深入解析教学
前端·javascript·人工智能
无奈何杨1 小时前
CoolGuard增加枚举字段支持,条件编辑优化,展望指标取值不同
前端·后端
掘金安东尼1 小时前
工具过多:如何管理前端工具泛滥?
前端
江城开朗的豌豆1 小时前
从生命周期到useEffect:我的React函数组件进化之旅
前端·javascript·react.js
brzhang1 小时前
当AI接管80%的执行,你“不可替代”的价值,藏在这20%里
前端·后端·架构
江城开朗的豌豆1 小时前
React组件传值:轻松掌握React组件通信秘籍
前端·javascript·react.js
Sailing1 小时前
别再放任用户乱填 IP 了!一套前端 IP 与 CIDR 校验的高效方案
前端·javascript·面试