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

相关推荐
甜兒.25 分钟前
鸿蒙小技巧
前端·华为·typescript·harmonyos
她似晚风般温柔7893 小时前
Uniapp + Vue3 + Vite +Uview + Pinia 分商家实现购物车功能(最新附源码保姆级)
开发语言·javascript·uni-app
Jiaberrr4 小时前
前端实战:使用JS和Canvas实现运算图形验证码(uniapp、微信小程序同样可用)
前端·javascript·vue.js·微信小程序·uni-app
everyStudy4 小时前
JS中判断字符串中是否包含指定字符
开发语言·前端·javascript
城南云小白4 小时前
web基础+http协议+httpd详细配置
前端·网络协议·http
前端小趴菜、4 小时前
Web Worker 简单使用
前端
web_learning_3214 小时前
信息收集常用指令
前端·搜索引擎
Ylucius4 小时前
动态语言? 静态语言? ------区别何在?java,js,c,c++,python分给是静态or动态语言?
java·c语言·javascript·c++·python·学习
tabzzz4 小时前
Webpack 概念速通:从入门到掌握构建工具的精髓
前端·webpack
200不是二百5 小时前
Vuex详解
前端·javascript·vue.js