在JavaScript/ActionScript中重新定义Math.constructor有什么实际用途?

3

Math对象没有原型属性,但有构造函数属性。是否存在重新定义构造函数的情况会很有用?


1
Math.constructor === Object // true ... Math.constructor дЄО Object зЫЄеРМпЉМеЫ†ж≠§ Math.constructor("foo") дЄО Object("foo") зЫЄеРМгАВ - Dagg Nabbit
8个回答

6

在ECMAScript全局对象的Math属性的初始值所引用的对象中,Math对象(确切地说)没有constructor属性,详见ECMAScript语言规范5.1版,第15.8节“Math对象”。因此,

Math.hasOwnProperty("constructor")

在符合ECMAScript Ed.3及更高版本的实现中,返回false

Math对象通过原型链从其原型继承了constructor属性,其原型是“标准内置Object原型对象(15.2.4)”(如上所述),这与最初由Object.prototype属性引用的相同。后者提供了几个有用的属性,例如Object.prototype.hasOwnProperty(见上文)。因此,Math对象的原型链不是空的,这是有意义的。

Math对象还继承了Object.prototype.constructor,这只是ECMAScript实现中无条件继承的副作用(除了Edition 4提案的实现和可能的未来版本之外,属性没有适当的可见性修饰符来防止这种情况,就像几种基于类的语言中的private一样)。当然,继承自相同原型的Object实例的构造函数是全局对象的Object属性的初始值所引用的对象。因此,Object.prototype.constructor必须反映这一点。因此,评估结果为

Math.constructor === Object

true


那么,为什么 Math.constructor === Object 是正确的,而 Object.getPrototypeOf(Math) === Math.constructor() 是错误的呢? - Paul Sweatte
1
@PaulSweatte 这毫无意义。在任何正常情况下,Object.getPrototypeOf(x) 何时等于 x.constructor(或 x.constructor())? - Dagg Nabbit
1
@PaulSweatte 也许你想检查的是 Object.getPrototypeOf(Math) === Math.constructor.prototype - Dagg Nabbit

5
所有对象都有一个“constructor”属性,该属性指示创建该对象的构造函数。 即使是({}).constructor也返回Object()。

3
请将其改为“通常”。您可以删除 Object.prototype.constructor。此外,当使用“Constructor.prototype = {...}”设置原型时,“constructor”属性经常会丢失/错误。 - Bergi
2
@Bergi 删除 Object.prototype.constructor 不是一个好主意。但是,您可以创建一个具有空原型链的对象,因此不继承任何属性。这样的对象作为数据容器非常有用,因为几乎没有任何属性名称冲突的可能性。自第5版以来,可以在不使用专有的 __proto__ 属性的情况下实现此操作。例如,Object.create(null).constructorundefined - PointedEars

3
实际上,数学对象并没有自己的"constructor"属性。它从Object.prototype继承"constructor",就像它继承"toString"、"hasOwnProperty"和所有其他Object.prototype的属性一样。
对于数学对象来说,"construct"可能有很少的实用性。它只是因为JavaScript继承的工作原理而存在的。

数学没有原型,那么它是如何继承的? - Paul Sweatte
1
@PaulSweatte:Math的原型是Object - SLaks
Math.prototype 返回未定义。 - Paul Sweatte
1
@PaulSweatte 请检查 Math.__proto__,或使用 Object.getPrototypeOf(Math)。两者都等同于 Object - Rob W
所以 Math.proto 和 Object.getPrototypeOf(Math) 等于 Math.constructor()。 - Paul Sweatte
@PaulSweatte 不是的,它们不相等。 Math.__proto__ 是一个普通对象。 Math.constructorObject,因此 Math.constructor() 返回另一个普通对象,它等效但不等于Math.__proto__ - Dagg Nabbit

2

Math对象“继承”自Object(意味着Math.__proto__ === Object.prototype)。

对于任何JavaScript程序员来说,Math对象不过是一个“特殊”的但简单的Object,其方法的实现和构造都是自动和隐藏的。

Object.prototype定义了一个.constructor字段(实际上任何函数都会将自己分配给其自己原型的构造函数,请参见ex1

ex1(稍微有些弯路)

function xxx() { }
// then:
xxx.prototype.constructor === xxx; // true
// since xxx is a function:
xxx.constructor === Function.prototype.constructor; // true
// and as such:
xxx.constructor === Function; // true

因此,当您使用Math.constructor时,它只是像这样查找Math对象的原型链(...嗯,有点像):
  1. Math --> Math.hasOwnProperty('constructor')=== false

  2. 未找到,移至下一个

  3. Math.__proto__ --> Math.__proto__.hasOwnProperty('constructor')=== true

  4. 找到,返回:Math.__proto__.constructor

因此,基本上如下:
Math.constructor === Object.prototype.constructor; // true
// and as such:
Math.constructor === Object; // true
// and as stated at the top:
Math.__proto__ === Object.prototype; // true

希望这可以帮到您 -ck

2

MDN表示:

与其他全局对象不同,Math不是构造函数。Math的所有属性和方法都是静态的

在其他语言中,当一个类是静态的时,你可以直接使用它的属性和方法,而不需要创建该类(即对象)的实例。如果使用Math构造函数,则没有本地类型支持该对象,不像原始类型:Number、String、Boolean。它们可以使用其包装器转换为对象。

此外,扩展根对象是一种不好的做法。如果将来在环境中实现新功能并且代码没有对此进行故障安全检查,它将覆盖本机功能。

我个人的观点是您不需要构造函数,也不需要原型 - 您可以定义自己的数学函数。 Math对象仅在此处提供标准函数,并使程序员能够不定义例如PiE。可能用户定义的数学函数比内置函数慢几倍。


如果 Math 的属性和方法是静态的,那么通过定义自定义原型和构造函数来扩展它的危险在哪里? - Paul Sweatte
1
对于原型 - 也许将来会有,谁知道呢;将来的标准可能定义一些新的、完整的或其他的东西。但我不明白构造函数的意义 - 没有数学类型来支持由Math.constructor()创建的对象。 - Bakudan

0
在我看来,JavaScript 中的 Math 对象试图模拟其他流行编程语言(例如 Java)中的静态 Math 行为。由于这只能在 JavaScript 中模拟,并且所有对象默认都具有原型和构造函数属性,我的猜测是开发人员只是忘记将构造函数属性设置为 undefined,通过显式执行类似于 Math.constructor = undefined; 的操作来实现。

0
在需要生成转换表但不想污染全局作用域的情况下,这个方法非常有用。例如:
Math.constructor = function() {
  var i = 0, unicode = {}, zero_padding = "0000", max = 9999;
  //Loop through code points
  while (i < max) {
    //Convert decimal to hex value, find the character, then pad zeroes to the codepoint
    Math.constructor[String.fromCharCode(parseInt(i, 16))] = ("u" + zero_padding + i).substr(-4);
    i = i + 1;
    }    
  }

调用构造函数以填充它,如下所示:

Math.constructor();

Math.constructor["a"]

另一个用途是扩展属性和方法以定义图形库的原语:
root(arg,index) "index-th" root of argument. Example: root(x,6) sixth root of x, root[tan(x),4] fourth root of the tangent of x. sqrt() Square root of argument (number or expression inside the parentheses). Equivalent to root(argument,2) cbrt() Cube root of argument. Equivalent to root(argument,3) logn(arg,base) Logarithm base-base of arg. ln() Natural logarithm of argument (base-E logarithm of argument where E is Euler's constant) lg() Logarithm base-10 of argument, equivalent to logn(argument,10). lb() Logarithm base-2 of argument. exp() Exponential Function E to the power of argument, equivalent to E^argument sin() Sine of argument cos() Cosine tan() Tangent cot() Cotangent sec() Secant of argument, equiv. to 1/cos(arg). csc() Cosecant, equiv. to 1/sin(arg). asin() Arc sine acos() Arc cosine atan() Arc tangent acot() Arc cotangent asec() Arc secant, inverse secant. acsc() Arc cosecant, inverse cosecant. sinh() Hyperbolic sine, Sinus hyperbolicus cosh() Hyperbolic cosine, Cosinus hyperbolicus tanh() Hyperbolic tangent, Tangens hyperbolicus coth() Hyperbolic cotangent, Cotangens hyperbolicus sech() Hyperbolic secant, Secans hyperbolicus. csch() Hyperbolic cosecant, Cosecans hyperbolicus. asinh() Area sine, Area sinus hyperbolicus, inverse sinh(). acosh() Area cosine, Area cosinus hyperbolicus, inverse cosh(). atanh() Area tangent, Area tangens hyperbolicus, inverse tanh(). acoth() Area cotangent, Area cotangens hyperbolicus, inverse cotanh(). asech() Area- secant, Area secans hyperbolicus, inverse sech(). acsch() Area- cosecant, Area cosecans hyperbolicus, inverse csch(). gaussd(x,mean,sigma) Gaussian distribution ("Bell Curve"). gaussd(x,0,1), by the way, is the special case "Normal distribution density with mean-value=0, standard-deviation=1". min(arg1,arg2) Returns the lesser of the two arguments max(arg1,arg2) Returns the greater of the two arguments round() Rounds argument up or down to the closest integer floor() Rounds arg down. ceil() Rounds arg up. abs() or | | Absolute value of argument. Example: 2abs(sin[x]) or alternatively 2|sin(x)| . sgn() Sign Function.
rand 随机数在0和1之间。例如:pi*rand*sin(x) 或者甚至是Pirandsin(x)。 E 自然常数2.718281828459045... LN2 2的自然对数,为0.6931471805599453... LN10 10的自然对数,为2.302585092994046... LOG2E 以2为底的E的对数(E:见上文),为1.4426950408889633... LOG10E 以10为底的E的对数,为0.4342944819032518... PHI 黄金分割率1.61803398874989... PI 3.141592653589793... SQRT1_2 1/2的平方根,为0.7071067811865476... SQRT2 2的平方根,为1.4142135623730951... 参考资料

-1
   <script type="text/javascript">
    Math.prototype=Math;
    Math.prototype.rand=function(min,max){
        return Math.floor((Math.random() * (max-min+1))+min)
    }

    var o=[];
    for(var i=0;i<100;i++)o.push(Math.rand(-1,1));
    alert(o.join(", "))
    </script>

当然,你也可以这样做:

Math.rand=function(min,max){
    return Math.floor((Math.random() * (max-min+1))+min)
}

Math没有像Array和String一样带有自己的原型,这是因为它不是一个函数而是一个对象。由于你可以使用new String()和new Array(),所以你也能够使用String.prototypeArray.prototype

对于Object、Function、Number、Date、RegExp甚至Boolean都是一样的。然而,任何定义的函数都将被分配一个原型属性,并从Function和其他链中继承。

如果你想把Math当作一个函数来处理,你只需要用一个函数覆盖变量。这样,当调用Math.constructor时,它将不会返回Object,因为它实际上是链接到你创建的用户定义函数。

你可以先复制原生对象,然后将其丢到你重写函数的原型属性之一,或者使用封装,这样只有你的新Math函数才能访问原生方法。关于这个主题,不确定还能说什么。

开头的问题有点无意义。当调用Math.constructor时,将返回Object,并且与直接调用Object相同。唯一的区别是如果你改变了构造函数。

为什么你想要改变构造函数呢? Math 对象现在的状态已经很好了。如果我们期望它从某个地方继承一些东西,那么我们会期望 "this" 指向哪里呢?如果你能回答这个问题,你就可以编写有用的代码了。

为什么你想要改变构造函数是问题的关键?Resig几年前提到了一些东西,那个想法如何得到改进? - Paul Sweatte
2
@PaulSweatte 谁说你必须改变构造函数,除非你要改变原型?而且为什么要改变静态对象的原型呢?扩展它?既然它是静态的,你可以通过添加自己的方法/属性来简单地扩展它。为什么要让它变得复杂呢?顺便说一句,我不明白 Resig 的想法在这里哪里体现出来了 - 这与“窃取内置功能”有关吗? - Parth Thakkar
这个想法利用了 Math 中的内置功能来增强 Array,那么问题是有没有更好的方法来实现呢? - Paul Sweatte
2
Math.prototype = Math 没有意义。 Math 不是一个构造函数,因此给它一个原型属性不会产生任何效果。你只是覆盖了 Mathrand 属性,并添加了一个没有任何作用的自引用 prototype 属性。 - Dagg Nabbit
3
这个答案有很多问题,我几乎不知道从哪里开始。函数本身就是对象。Function 实例的 prototype 属性仅在对象构造时隐式使用;它并不能证明一个对象具有非空的原型链,也不能证明其缺少;例如 Math 对象显然有一个,这就是它能够继承 constructor 属性的原因。此外还有其他问题。答案说这个问题 "有点毫无意义",但是它完全没有理解问题的关键点。 - PointedEars

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接