DS.Lab筆記

来源:互联网 发布:数据分析的对比分析法 编辑:程序博客网 时间:2024/05/21 00:46

属性的种类

作者在一句话里总结说有三种。但是在这句话后面的一个段落里只罗列了两个:一个是命名属性,在JS代码中可以访问,还有一种是内部属性,JS代码中不能访问到。第三种是他在第一段落中提到的,在ES3中的传统的属性,在这个版本的标准里,属性的值与其命名是直接关联的,其内部的实现并没有被详细解释,虽然有些实现在属性值背后添加了访问器的概念,setter和getter方法。但是在ES5里这种情况被明确描述了,命名属性明确地有访问器,作者称之为named accessor property。。


属性的特性(attribute)

[[Value]]

[[Writable]]对应ES3里的{ReadOnly}。

[[Enumerable]]对应ES3里的{DontEnum}。

[[Configurable]]对应ES3里的{DontDelete}。


命名数据属性

这类属性有四个特性,它们的默认值是:

var defaultDataPropertyAttributes = {  [[Value]]: undefined,  [[Writable]]: false,  [[Enumerable]]: false,  [[Configurable]]: false};


所以,也就是说你创建一个新属性的时候,你要是不提供这几个特性的值,那么这个属性就成了一个不可以修改,不能够枚举不能够再修改特性值的属性了,那也就等于一个常量:

// define a global constant Object.defineProperty(this, "MAX_SIZE", {  value: 100});console.log(MAX_SIZE); // 100 MAX_SIZE = 200; // error in strict mode, [[Writable]] = false,delete MAX_SIZE; // error in strict mode, [[Configurable]] = false console.log(MAX_SIZE); // still 100


在《Effective JavaScript》里,有几个知识点都是跟可枚举特性有关的,问题在于在原型链上任何一个代理点新创建的属性都会被for...in循环遍历。所以在ES5里,这个问题可以通过enumerable解决:

Object.defineProperty(Array.prototype, "sum", {   value: function arraySum() {    //  sum implementation  },   enumerable: false }); // now with using the same example this "sum"// is no longer enumerable for (var k in a) {  console.log(k); // 0, 1, 2}


这样一来,在ES3里面的一个单纯的赋值操作

// simple assignment (if we create a new property)foo.bar = 10;


在ES5里就成了

// the same asObject.defineProperty(foo, "bar", {  value: 10,  writable: true,  enumerable: true,  configurable: true});


注意:

  • defineProperty()不仅用来创建属性,也负责修改
  • defineProperty()返回被修改的对象本身
  • Object.keys()返回一个对象上的所有可枚举属性;
  • Object.getOwnPropertyNames()返回一个对象上的所有属性,包括可枚举和不可枚举的。


命名访问器属性

学术化的定义是:命名访问器属性将两个访问器与一个属性名关联起来,分别是设定器和取值器,两者用来间接地修改和读取该属性的值。


其实感觉很接近C#里的property。


这类属性多了两个特性:[[Get]]和[[Set]]。


以前ES3系列的文章里有讲到过,ES3里每个对象都有些内部属性,其中[[Get]]和[[Put]]是负责读写属性值的。在ES5里,它们不是一回事,而实际上却又有紧密的关系,对象上的内部属性[[Get]]会呼叫访问器属性的[[Get]]特性,[[Put]]会呼叫[[Set]]


10和20这段讲的意思在下面的示例代码里能更清楚地显现出来,如果在getter做一些手脚,那么将读不到这个属性自己的值。


这类属性的特性的默认值是:

var defaultAccessorPropertyAttributes = {  [[Get]]: undefined,  [[Set]]: undefined,  [[Enumerable]]: false,  [[Configurable]]: false};

如果没有定义set,属性就成为只读。


用defineProperty()来定义属性的语法是:

var foo = {}; Object.defineProperty(foo, "bar", {   get: function getBar() {    return 20;  },   set: function setBar(value) {    // setting implementation  } }); foo.bar = 10; // calls foo.bar.[[Set]](10) // independently always 20console.log(foo.bar); // calls foo.bar.[[Get]]()

或者用声明式也可以:

var foo = {   get bar () {    return 20;  },   set bar (value) {    console.log(value);  } }; foo.bar = 100;console.log(foo.bar); // 20


第一个坑:修改一个[[Configurable]]为false的对象

好,下面作者指出一个貌似是bug的东西,倒不能算bug,但或许是个坑。在前面讲过,一旦一个属性的[[Configurable]]是false,以后就不能再修改它的任何特性了,只能修改它的值而已,所以再次给getter赋值会导致一个异常,这个很合情合理:

// configurable false by defaultvar foo = Object.defineProperty({}, "bar", {  get: function () {    return "bar";  }}); // trying to reconfigure the "bar"// property => exception is throwntry {  Object.defineProperty(foo, "bar", {    get: function () {      return "baz"    }  });} catch (e) {  if (e instanceof TypeError) {    console.log(foo.bar); // still "bar"  }}

但是,如果修改时的新值与当前值相同,这个异常不会被抛出。

function getBar() {  return "bar";} var foo = Object.defineProperty({}, "bar", {  get: getBar}); // no exception even if configurable is false,// but practically such "re"-configuration is uselessObject.defineProperty(foo, "bar", {  get: getBar});


第二个坑:[[Configurable]]为false的前提下,[[Writable]]可以由true到false,但不可由false到true

看下下面的代码就明白了:

var foo = Object.defineProperty({}, "bar", {  value: "bar",  writable: true,  configurable: false}); Object.defineProperty(foo, "bar", {  value: "baz"}); console.log(foo.bar); // "baz" // change writableObject.defineProperty(foo, "bar", {  value: "qux",  writable: false // changed from true to false, OK}); console.log(foo.bar); // "qux" // try to change writable again - back to trueObject.defineProperty(foo, "bar", {  value: "qux",  writable: true // ERROR});


第三个坑:[[Configurable]]为true的前提下,数据属性和访问器属性之间可以相互转换

如果[[Configurable]]为false当然就不可能了,作者得出结论说可见[[Writable]]有多么不重要。

// writable false by defaultvar foo = Object.defineProperty({}, "bar", {  value: "bar",  configurable: true}); Object.defineProperty(foo, "bar", {  get: function () {    return "baz";  }}); console.log(foo.bar); // OK, "baz"


第四个坑:










原创粉丝点击