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.getPrototypeOf
abstract operation. Setting the[[Prototype]]
internal slot of an object at run-time is accomplished using theObject.setPrototypeOf
abstract 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了吗?😁😎😊