【问题标题】:What are the benefits of making properties non-enumerable?使属性不可枚举有什么好处?
【发布时间】:2013-02-05 17:32:06
【问题描述】:

可枚举性是属性的三个属性之一:可写性、可枚举性和可配置性。我的问题是:

  • 在 JavaScript 中使属性不可枚举有什么好处?我知道我们通过使属性不可枚举来隐藏属性,但是属性隐藏有什么好处?
  • 我们可以访问不可枚举的属性吗?如果是,那么使它们不可枚举有什么好处?
  • 对象的所有预定义属性是否都设置为不可枚举?比如Array的poppush属性不可枚举?

【问题讨论】:

标签: javascript prototype enumerable


【解决方案1】:

我认为主要的好处是能够控制枚举对象属性时显示的内容,例如for inObject.keys()

MDN 用Object.defineProperty 解释得很好:https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty

所以通常情况下,当人们想向Object 添加一个方法时,例如旧浏览器不支持的某些方法的polyfill,他们会修改.prototype。但这会使属性变得可枚举,并弄乱了循环/键集合中返回的内容(不使用.hasOwnProperty...不是每个人都使用)。

所以不要像这样:

Object.prototype.myMethod = function () {
    alert("Ahh");
};

您可以使用Object.defineProperty 明确表示它不可枚举:

Object.defineProperty(Object.prototype, 'myMethod', {
    value: function () {
        alert("Ahh");
    },
    enumerable: false
});

这样,例如当您使用for (var key in obj) 时,“myMethod”将不会是枚举项,您不必担心使用.hasOwnProperty。主要问题是某些浏览器当然不支持它:http://kangax.github.com/es5-compat-table/ 并且并非所有库/代码都使用它,因此您不能总是依赖外部库/代码来正确使用.

您可以随时访问不可枚举的属性,它只是在枚举对象的属性时不会显示 - 这是重点。

而且我确实相信对象的所有“预定义”属性都是不可枚举的。我真的只指本机属性,不一定是继承或创建的。因此,在您的示例中,poppush 被枚举,但 Array.prototype.indexOf 将在不支持该方法的旧浏览器上创建为 polyfill。 ..这当然可以通过使用Object.defineProperty 来避免,就像我上面的例子一样。另一个例子是length属性,没有枚举过。

这是一个一般性的例子:http://jsfiddle.net/aHJ3g/

Object.keys 的使用和定义很重要:“返回给定对象自己的可枚举属性的数组,其顺序与 for-in 循环提供的顺序相同(区别在于 for-in 循环枚举原型链中的属性)。” - 来自 MDN - https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/keys

【讨论】:

    【解决方案2】:

    我认为的另一个主要好处是,它可以防止对象的私有属性污染公共命名空间。

    假设您创建并发布了一个名为Cosmos 的强大库。用户启动 Node 解释器并通过调用构造函数创建它的新实例:

    var Cosmos = require('Cosmos');
    var cosmos = new Cosmos('my empire');
    

    现在用户只需键入cosmos 并按回车键即可查看它支持的公共 API。您希望用户看到两者中的哪一个?

    { name: 'my empire',
      grow: [Function: grow],
      addStar: [Function: addStar],
      beautify: [Function: beautify],
      implode: [Function: implode],
      destroy: [Function: destroy] }
    

    { _age: 25000,
      _size: 35000,
      _destroyed: false,
      name: 'my empire',
      _numStars: 200,
      _init: [Function: _init],
      grow: [Function: grow],
      _grow: [Function: _grow],
      addStar: [Function: addStar],
      _checkStatus: [Function: _checkStatus],
      beautify: [Function: beautify],
      implode: [Function: implode],
      destroy: [Function: destroy] }
    

    【讨论】:

    • 不可枚举的属性仍然是公开的并且仍然污染命名空间。如果你想要私有属性,你需要一个闭包。
    • 我对答案和批评意见都投了赞成票。在我看来,评论的批评是有效的,所以回答者对“公共”和“私人”的使用可能不是最佳的。但是,我认为答案的重点仍然有帮助(至少对我来说),因为它显示了可枚举性如何帮助图书馆用户更容易地了解图书馆作者打算让图书馆用户轻松使用的内容(例如,请使用 @ 987654326@ 而不是 _grow,即使两者在技术上仍然是公开的)。
    【解决方案3】:

    不可枚举的属性没有真正的实际用途

    这是一个非常好的问题,我正要问自己。经过一番调查,我的结论是:你肯定不需要这个功能。

    背景

    对于那些不知道什么是不可枚举属性的人,请查看以下链接:

    您实际上可以枚举它们

    这很奇怪,但枚举所说的“不可枚举”属性实际上非常简单:

    // start with some enumerable properties
    const foo = {
      a: 1,
      b: "yes",
      c: function () {}
    }
    
    // then add a couple of non-enumerable ones
    Object.defineProperty(foo, "d", { value: "hiding here", isEnumerable: false });
    Object.defineProperty(foo, "e", { value: 42, isEnumerable: false });
    
    const enumerableProperties = Object.keys(foo).join(", ");
    console.info("Enumerables: " + enumerableProperties);
    // Enumerables: a, b, c
    
    const ownPropertyNames = Object.getOwnPropertyNames(foo).join(", ");
    console.info("Enumerating also the non-enumerables: " + ownPropertyNames);
    // Enumerating also the non-enumerables: a, b, c, d, e

    当他们说您不能枚举它们时,他们专门指的是Object.keys()for..in 循环以及它们只返回可枚举属性的事实。 getOwnPropertyNames() 不是这种情况。

    不要使用它

    好的,现在我们在同一页面上:在我看来,它就像一种晦涩难懂的语言功能,只会让我的代码更难阅读和理解。我实在想不出它的真正合法用途。

    现有的两个答案都谈到了两个非常具体的案例:

      1234563 Object.keys()。如果这是您的情况,您将不得不支持非常旧的代码(即不符合当今最佳实践的遗留代码),我为您感到抱歉(尽管我知道今天仍有很多系统仍在维护不幸的是,属于这种情况);
    1. 当您开发公共库并希望保持面向公众的对象干净时,它很有用。这是非常具体的,对吧?而且我也不想仅仅为此而用几个definePropertys 污染我的图书馆的代码。此外,这个答案很快就会过时,因为我们现在有private fields。最后,答案还表明它也会保持公共命名空间的清洁,但事实并非如此;命名空间被对象本身而不是其属性所污染。

    因此,除非您不幸不得不维护旧代码,否则请忘记使用它。你不需要它,相信我。如果您发现自己在枚举对象时需要隐藏某些属性,那么您肯定是在建模错误。只需将这些属性放在单独的对象中即可。它们并不意味着与您拥有的其他财产一起生活。这将使您的代码更简洁、更易于理解,这是您在编写代码时应该努力实现的最重要的事情。

    Here's a more detailed article 证实了不可枚举属性在当今已不起重要作用的观点。

    【讨论】:

      【解决方案4】:
      • 通过使属性不可枚举,您仍然可以访问它。但是,当您在对象上应用 for in 循环时,将不会迭代不可枚举的属性。
      • 见第一点
      • 继承的属性是可枚举的(只要它们被标记为可枚举的)

        var x = {a:1, b:2} // a and b are enumerable properties by default
        x.propertyIsEnumerable("toString") // returns false, because it is not marked as enumerable
        var y = Object.create(x);
        y.c = 3;
        for(p in y) console.log(p); // this loop will print c, a and b but not toString
        

      【讨论】:

      • 我认为第三点是错误的。继承的属性当然和其他任何属性一样可枚举...var x = {a: 1}; var y = Object.create(x); y.b = 2; for (name in y) {console.log(name, y[name]);} 产生 b 2a 1
      • 我修正了我的答案。请原谅我的错误!
      猜你喜欢
      • 1970-01-01
      • 2017-12-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多