原来写文章都是一次写两三个小时写完,偶尔看到一我的的博客了解到还有草稿箱这个功能,因此之后写文章的时候就舒服多了哈哈,能够存起来再发,不须要一口气写完了javascript
最近一直在看JavaScript高级程序设计,看到defineProperty的时候感觉挺深的,由于大名鼎鼎的Vue的双向数据绑定的原理就是根据这个东西来的,因此看到这里的时候长了见识vue
要提及Object.defineProperty的话,须要先来介绍一下JavaScript中的对象。java
面向对象的语言有一个标志,那就是他们都有类的概念,而经过类能够建立任意多个具备相同属性和方法的对象。ECMAScript中没有类的概念,所以它的对象也与基于类的语言中的对象有所不一样。数组
JavaScript中的对象定义: 无序属性的集合,其属性能够包含基本值、对象或者函数。严格来说,这就至关于说对象是一组没有特定顺序的值。对象的每一个属性或方法都有一个名字,而每一个名字都映射到一个值。正由于这样,能够把对象想象成散列表,就是一个键值对,其中的值能够是任意的(数据,函数,对象,数组~~)函数
前面balabala一堆概念,真心感受若是能够理解挺重要的 this
可使用构造函数,建立一个Object的实例,而后为其添加属性和方法设计
var person = new Object(); person.name = 'zhang'; person.age = 123; person.job = "SoftWare Engineer"; person.sayName = function () { alert(this.name); }; person.sayName();
这个例子建立了一个名为person的对象,并为其添加了三个属性(name、age、和job)和一个方法(sayName),其中sayName()方法用于显示this.name 这个会被解析为person.name和值。对象
不过如今建立这种对象大都是经过字面量的方式来建立了blog
var person = { name: 'zhang', age: 18, job: 'SoftWare Engineer', sayName: function () { alert(this.name); } }; person.sayName();
这种写法的结果和上面的那一种是同样的,这种写法若是你要拓展功能的话就不能再赋值了,也是用到了上面的写法ip
person.home = 'hebei'; person.showHome = function () { alert(this.home); };
这个样子去拓展它。
ES5定义只有内部采用的特性时,描述了属性的各类特征。定义这些特性时为了实现JavaScript引擎用的,所以在JavaScript中不能直接访问它们。为了表示特征是内部值,该规范把它们放在了两对儿方括号中,例如[[Enumerable]]
前面例子中的name,age,job,home,showHome都是属性,引号后面的东西是值,说一下属性的一些东西。
ECMAScript中有两种属性: 数据属性和访问器属性
数据属性包含一个数据值的位置。在这个位置能够读取和写入值。数据属性有4个描述其行为的特性。
[[Configurable]] 表示可否经过delete删除属性从而从新定义属性,可以经过属性的特性,或者可否把属性修改成访问器属性,像前面的例子中那样直接在对象上定义的属性,它们的这个特性默认值为true。
[[Enumerable]] 表示可否经过for-in循环返回属性。像前面例子中那样直接在对象上定义的属性,他们的这个特性默认值为true
[[Writable]] 表示可以修改属性的值。像前面例子中那样直接在对象上定义的属性,他们的这个特性默认值为true
[[Value]] 包含这个属性的数据值。读取属性值的时候,从这个位置读;写入属性值的时候,把新值保存在这个位置。这个特性的默认值为undefined
例子
var person = { name: 'zhang' }; for(var key in person){ console.log(key); } // 他这个属性的value值就是他在上面所定义的那一些,对这个值的任何修改都反映在这个位置 console.log(person.name); person.name = 'goudan'; // 在这里也能够修改,由于[[Writable]]属性默认为true console.log(person.name); // [[Configurable]] 这个东西为true 表示也能够删除它 delete person.name; console.log(person.name); // undefined 由于删除以后这个属性就不存在了 [[Value]]特性的默认值为undefined
重点来了
若是要修改 属性默认的特性 就必要要使用ES5的Object。defineProperty()方法。这个方法接收三个参数。
属性所在的对象、 属性的名字、 一个描述符对象 (其中描述符对象的属性必须是: configurable、enumerable、writeable、和value)设置其中一个或多个值,能够修改对应的特性值
下面去演示这些例子了,但愿你也能够试着去尝试一遍,我举得例子写法可能比较啰嗦,但愿没有尝试过的能够试着练习一下
提示:我后面 // 注释后面的东西 是输出打印的东西,若有错误能够本身具体状况分析一下
var person = { name: 'zhang', age: 12 }; Object.defineProperty(person,"name",{ writable: false }); // 读取person.name console.log(person.name); // zhang console.log(person.age); //12 // 修改person.name person.name = 'wang'; person.age = 14; console.log(person.name); // zhang ?????? 咋没变 console.log(person.age); // 14 // 缘由: 那个配置里面也说了的[[Writable]]默认是true,可是改成false以后 就是这个属性不可写,没有写进去的也能够看到是能够正常修改的
var person = { name: 'zhang', baba: '1', job: 'Software Engineer' }; for(var key in person){ console.log(key); // name, baba, job } // 三个属性均可以正常的打印出来
// 配置一下 enumerable Object.defineProperty(person,'baba',{ enumerable: false }); for(var key1 in person){ console.log(key1); // name job } // ??? baba去哪了? 我告诉你去哪了 由于配置了 enumerable: false console.log(person.baba); // 1 不会影响输出
var person = { name: 'zhang', age: 12 }; Object.defineProperty(person,'name',{ configurable: false }); console.log(person.name); // zhang console.log(person.age); // 12 delete person.name; console.log(person.name); // zhang // 刚才删除不是undefined吗 configurable: false就不容许删除了 // 若是声明是严格模式的话 那一句 delete person.name 还会报错 person.name = 'wang' console.log(person.name); // wang
注意:这个配置有一点须要注意一下
var person = { name: 'zhang' }; Object.defineProperty(person,'name',{ configurable: false }); // 若是之后你有需求你还想删除他的话 你会想到 Object.defineProperty(person,'name',{ configurable: true }); // 控制台打印错误消息: Uncaught TypeError: Cannot redefine property: name at Function.defineProperty (<anonymous>) // 这个东西一旦被配置为 false,之后在也不能够把他变为true了 会报错
var person = { name: 'zhang' }; console.log(person.name); // zhang Object.defineProperty(person,'name',{ value: 'wang' }); console.log(person.name); // wang // 你小子怎么改姓了 person.name = 'zhang'; // 给我改回来 console.log(person.name); //zhang 又是我老张家的孩子了,这个是能够修改回来的
固然了上面的属性只是单独演示的, 这四个属性能够结合起来使用,好比我要定义一个永远都没法改变它的值
var person = { name: 'zhang' }; console.log(person.name); // zhang Object.defineProperty(person,'name',{ writable: false, value: 'wang' }); console.log(person.name); // wang // 卧槽你个小兔崽子又改姓了 给我改回来 person.name = 'zhang'; console.log(person.name); // wang // 你会发现 有些事情发生了就是发生了,永远也回不来了 person.name = 'zhang1'; console.log(person.name); // wang
注意 在调用 Object.defineProperty()方法时,若是不指定,configurable、enumerable和writable特性的默认值都是true。
你给我演示这一堆东西到底有啥意思,能干啥。我想告诉你多数状况下可能都没有必要利用Object.defineProperty()方法提供的这些高级功能。不过,理解这些概念对理解JavaScript对象却很是有用
访问器属性不包含数据值;他们包含一对儿getter和setter函数(不过这两个函数都不是必须的)在读取访问器属性时,会调用getter函数,这个函数负责返回有效的值;在写入访问器属性时,会调用setter函数并传入新值,这个函数负责决定如何处理数据。访问器属性有以下4个特性
[[Configurable]] : 可否修改属性的特性,或者可否把属性修改成数据属性。像前面的例子中那样直接在对象上定义的属性,它们的这个特性默认值为true
[[Enumerable]] : 可否经过for-in循环 *********和上面的同样
[[Get]]: 在读取属性的时候调用。 默认值 undefined
[[Set]]: 在写入属性的时候调用 默认值 undefined
前面两个属性大同小异,几乎是同样的,一样的访问器属性不能直接定义,必须使用Object.defineProperty()来定义.
这个重要
var book = { _year: 2018, edition: 1 }; Object.defineProperty(book,'year',{ get: function () { console.log(`获取的时候触发函数`); // 每次获取的时候都会触发这个函数 console.log(this === book); // true 这里面的this就是book的引用 也就是book对象 return this._year; }, set: function (newValue) { console.log('修改值的时候触发函数'); // newValue ----- 这里的newValue就是你设置的修改后的值 if(newValue> 2018){ this._year = newValue; this.edition = newValue - 2018; } } }); console.log(book.year); // 2018 他的值也就是咱们返回的值 book._year // 若是get函数不写返回值就是undefined book.year = 2020; // 修改值的时候触发函数 console.log(book); // {_year: 2019, edition: 2};
vue双向数据绑定就是这个,当咱们在修改数据的时候,会触发set那个函数,就在set函数中执行咱们的逻辑,去修改数据,之后我会写一个大概的双向数据绑定的那个代码,会发出来,网上也有好多相似的代码。
上面的代码建立了一个book对象,并给他定义两个默认的属性: _year和edition。
而访问器属性year则包含一个getter函数和一个setter函数。 getter函数返回_year的值,setter函数修改了_year和edition。这是使用访问器属性的常见方式,即设置一个属性的值会致使其余属性发生变化。
不必定非要同时制定getter和setter。只指定getter意味着属性是不能写,尝试写入属性会被忽略。在严格模式下,尝试写入至指定了getter函数的属性会抛出错误。相似的,只指定setter函数的属性也不能读,不然在非严格模式下回返回undefined,而在严格模式下会抛出错误。
同时定义多个属性的写法因为未对象定义多个属性的可能性很大,ES5又定义了一个Object.defineProperties()方法利用这个方法能够经过描述符一次定义多个属性。
这个方法接收两个对象参数:第一个对象是哟啊添加和修改其属性的对象,第二个对象的属性与第一个对象中要添加或修改的属性一一对应。
同时定义多个属性的写法
var book = {}; Object.defineProperties(book,{ _year: { value: 2018 }, edition: { value: 1 }, year: { get: function () { return this._year; }, set: function (newValue) { console.log('修改函数触发触发了吗'); if(newValue>2018){ this._year = newValue; this.edition += newValue - 2018; } } } }); console.log(book.year); book.year = 2020; console.log(book); //{_year: 2018, edition: 1} // ???? 我前面不是触发了修改的函数了吗为何没有改呢 上一个分开写的都修改为功了
别着急,下面来看一个新的API
读取属性的特性
使用Es5的Object.getOwnPropertyDescription() 方法,能够取得给定属性描述符。这个方法接收两个参数: 属性所在的对象和要读取其描述符的属性名称。返回值是一个对象,若是是访问器属性,这个对象的属性有 configurable enumerable writable 和value
var descriptor1 = Object.getOwnPropertyDescriptor(book,"_year"); var descriptor2 = Object.getOwnPropertyDescriptor(book,"edition"); console.log(descriptor1); /* {configurable: false, enumerable: false, value: 2018, writable: false} */ console.log(descriptor2); /* { configurable: false, enumerable: false, value: 1, writable: false} */ */ // 前面分开写能够成功的缘由能够用这个东西去获取一下,能够看到他们是true的,这个是false,因此 // 虽然你修改了,可是他是不生效的
在JavaScript中,能够针对任何对象------包括DOM和BOM对象,使用Object.getOwnPropertyDescriptor()方法。
若是你阅读了本文章有了一些收获,我会感到很是开心,因为能力有限,文章有的部分解释的不到位,但愿在之后的日子里能慢慢提升本身能力,若是不足之处,还望指正。