關於Object.defineProperty 的基礎知識

Object.defineProperty 這個方法你們耳熟能詳,能夠對 對象的屬性進行添加或修改的操做。便可以進行  數據劫持 。vue就是經過這個方法來劫持數據的。javascript

平時咱們建立對象的時候,通常經過對象字面量的方式建立:html

var student = { name:"小明", age:10 }
對象的屬性在建立的時候,都帶有一些特徵值(特性),JS經過這些特徵值來定義它們的行爲。 
 
ECMA-262 第 5 版在定義只有內部才用的特性(attribute)時,描述了屬性(property)的各類特徵。
ECMA-262 定義這些特性是爲了實現 JavaScript 引擎用的,所以在 JavaScript 中不能直接訪問它們。
爲了表示特性是內部值,該規範把它們放在了兩對兒方括號中,例如[[Enumerable]]。
 
ECMAScript 中有兩種屬性:數據屬性和訪問器屬性。
 
                    -------------《JavaScript高級程序設計(第三版)》 第六章
屬性描述符
 
MDN上,對於對象的屬性在建立的時候,帶有的特徵值,叫的是屬性描述符。包含數據描述符存取描述符
 
a)數據屬性(數據描述符):
  1. [[Configurable]] :可否經過 delete 刪除屬性,可否修改屬性的特性,或者可否把屬性修改成訪問器屬性。
  2. [[Enumerable]]:可否經過 for ·· in 或者 Object.key() 枚舉。
  3. [[Writable]]:屬性的值可否被修改。
  4. [[Value]]:屬性的值,能夠是任何有效的 JavaScript 值(數值,對象,函數等)
b)訪問器屬性(存取描述符): 
  1. [[Configurable]] :可否經過 delete 刪除屬性,可否修改屬性的特性,或者可否把屬性修改成訪問器屬性。
  2. [[Enumerable]]:可否經過 for ·· in 或者 Object.key() 枚舉。
  3. [[Get]]:在讀取屬性時調用的函數。
  4. [[Set]]:在寫入屬性時調用的函數。
 
屬性描述符的默認值:有兩種狀況

 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 這個方法了。 

語法:Object.defineProperty(obj,prop,descriptor)
  1. obj,即須要修改屬性的對象。必填。
  2. prop,須要修改的屬性。必填。
  3. descriptor,屬性修飾符配置項,是個對象。屬性修飾符不填的狀況下,這個參數也不能少,最少也要是一個 { } 空對象。
  4. 最終返回處理後的 obj 對象
descriptor 也是分數據描述符存取描述符。功能也是同樣
a) 數據描述符:
  1.  configurable 
  2.  enumerable 
  3.  writable 
  4.  value

b) 存取描述符

  1.  configurable
  2.  enumerable 
  3.  get 
  4.  set

上面的這些屬性都是能夠直接訪問配置的。

數據描述符和存取描述符用法都很簡單。不過須要注意的是:

  1. 數據屬性符的writable或value 與 存取描述符的get或set不能同時存在 。會報錯。
  2. 存取描述符的get與set也能夠不一樣時存在,若是隻指定get表示屬性不能寫(意思進行賦值操做,最後屬性仍是爲undefined,即便最初屬性定義了初始值),只指定set表示屬性不能讀(意思是獲取屬性的時候是undefined,整個對象都爲{ }。即便最初定義了一些屬性的)。 
  3. 存取描述符的get與set是個函數,函數裏的 this 指向的是 須要修改屬性的對象即obj

還有個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的缺點:

  1. 沒法監控到數組下標的變化,致使直接經過數組的下標給數組設置值,不能實時響應。因此vue才設置了7個變異數組(push、pop、shift、unshift、splice、sort、reverse)的 hack 方法來解決問題。
  2. 只能劫持對象的屬性,所以咱們須要對每一個對象的每一個屬性進行遍歷。若是能直接劫持一個對象,就不須要遞歸 + 遍歷了。因此 vue3.0 會使用 Proxy 來替代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。 

相關文章
相關標籤/搜索