Object.defineProperty 這個方法你們耳熟能詳,能夠對 對象的屬性進行添加或修改的操做。便可以進行 數據劫持 。vue就是經過這個方法來劫持數據的。javascript
平時咱們建立對象的時候,通常經過對象字面量的方式建立:html
var student = { name:"小明", age:10 }
對象的屬性在建立的時候,都帶有一些特徵值(特性),JS經過這些特徵值來定義它們的行爲。ECMA-262 第 5 版在定義只有內部才用的特性(attribute)時,描述了屬性(property)的各類特徵。ECMA-262 定義這些特性是爲了實現 JavaScript 引擎用的,所以在 JavaScript 中不能直接訪問它們。爲了表示特性是內部值,該規範把它們放在了兩對兒方括號中,例如[[Enumerable]]。ECMAScript 中有兩種屬性:數據屬性和訪問器屬性。-------------《JavaScript高級程序設計(第三版)》 第六章
1) 當使用對象字面量或者構造函數的形式建立屬性的時候,enumerable 、configurable 、 writable都爲 true ,value、get、set都爲undefined 。因此平時定義對象的時候,咱們能夠隨意增刪改查。vue
2) 當使用Object.defineProperty、Object.defineProperties 或 Object.create 函數的狀況下添加的屬性。enumerable 、configurable、writable都爲 false;value、get、set都爲undefined。java
能夠經過Object.getOwnPropertyDescriptor(對象名,屬性名)來獲取屬性描述符的默認值。es6
Object.defineProperty :數組
Object.create :瀏覽器
怎麼修改默認屬性默認值?函數
這種兩個方括號 [[ ]] 的方式,我感受就和指向對象的原型的指針相似,ECMA-262 第 5 版 稱這個指針爲 [[prototype]] ,也是沒有標準的方式訪問,可是主流瀏覽器都提供了__proto__屬性來訪問。this
這上面的屬性描述符都有本身的默認值,可是若是我想修改某些數據描述符的默認值呢?它並不能直接訪問啊,好比 obj.age.[[Enumerable]] 這樣是不行的。既然不能直接訪問,那麼我怎麼去修改對象中某些屬性的指定特性呢?spa
之前可使用非標準的方式: 對象.__defineGetter__( "屬性", function(){} ) 或者 對象.__defineSetter__( "屬性", function(){} ) 。不過這方法已經被廢棄了,雖然有些瀏覽器還支持,可是不建議使用。
這時候就須要用到 Object.defineProperty 這個方法了。
b) 存取描述符
上面的這些屬性都是能夠直接訪問配置的。
數據描述符和存取描述符用法都很簡單。不過須要注意的是:
還有個Object.defineProperties() 能夠劫持多個屬性。有興趣的能夠去 MDN 看看
若是對象的屬性中還有對象,那麼這時候須要深層遍歷,通常的方法是:
var obj = { name:"zjj", sex:'male', money:100, info:{ face:'smart' } } observe(obj) console.log(obj)
obj.sex = 'female' obj.info.face = 20; obj.info.hobit = 'girl';
console.log(obj) function observe(target){ if (!target || typeof target !== 'object') return; Object.keys(target).forEach(function(val){ defineProp(target,target[val],val) }) } function defineProp(curObj,curVal,curKey){ observe(curVal) //再次遍歷子屬性 Object.defineProperty(curObj,curKey,{ enumerable:true, configurable:true, get:function(){ console.log('獲取了屬性',curVal) return curVal }, set:function(newData){ console.log('設置了屬性',newData) curObj = newData; } }) }
這樣,目標對象中的屬性的值爲對象的時候也能進行數據劫持了。不過我疑惑的點是:添加不存在的屬性時,爲何調用的是get方法???後面搞懂了再來解決這個問題
Object.defineProperty的缺點:
Proxy:代理
據說vue3.0 會用 proxy 替代 Object.defineProperty()方法。因此預先了解一些用法是有必要的。
proxy 可以直接 劫持整個對象,而不是對象的屬性,而且劫持的方法有多種。並且最後會返回劫持後的新對象。因此相對來說,這個方法仍是挺好用的。不過兼容性不太好。
關於proxy的介紹與用法,能夠看看 阮一峯老師的 這篇文章
題外話:ECMAScript 與 JavaScript 的關係
參考:這裏
Netscape 公司最初建立了一個用於瀏覽器的腳本語言,後與Sun 公司(建立了Java)聯合發佈了該腳本語言,命名爲Javascript;後來微軟也出了一個 JScript,用於IE3.0瀏覽器;還有Cenvi的ScriptEase。因而Netscape 公司決定將 JavaScript 提交給國際標準化組織 ECMA,但願 JavaScript 可以成爲國際標準。
1997年7月,ECMA的TC93(39號技術委員會)發佈262號標準文件(ECMA-262)的初版,規定了瀏覽器腳本語言的標準。因爲商標和其它協議的緣由,只有Netscape 公司能使用Javascript 這個名稱,因此最後將這種語言稱爲 ECMAScript。而如今咱們所說的 JavaScript 是 ECMAScript + DOM + BOM的集合。DOM和BOM是W3C制定的規範。
如今說的ES5就是 ECMAScript 5.0版,而ES6就是 ECMAScript 6 後改名爲 ECMAScript 2015(簡稱ES2015),後面每年的6月份都會發佈一個新的版本,不過增長的內容並很少。ES7(ES2016)、ES8 (ES2017)、ES9 (ES2018),如今2019.7月了,這個時候都已經出了ES10(ES2019)。不過ES10仍是一個草案,並無多少瀏覽器支持。主流的都是ES5 和 ES6。