我们可以手动断开JS中预定义的数字、字符串、布尔值、数组的原型链,或者重新赋值吗

当我们创建一个Number类型的新变量(比如Num1)时,它的__proto__指向Number Object。它的__proto__将再次指向Object Core,其__proto__指向null,从而结束原型链。

我尝试做的是通过尝试将__proto__链接从Number Object更改为某个字符串来覆盖中间的这个链。我无法中断链,因为在我的赋值之后,它仍然指向Object Core结构。

断开此原型链是我做错了什么,还是无法在浏览器中断开预定义的原型链!

Number.__proto__ = "abc"; 
let num4 = 8;
console.log(num4.__proto__);//returns Number Object
console.log(num4.__proto__.__proto__)//should return string "abc"

输出:

不过,我知道我可以通过以下代码在链的中间添加某些项目(如Number-Object):

Number.prototype.alpha = "def";
let num5 = 99; 
console.log(num5.__proto__);

输出:


解决方案

修改基本对象的内置原型或内置原型链通常是非常糟糕的想法。我强烈建议您不要这么做。

Number.__proto__Number函数的原型,不是Number对象(即Number.prototype)的原型。

此外,JavaScript同时具有Number基元和Number对象。大多数情况下,您要处理的是一个原始的数字。当您在Number基元(n = 42)(如LIKEtoString)上使用属性(包括方法)时,即使n是基元,JavaScript引擎也会从Number.prototype获取这些属性。

您可以更改Number.prototype的原型。例如,您可以将其设置为null,以便Number对象不再继承Number.prototype以外的任何对象(用Object.prototype断开链接),这意味着对数字原语的属性查找不再从Object.prototype中找到属性和方法:

const n = 8;
console.log(typeof n.hasOwnProperty);           // function
console.log(Object(n) instanceof Number);       // true
console.log(Object(n) instanceof Object);       // true
Object.setPrototypeOf(Number.prototype, null);
console.log(typeof n.hasOwnProperty);           // undefined
console.log(Object(n) instanceof Number);       // true
console.log(Object(n) instanceof Object);       // false

(Object(n)返回数字原语nNumber对象。我在那里使用它是因为instanceof对于原语总是false[例如n instanceof Numberisfalse]。若要检查继承,我们临时需要一个对象。)

如您所见,Number对象和Object之间的链接已断开。

再说一次,这在一般情况下是一个非常糟糕的想法,因为它往往会打碎东西。仅仅因为我们可以做并不意味着我们应该。

不过,我知道我可以通过以下代码在链的中间添加某些项目(如Number-Object):

Number.prototype.alpha = "def";
let num5 = 99; 
console.log(num5.__proto__);

这只是向Number.prototype添加属性,而不是向原型链中插入某些内容。但是也可以插入到原型链中,因为我们可以更改Number.prototype

的原型
function Custom() {
}
// Change `Custom.prototype` to an object whose prototype
// is the prototype of `Number.prototype` (which will be
// `Object.prototype` in an unsullied environment).
// (`Object.create` creates an object setting its prototype
// to the given object.)
Object.defineProperty(Custom, "prototype", {
    value: Object.create(Object.getPrototypeOf(Number.prototype)),
    writable: true,
});
Object.defineProperty(Custom.prototype, "constructor", {
    value: Custom,
    writable: true,
    configurable: true,
});
Object.defineProperty(Custom.prototype, "example", {
    value() {
        return "hi there";
    },
    writable: true,
    configurable: true,
});
Object.setPrototypeOf(Number.prototype, Custom.prototype);

const n = 8;
console.log(n.example());                 // "hi there"
console.log(Object(n) instanceof Custom); // true

我在那里使用了构造函数,这样我们就可以方便地使用instanceof检查继承,但是您可以在没有构造函数的情况下插入原型。下面是不带一个相同代码:

const custom = Object.create(Object.getPrototypeOf(Number.prototype));
Object.defineProperty(custom, "example", {
    value() {
        return "hi there";
    },
    writable: true,
    configurable: true,
});
Object.setPrototypeOf(Number.prototype, custom);

const n = 8;
console.log(n.example()); // "hi there"


注意1:最好避免更改现有对象的原型(例如,使用Object.setPrototypeOf)。JavaScript引擎基于这样的假设进行优化(通常是正确的),即对象的原型在创建之后不会更改。更改它会打破这一假设,从而从对象中删除这些优化。

注意2:您会注意到我没有在上面的任何地方使用__proto__ accessor property。它已弃用,不应在新代码中使用,请改用Object.getPrototypeOf和(如果绝对必要)Object.setPrototypeOf。对于不是从Object.prototype继承任何对象,它也会失败,因为__proto__访问器属性就是在这里定义的。

相关文章