【问题标题】:Why to avoid creating objects of primitives in JavaScript?为什么要避免在 JavaScript 中创建原始对象?
【发布时间】:2017-05-18 09:36:53
【问题描述】:

我正在关注 W3Schools 上的 JavaScript 教程。几乎在每一页上阅读时,他们都会提醒用户“避免创建对象”并改用原始数据类型。他们给出了理由,因为“如果使用对象,代码变得难以理解或执行速度将降低”。我们真的应该避免在 JavaScript 中创建对象吗?

例如:

var value = new Number(1);  // Avoid this
var value = 1;              // do something like this instead.

【问题讨论】:

  • w3schools.com 是出了名的穷。想要了解更多信息,请查看其他地方,例如 MDN。也就是说,创建基元的对象版本几乎没有必要或不合适。
  • 好吧,在您提供的示例中,使用构造函数会有点难以阅读。但是随着您的代码变得越来越复杂,不使用对象变得有点不可能。另外,我发现 w3schools 的信息不是很好。我建议查看 MDN (developer.mozilla.org/en-US) 以获得更深入的文档。虽然如果您只是使用 w3schools 作为起点,这可能是可以理解的。
  • @Jone Dotosvky 在这里阅读w3fools.com
  • 其中一个原因可能是代码优化和可读性
  • @FlyingGambit 我敢打赌,您现在甚至没有阅读该网站上的内容。

标签: javascript


【解决方案1】:

“避免创建对象”这句话本身在 JavaScript 中是荒谬的,JavaScript 到处都有对象,是现存最面向对象的语言之一。但是“避免创建原语的对象版本”,这是您引用的代码所做的,是有效的。也就是说,避免使用new Stringnew Numbernew Boolean

JavaScript 有字符串、数字和布尔值的原始版本和对象版本。几乎没有任何理由明确地创建它们中的任何一个的对象版本,这样做确实会导致混乱;见内联 cmets:

var s1, s2, n1, n2;

// These are false because with ===, an object is never equal to a non-object
s1 = new String("hi");
s2 = "hi";
console.log(s1 === s2); // false
n1 = new Number(42);
n2 = 42;
console.log(n1 === n2); // also false

// These are false because even with ==, two *different* objects are never equal
// (even if they're equivalent)
s1 = new String("what the...");
s2 = new String("what the...");
console.log(s1 == s2);  // also false
n1 = new Number(42);
n2 = new Number(42);
console.log(n1 == n2);  // also false

字符串、数字和布尔值的对象版本主要是为了使基元上的方法能够使用为对象类型提供方法的相同机制提供。当你这样做时

console.log("foo".toUpperCase()); // "FOO"

primitive 字符串"foo" 创建一个临时对象,然后从该对象中读取toUpperCase 属性。由于该对象继承自String.prototype,因此它具有toUpperCase,一切正常。操作完成后,临时对象将被丢弃(除非有东西保留了对它的引用,但toUpperCase 没有任何作用,也没有任何作用,您必须向String.prototype 添加一个按顺序返回对象的方法以便保留)。

【讨论】:

  • 很好的答案!另一个相关的事情是this 在非严格模式与严格模式下将原语传递给函数上下文时的行为。像这样的东西:function coerce() { return this; }; typeof coerce.call(5) === "object"; /* non-strict mode */ typeof coerce.call(5) === "number"; /* strict mode */
  • 我只是说它是相关的,因为它是一种保持对在非严格模式下创建的临时对象的引用的方式。例如,您可以将函数分配给原型来执行此操作:Number.prototype.coerce = coerce; 5..coerce();
  • 鉴于var q=foo; var r=q.toUpperCase();,与识别模式并将原始字符串提供给大小写转换函数相比,体面的实现是否会打扰创建对象?
  • @supercat:我不知道 if String.prototype.toUpperCase 仍然是引擎在启动时赋予它的原始值。事实上,如果他们不这样做,我会相当惊讶(但是,有时我会相当惊讶)。
【解决方案2】:

它改变了运算符处理数字、字符串和布尔值的直观方式:

  • 当构造任何数字时,严格比较 (===) 会中断,因此 42 === 42 为真,而42 === new Number(42) 不是,
  • 当两个数字都是对象时,抽象比较 (==) 会中断,因此 42 == new Number(42) 为真,而 new Number(42) == new Number(42) 不是,
  • typeof 运算符在构造数字时给出不同的结果,所以typeof(42)number,但typeof(new Number(42))object
  • 当转换为布尔值时,0 为 false,但 new Number(0) 为 true,因此以下两个会有不同的行为:

var a = 0;
if (a)
  console.log("not zero");
else
  console.log("zero!");     // "zero!"

var b = new Number(0);
if (b)
  console.log("not zero");     // "not zero"
else
  console.log("zero!");

所以,避免new Numbernew Stringnew Boolean

除此之外,还有在构造函数中使用/不使用new 的问题。它源于几个事实:

  • 在JS中,构造函数是一个常规函数,使用this.foo语法添加新的属性和方法;
  • 如果在没有 new 关键字的情况下调用,this 将成为全局对象,从而导致副作用。

因此,一个微小的错误可能会产生灾难性的影响:

color = "blue";

var Fruit = function(color) {
  this.color = color;
  return this;
};

var apple = new Fruit("green");

console.log(apple.color);       // "green"  --  okay

console.log(color);             // "blue"  --  okay

var banana = Fruit("yellow");

console.log(banana.color);      // "yellow"  --  okay

console.log(color);             // "yellow"  --  wait, what?

console.log(banana.apple);      // "{ color: 'green' }"  --  what??

console.log(banana.document);   // "{ location: [Getter/Setter] }"  --  what???

(这就是为什么有些人会在构造函数中添加显式检查,或者改用闭包。但那是另一回事了。)

【讨论】:

  • 通常,您不会在构造函数中使用return this;。这是多余的,省略它可以帮助开发人员使用您的构造函数更快地调试,因为banana 将等于undefined 而不是window。要考虑的另一件事是,您的示例假定非严格模式而不是严格模式,其中函数将抛出错误 Cannot set property 'color' of undefined,因为由于缺少 new 而没有定义上下文。
  • JS 中的 new 运算符会插入一个空对象 => {},这就是比较失败的原因。 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
  • "在没有new 关键字的情况下调用,this 成为全局对象,导致副作用。" 仅在松散模式下,不应该使用.在严格模式下(从 ES5 开始可用),当在Fruit 中调用Fruit() 时,this 将是undefined,所以this.color 将是一个错误。另外,从 ES2015 开始,如果你使用 class 语法,Foo 将是一个类构造函数,并且调用没有 new 的那些有它自己的特定错误。
【解决方案3】:

在调用 Number() 时不要使用 new 来源:JSLint...

当 JSLint、JSHint 或 ESLint 遇到前面带有 new 运算符的对 String、Number、Boolean、Math 或 JSON 的调用时,将引发“不使用 {a} 作为构造函数”错误。 (来源:LintErrors.com: Do not use {a} as a constructor

console.log(Number("3") == Number("3"))

console.log(new Number("3") == new Number("3"))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-23
    • 2018-11-13
    • 2011-11-15
    相关资源
    最近更新 更多