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

相关推荐
志尊宝18 分钟前
Vue3 零基础每日笔记(052):路由常见坑一次排雷——404、刷新丢参数、部署白屏
前端·javascript·vue.js·笔记·html5
linux_cfan20 分钟前
videojs v10 源代码系列解读:13 · 能力型媒体契约:`Media` 的组合接口
前端·javascript·音视频
涛涛ing25 分钟前
2026年9月,前端圈同时发生了四件事,指向同一个方向
前端
艾伦野鸽ggg38 分钟前
Vue2 模板语法与样式绑定
前端
Amos_Web39 分钟前
Rspack 源码解析(十一):资产 Hook 与增量构建
前端·rust·源码阅读
PC2005_cloud39 分钟前
Windows 数据使用量页面卡死:1097 个热点档案和 161 MB 的 SRUM 库
前端·后端
烈风逍遥41 分钟前
AI大模型中fetch 和 ReadableStream为啥一起出现
前端·人工智能
PC2005_cloud1 小时前
Nginx 防盗链配置实战:用 referer 模块保护网站静态资源
前端·后端
福兮说1 小时前
Canvas 文字排版:measureText 量出来的宽度为什么总是不对
前端·javascript
夏幻灵1 小时前
从零理解 Redux:从 state、action、reducer 到 Redux Toolkit 与异步请求
前端