不會Object.defineProperty你就out了

 
Object.defineProperty ,顧名思義, 爲對象定義屬性。在js中咱們能夠經過下面這幾種方法定義屬性
// (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很麻煩,那爲啥存在這樣的方法呢?vue

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


what is Object.defineProperty

The Object.defineProperty() method defines a new property directly on an object, or modifies an exisiting property on an object, and returns the object.github

從上面得知,咱們能夠經過Object.defineProperty這個方法,直接在一個對象上定義一個新的屬性,或者是修改已存在的屬性。最終這個方法會返回該對象。web

語法

Object.defineProperty(object, propertyname, descriptor)express

參數

  • object 必需。 要在其上添加或修改屬性的對象。 這多是一個本機 JavaScript對象(即用戶定義的對象或內置對象)或 DOM 對象。propertyname 必需。 一個包含屬性名稱的字符串。descriptor 必需。 屬性描述符。 它能夠針對數據屬性或訪問器屬性。
屬性的狀態設置

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

  • 【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。並不衝突,以下代碼所示:cookie

//調用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)session

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


實際運用

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

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如下是使用不到的哈。
相關文章
相關標籤/搜索