【问题标题】:Usefulness of primitive constructors in JS [closed]JS中原始构造函数的用处[关闭]
【发布时间】:2021-11-11 15:10:30
【问题描述】:

注意:我不是在寻找意见。原始构造函数的单个、客观有用的示例将满足该问题。


有什么好的理由使用new Number(n)?为什么会有这个构造函数?

我可以想到几个避免它的原因,例如,

  • 文字更节省内存,因为它们不构造任何新内容。
  • 文字彼此相等(5 === 5new Number(5) !== new Number(5))。
  • typeof 运算符表示它是一个数字/str/etc 而不是一个对象。
  • new Number(0) 绝不是虚假的。 (!!new Number(false) == true)。
  • 你可以用它来进行类型转换,但是还有更简洁的方法。 (+false === 0)。

明确地说,我了解 Number 对象(或 String 或 Object 或 Array 对象)具有有用的方法和属性,我的问题专门针对 构造函数

我也知道在使用这些原始类型的方法时会隐式调用构造函数(例如123.45.toFixed(n)),但这并不能解释为什么构造函数会暴露给浏览器,或者如何(或 if) 构造函数本身很有用。

我以数字为例,但我不明白为什么 Javascript 会公开其他可用文字的构造函数(字符串、对象、数组等)。

为什么存在这些,有什么好的理由使用它们吗?

【问题讨论】:

  • 一方面,可能是运行时间:stackoverflow.com/questions/4859800/… 但我承认我想不出一个好的推理 wrt 数字
  • 我们不能在这里真正回答有关语言设计原理的问题,但对于这样的事情,通常只是“为什么不公开它?”它只是超出了类型层次结构的一般设计,将这些视为特殊情况会更有效。'
  • @LiorPollak - 我认为这个问题更多是关于用户定义的构造函数而不是内置的原始构造函数:/
  • 老实说,其中一些可能归结为我们不再使用的旧旧东西。 JS 中所有从一开始就仓促起来的旧东西就是为什么像 JavaScript: The Good Parts 这样的书很重要。例如,JS 还有一个with 声明,我从未 使用过,MDN 建议反对。 FWIW,我从事 JS 开发已有 10 年了,在那段时间里使用原始构造函数的次数可能不到 5 次。
  • "您可以将其用于类型转换" - 不,您不能为此使用构造函数。您可能希望将Number 作为函数调用(不带new)。

标签: javascript constructor literals


【解决方案1】:

这些构造函数围绕原始类型创建一个“包装对象”,因此返回的对象与原始类型本身根本不同。

var example = new String('abc')

将返回一个包含值“abs”的包装对象,要访问它,您需要调用函数 .valueOf()

example.valueOf() // returns abc 

原语也可以访问包装器的原型,但它们仍然有着根本的不同。基元只是从其包装器中“借用”其原型上的方法。

原语包装器存在的原因只是历史原因,因为这种行为存在于 Java 中,Javascript 继承了对象和原语之间的差异。

我能想到使用它的唯一原因是在原语上添加一个属性,如下所示:

如果你这样做:

var example = "Foo" ; 
example.bar = "Baz"; // The property "bar" only exist on this line and does not persist; 

使用包装器:

var example = new String("foo") ; 
example.bar = "baz" ; // Now you can access the property "baz"
example.charAt(1); //You still can access the methods on the string prototype  

这是非常尴尬的语法,但它有效。

感谢您提出的好问题,我期待看到所有答案和不同的讨论,以尝试了解 Javascript 中一些非常奇怪的行为背后的存在。

【讨论】:

    【解决方案2】:

    最后,这样的问题被“因为它在规范中”回答了,但还是有一些直观的逻辑来拥有Number构造函数:

    正如您在问题中已经提到的,有一个(有用的)数字原型对象,因此您可以调用诸如.toFixed 等方法。每个具有非空原型的 JavaScript 对象都有一个构造函数。构造器和原型齐头并进;除非代码覆盖所涉及的属性,否则每个具有非空原型的对象都存在这种关系:

    instance.constructor.prototype === Object.getPrototypeOf(instance)
    

    因为这就像一般规则,所以不要偏离(对象包装的)数字的规则,并且对于 constructor 属性具有有意义的值,即 ... 构造函数。而通过“constructor”的definition,可以用new调用。

    有用吗?

    在这里,我们真的进入了意见的灰色地带。

    只有一个观察:Number 实例可以具有自定义属性,但即使为此目的,您也可以依赖自动包装:

    let n = Object.assign(2, { isPrime: true });
    console.log(n instanceof Number); // true
    console.log(n.isPrime); // true
    console.log(n*2); // 4

    【讨论】:

      【解决方案3】:

      使用 new Number(n) 有什么好的理由吗?

      我能想到一个原因,但今天可能不成立。这是一个延伸,但我相信它可以用于性能原因,具体取决于实现。

      规范说明了您会执行以下操作的情况:

      1.2.toString()
      

      步骤 4.a 中可能创建的对象在上述抽象操作和普通对象 [[Get]] 内部方法之外是不可访问的。实现可能会选择避免实际创建对象。

      所以从技术上讲,如果他们先发制人地创建数字对象并在那里调用方法,那么做类似事情的人可能会受益一点。那是如果实现没有避免对象包装器。

      但是,鉴于今天的当前实施,您可能会得到相反的结果并获得更差的性能。

      无论如何,这是一个很大的延伸。

      为什么会有这个构造函数?

      嗯,我能想到几个原因,主要是因为一致性。首先,StringNumberBoolean 每个都兼作构造函数和常规函数。在没有new 的情况下调用时,它们每个都可以将输入转换为各自的类型。那时实际上很少有不是构造函数的函数(例如evalFunction.prototype)。您还必须考虑将这些对象原型放在哪里,并且一个非常明显的位置应该放在这些函数上,就像其他内置函数一样。

      总而言之,您可能不希望这些函数不是构造函数,因为您不希望将记住所有这些边缘情况的不必要的负担加给您的用户和那些将要实现这些情况的人。今天我们会有这样的问题:“为什么Number 是一个函数而不是构造函数,即使它的Number 属性中有Number 原型?”

      我以数字为例,但我不明白为什么 Javascript 会公开其他可用文字的构造函数(字符串、对象、数组等)。

      嗯,这些都可以通过class 扩展。例如,扩展 Array 在 ES6 之前就已被寻求。例如

      class myArrayClass extends Array {
          myCustomMethod(){
      
          }
      }
      

      您还可以以某种安全的方式扩展StringNumber

      【讨论】:

        猜你喜欢
        • 2013-08-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-02-13
        • 1970-01-01
        • 1970-01-01
        • 2010-12-08
        • 2018-05-06
        相关资源
        最近更新 更多