通过原型扩展数学对象不起作用

Extending Math object through prototype doesn't work

本文关键字:对象 不起作用 扩展 原型      更新时间:2023-09-26

我尝试扩展JavaScript Math。但有一件事让我感到惊讶。

当我试图通过prototype扩展它时

Math.prototype.randomBetween = function (a, b) {
    return Math.floor(Math.random() * (b - a + 1) + a);
};

在控制台中,我有错误"无法设置未定义的属性'随机之间'"...

但是如果我将此功能分配给Math.__proto__

Math.__proto__.randomBetween = function (a, b) {
    return Math.floor(Math.random() * (b - a + 1) + a);
};

然后一切正常。

谁能解释我为什么它以这种方式工作?我感谢任何帮助。

Math不是

构造函数,因此它没有prototype属性:

new Math(); // TypeError: Math is not a constructor

相反,只需将方法添加为将自身Math为自己的属性:

Math.randomBetween = function (a, b) {
    return Math.floor(Math.random() * (b - a + 1) + a);
};

您使用__proto__的方法有效,因为由于Math是一个Object实例,因此Math.__proto__ Object.prototype

但请注意,您正在randomBetween方法添加到所有对象,而不仅仅是Math .这可能会有问题,例如,在使用 for...in 循环迭代对象时。

那是因为有一个对象Math而不是一个function

在javascript中,function大致相当于面向对象语言中的类。 prototype 是一个特殊属性,允许您将实例方法添加到此类1。 当你想扩展该类时,你使用prototype它"只是工作"。

现在让我们想想Math是什么。 你永远不会创建一个数学对象,你只是使用它的方法。 事实上,创建两个不同的Math对象是没有意义的,因为Math总是相同的工作! 换句话说,javascript 中的 Math 对象只是将一堆预先编写的数学相关函数组合在一起的便捷方法。 这就像一本普通数学的字典。

想要向该组添加内容? 只需将属性添加到集合中即可! 这里有两种简单的方法可以做到这一点。

Math.randomBetween = function() { ... }
Math["randomBetween"] = function() {... }

使用第二种方式可以更明显地表明它是一个字典类型的集合,但它们都做同样的事情。

引用这个答案:

一些JavaScript实现允许直接访问[[Prototype]]属性,例如通过名为__proto__的非标准属性。通常,只能在对象创建期间设置对象的原型:如果通过 new Func(( 创建新对象,则对象的 [[Prototype]] 属性将设置为 Func.prototype 引用的对象。

无法使用 .prototype 分配给其原型的原因是Math对象已创建。

幸运的是,我们可以通过简单地使用以下命令为 Math 对象分配新属性:

Math.myFunc = function() { return true };

在您的情况下,这将是:

Math.randomBetween = function(...) { ... };
var MyMath = Object.create(Math); // empty object with prototype Math
MyMath.randomBetween = function (a, b) {
    return this.floor(this.random() * (b - a + 1) + a);
};
typeof(MyMath);                 // object
Object.getPrototypeOf(MyMath);  // Math
MyMath.PI;                      // 3.14...
MyMath.randomBetween(0, 10);    // exactly that
  • Math对象是新MyMath对象的原型
  • MyMath可以访问Math的所有功能
  • 您可以将自定义功能添加到MyMath而无需操作Math
  • 在自定义方法中,使用关键字this来引用Math功能

这种方法没有猴子补丁。这是扩展 JavScript Math 的最佳方式。没有必要重复其他答案的解释。

虽然Math是一个实际的Object而不是一个constructible class但您可以使用spread operator: {...obj}来扩展数学对象,如下所示:

const ExtendMath = {
    ...Math,
    randomInt(max, min = 0){Math.floor(Math.random() * (max - min + 1)) + min},
}

现在,randomInt() 方法将作为扩展方法添加到Math对象中。