new的作用 ✨✨✨
- 通过
new操作符创建一个构造函数实例。 - 首选初始化一个空的
obj对象 - 实例对象的
__proto__指向构造函数的prototype - 构造函数中的
this指向实例对象 - 如果构造函数有返回值并且是引用类型值,则返回此返回值;如果没有返回值或者返回值为基础类型值,则返回
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 theObject.getPrototypeOfabstract operation. Setting the[[Prototype]]internal slot of an object at run-time is accomplished using theObject.setPrototypeOfabstract operation. The[[Prototype]]internal slot of an object may also be exposed indirectly to ECMAScript code using the accessorsObject.prototype.__proto__orObject.prototype.isPrototypeOf.这里说明了
[[Prototype]]可以通过Object.getPrototypeOf来获取,也提到了__proto__的访问器的作用。8.3.1 [[GetPrototypeOf]] () > The [[GetPrototypeOf]] internal method of a native ECMAScript object O performs the following steps:
- Return ? O.[GetPrototypeOf].
- If O does not have a [[Prototype]] internal slot, then a. Return null.
- Let proto be the value of O's [[Prototype]] internal slot.
- If proto is not null, return proto.
- 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了吗?😁😎😊