为什么Object.defineProperty可以使ES6符号属性可枚举?

时间:2015-06-24 14:39:24

标签: javascript ecmascript-6

在ES6中,属性可以定义为符号属性:

var symbol = Symbol();
var object = {};
object[symbol] = 'value';

MDN将可枚举属性定义为可以通过for..in循环迭代的属性。 (1)。符号属性永远不会被for ... in循环迭代,因此它们可以被认为是不可枚举的(2)。

那么,你能做到这一点是否有意义:

Object.defineProperty(object, symbol, {
    value: 'value',
    enumerable: true
});

并且查询对象的描述符确实确认了这个属性是可枚举的:

Object.getOwnPropertyDescriptor(object, symbol)
// -> { enumerable: true }

为什么呢?这有什么用?

(1)https://developer.mozilla.org/en-US/docs/Web/JavaScript/Enumerability_and_ownership_of_properties

(2)for ... in use [[Enumerate]],仅包含字符串键。由于我们有符号属性,因此可能应该更改MDN上的定义。

2 个答案:

答案 0 :(得分:17)

是的,允许Symbol属性可以枚举的原因是:Object.assign

let s1 = Symbol();
let s2 = Symbol();
let s3 = Symbol();
let original = {};
original[s1] = "value1";                // Enumerable
Object.defineProperty(original, s2, {   // Enumerable
  enumerable: true,
  value: "value2"
});
Object.defineProperty(original, s3, {   // Non-enumerable
  value: "value3"
});
let copy = {};
Object.assign(copy, original);
console.log("copy[s1] is " + copy[s1]); // value1, because it was enumerable
console.log("copy[s2] is " + copy[s2]); // value2, because it was enumerable
console.log("copy[s3] is " + copy[s3]); // undefined, because it wasn't enumerable
关于Babel的REPL的

Live Copy

为了清楚起见:

  

MDN将可枚举属性定义为可以通过for..in循环迭代的属性。 (1)。

ES6(ES2015)的错误。在ES5及更早版本中,这是一个合理的,如果简单化的定义,由于Symbol s,它不再是简单的正确定义。我修好了这篇文章。

这是一个CW,因为这是对这个问题的评论的产物。

答案 1 :(得分:5)

这是因为枚举规则包括需要字符串键的子句。请记住,枚举和要求键是不同的操作,具有完全不同的规则。

查看the section for for ... in/for ... of head evaluation (13.7.5.12),它表明迭代是使用:

完成的
  
      
  1. 如果iterationKind是枚举,那么

         

    ℃。返回obj.[[Enumerate]]()

  2.   

The description of [[Enumerate]] (9.1.11)非常清楚地说明:

  

返回一个Iterator对象(25.1.1.2),其next方法迭代O的可枚举属性的所有字符串值键。

对可枚举属性的检查稍后会出现在正文中,伪代码示例会使这一点更加清晰:

function* enumerate(obj) {
  let visited=new Set;
  for (let key of Reflect.ownKeys(obj)) {
      if (typeof key === "string") { // type check happens first
          let desc = Reflect.getOwnPropertyDescriptor(obj,key);
          if (desc) {
              visited.add(key);
              if (desc.enumerable) yield key; // enumerable check later
          }
      }
  }
  ...
}

(评论我的)

显然,不会枚举具有非字符串键的属性。使用这个例子:

var symbol = Symbol();
var object = {};

Object.defineProperty(object, symbol, {
    value: 'value',
    enumerable: true
});

Object.defineProperty(object, 'foo', {
  value: 'bar',
  enumerable: true
});

Object.defineProperty(object, 'bar', {
  value: 'baz',
  enumerable: false
});

Object.defineProperty(object, () => {}, {
  value: 'bin',
  enumerable: true
});

for (let f in object) {
  console.log(f, '=', object[f]);
}

for (let k of Object.getOwnPropertyNames(object)) {
  console.log(k);
}

您可以在Babel和Traceur中验证。

但是,您会看到两件有趣的事情:

  1. getOwnPropertyNames包含不可枚举的属性。这是有道理的,如it follows completely different rules
  2. for...in包含两个转发器下的非字符串属性。这似乎与规范不符,但确实符合ES5的行为。