Object.defineProperty

 

對象是由多個名/值對組成的無序的集合。對象中每一個屬性對應任意類型的值。
定義對象可使用構造函數或字面量的形式:javascript

var obj = new Object; //obj = {} obj.name = "張三"; //添加描述 obj.say = function(){}; //添加行爲

除了以上添加屬性的方式,還可使用Object.defineProperty定義新屬性或修改原有的屬性。vue

語法:java

Object.defineProperty(obj, prop, descriptor)

參數說明:git

obj:必需。目標對象 
prop:必需。需定義或修改的屬性的名字
descriptor:必需。目標屬性所擁有的特性github

返回值:web

傳入函數的對象。即第一個參數objtypescript

針對屬性,咱們能夠給這個屬性設置一些特性,好比是否只讀不能夠寫;是否能夠被for..inObject.keys()遍歷。express

給對象的屬性添加特性描述,目前提供兩種形式:數據描述和存取器描述。json

Object.defineProperty ,顧名思義,爲對象定義屬性。在js中咱們能夠經過下面這幾種方法定義屬性cookie

// (1) define someOne property name someOne.name = 'cover'; //or use (2) someOne['name'] = 'cover'; // or use (3) defineProperty Object.defineProperty(someOne, 'name', { value : 'cover' }) 

從上面看,貌似使用Object.defineProperty很麻煩,那爲啥存在這樣的方法呢?

帶着疑問,咱們來看下 Object.defineProperty的定義。


 

其中descriptor的參數值得咱們關注下,該屬性可設置的值有:

  • 【value】 屬性的值,默認爲 undefined。
  • 【writable】 該屬性是否可寫,若是設置成 false,則任何對該屬性改寫的操做都無效(但不會報錯),對於像前面例子中直接在對象上定義的屬性,這個屬性該特性默認值爲爲 true。
var someOne = { }; Object.defineProperty(someOne, "name", { value:"coverguo" , //因爲設定了writable屬性爲false 致使這個量不能夠修改 writable: false }); console.log(someOne.name); // 輸出 coverguo someOne.name = "linkzhu"; console.log(someOne.name); // 輸出coverguo 
  • 【configurable]】若是爲false,則任未嘗試刪除目標屬性或修改屬性如下特性(writable, configurable, enumerable)的行爲將被無效化,對於像前面例子中直接在對象上定義的屬性,這個屬性該特性默認值爲爲 true。 。
var someOne = { }; Object.defineProperty(someOne, "name", { value:"coverguo" , configurable: false }); delete someOne.name; console.log(someOne.name);// 輸出 coverguo someOne.name = "linkzhu"; console.log(someOne.name); // 輸出coverguo 
  • 【enumerable】 是否能在for-in循環中遍歷出來或在Object.keys中列舉出來。對於像前面例子中直接在對象上定義的屬性,這個屬性該特性默認值爲爲 true。

注意 在調用Object.defineProperty()方法時,若是不指定, configurable, enumerable, writable特性的默認值都是false,這跟以前所 說的對於像前面例子中直接在對象上定義的屬性,這個特性默認值爲爲 true。並不衝突,以下代碼所示:

//調用Object.defineProperty()方法時,若是不指定 var someOne = { }; someOne.name = 'coverguo'; console.log(Object.getOwnPropertyDescriptor(someOne, 'name')); //輸出 Object {value: "coverguo", writable: true, enumerable: true, configurable: true} //直接在對象上定義的屬性,這個特性默認值爲爲 true var otherOne = {}; Object.defineProperty(otherOne, "name", { value:"coverguo" }); console.log(Object.getOwnPropertyDescriptor(otherOne, 'name')); //輸出 Object {value: "coverguo", writable: false, enumerable: false, configurable: false} 
  • 【get】一旦目標對象訪問該屬性,就會調用這個方法,並返回結果。默認爲 undefined。
  • 【set】 一旦目標對象設置該屬性,就會調用這個方法。默認爲 undefined。

從上面,能夠得知,咱們能夠經過使用Object.defineProperty,來定義和控制一些特殊的屬性,如屬性是否可讀,屬性是否可枚舉,甚至修改屬性的修改器(setter)和獲取器(getter)

那什麼場景和地方適合使用到特殊的屬性呢?


實際運用

在一些框架,如vue、express、qjs等,常常會看到對Object.defineProperty的使用。那這些框架是如何使用呢?

MVVM中數據‘雙向綁定’實現

如vue,qjs等大部分mvvm框架(angular用的是髒處理)都是經過Object.defineProperty來實現數據綁定的 爲了更詳細的說明,我將在下一篇文章跟你們講解下。下面篇幅先不展開。(別扔磚。。。)

優化對象獲取和修改屬性方式

這個優化對象獲取和修改屬性方式,是什麼意思呢? 過去咱們在設置dom節點transform時是這樣的。

//加入有一個目標節點, 咱們想設置其位移時是這樣的 var targetDom = document.getElementById('target'); var transformText = 'translateX(' + 10 + 'px)'; targetDom.style.webkitTransform = transformText; targetDom.style.transform = transformText; 

經過上面,能夠看到若是頁面是須要許多動畫時,咱們這樣編寫transform屬性是十分蛋疼的。(┬_┬)

但若是經過Object.defineProperty, 咱們則能夠

//這裏只是簡單設置下translateX的屬性,其餘如scale等屬性可本身去嘗試 Object.defineProperty(dom, 'translateX', { set: function(value) { var transformText = 'translateX(' + value + 'px)'; dom.style.webkitTransform = transformText; dom.style.transform = transformText; } //這樣再後面調用的時候, 十分簡單 dom.translateX = 10; dom.translateX = -10; //甚至能夠拓展設置如scale, originX, translateZ,等各個屬性,達到下面的效果 dom.scale = 1.5; //放大1.5倍 dom.originX = 5; //設置中心點X } 

上面只是個簡單的版本,並非最合理的寫法,但主要是爲了說明具體的意圖和方法

有興趣瞭解更多能夠看下面這個庫:https://github.com/AlloyTeam/AlloyTouch/blob/master/transform.js

增長屬性獲取和修改時的信息

如在Express4.0中,該版本去除了一些舊版本的中間件,爲了讓用戶可以更好地發現,其有下面這段代碼,經過修改get屬性方法,讓用戶調用廢棄屬性時拋錯並帶上自定義的錯誤信息。

[ 'json', 'urlencoded', 'bodyParser', 'compress', 'cookieSession', 'session', 'logger', 'cookieParser', 'favicon', 'responseTime', 'errorHandler', 'timeout', 'methodOverride', 'vhost', 'csrf', 'directory', 'limit', 'multipart', 'staticCache', ].forEach(function (name) { Object.defineProperty(exports, name, { get: function () { throw new Error('Most middleware (like ' + name + ') is no longer bundled with Express and must be installed separately. Please see https://github.com/senchalabs/connect#middleware.'); }, configurable: true }); }); 

其餘如設置常量等用途。


兼容

最後注意下,Object.defineProperty是ES5的屬性,大部分場景使用是沒問題的, 但在一些場景如IE8如下是使用不到的哈。

在ie8下只能在DOM對象上使用,嘗試在原生的對象使用 Object.defineProperty()會報錯

相關文章
相關標籤/搜索