學習Object.defineProperty

Object.defineProperty ,顧名思義,為對象定義屬性。在js中我們可以通過下面這幾種方法定義屬性

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

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

帶著疑問,我們來看下 Object.defineProperty的定義。

1. 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.

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

1.1 語法

Object.defineProperty(object, propertyname, descriptor)

1.2 參數(shù)

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

1.3 屬性的狀態(tài)設置

1.【value】 屬性的值,默認為 undefined。
2.【writable】 該屬性是否可寫,如果設置成 false,則任何對該屬性改寫的操作都無效(但不會報錯),對于像前面例子中直接在對象上定義的屬性,這個屬性該特性默認值為為 true。

var geekjc = { };
Object.defineProperty(geekjc, "name", {
    value:"geekjc" , //由于設定了writable屬性為false 導致這個量不可以修改
    writable: false 
});  
console.log(geekjc.name); // 輸出 geekjc
someOne.name = "hhhaaa";
console.log(geekjc.name); // 輸出geekjc

3.【configurable]】如果為false,則任何嘗試刪除目標屬性或修改屬性以下特性(writable, configurable, enumerable)的行為將被無效化,對于像前面例子中直接在對象上定義的屬性,這個屬性該特性默認值為為 true。 。

var geekjc = { };
Object.defineProperty(geekjc, "name", {
    value:"geekjc" ,
    configurable: false 
});  
delete geekjc.name; 
console.log(geekjc.name);// 輸出 geekjc
someOne.name = "hhhaaa";
console.log(geekjc.name); // 輸出geekjc

4.【enumerable】 是否能在for-in循環(huán)中遍歷出來或在Object.keys中列舉出來。對于像前面例子中直接在對象上定義的屬性,這個屬性該特性默認值為為 true。
注意 在調用Object.defineProperty()方法時,如果不指定, configurable, enumerable, writable特性的默認值都是false,這跟之前所 說的對于像前面例子中直接在對象上定義的屬性,這個特性默認值為為 true。并不沖突,如下代碼所示:

//調用Object.defineProperty()方法時,如果不指定
var geekjc = { };
geekjc.name = 'geekjc';
console.log(Object.getOwnPropertyDescriptor(geekjc, 'name'));
//輸出 Object {value: "geekjc", writable: true, enumerable: true, configurable: true}

//直接在對象上定義的屬性,這個特性默認值為為 true
var otherOne = {};
Object.defineProperty(otherOne, "name", {
    value:"geekjc" 
});  
console.log(Object.getOwnPropertyDescriptor(otherOne, 'name'));
//輸出 Object {value: "geekjc", writable: false, enumerable: false, configurable: false}

5.【get】一旦目標對象訪問該屬性,就會調用這個方法,并返回結果。默認為 undefined。
6.【set】 一旦目標對象設置該屬性,就會調用這個方法。默認為 undefined。
從上面,可以得知,我們可以通過使用Object.defineProperty,來定義和控制一些特殊的屬性,如屬性是否可讀,屬性是否可枚舉,甚至修改屬性的修改器(setter)和獲取器(getter)

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

2. 實際運用

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

2.1 MVVM中數(shù)據(jù)‘雙向綁定’實現(xiàn)

如vue,qjs等大部分mvvm框架(angular用的是臟處理)都是通過Object.defineProperty來實現(xiàn)數(shù)據(jù)綁定的

2.2 優(yōu)化對象獲取和修改屬性方式

這個優(yōu)化對象獲取和修改屬性方式,是什么意思呢? 過去我們在設置dom節(jié)點transform時是這樣的。

//加入有一個目標節(jié)點, 我們想設置其位移時是這樣的
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
}

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

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

如在Express4.0中,該版本去除了一些舊版本的中間件,為了讓用戶能夠更好地發(fā)現(xiàn),其有下面這段代碼,通過修改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
  });
});

其他如設置常量等用途。。。

3. 兼容

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

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 來自:參 考 原 文 對象是由多個名/值對組成的無序的集...
    wyude閱讀 1,345評論 1 7
  • 概述 JavaScript提供了一個內部數(shù)據(jù)結構,用來描述一個對象的屬性的行為,控制它的行為。這被稱為“屬性描述對...
    許先生__閱讀 557評論 0 1
  • Object.defineProperty() 方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現(xiàn)有屬性,...
    采姑娘的大白菜閱讀 613評論 0 0
  • 概述 JavaScript提供了一個內部數(shù)據(jù)結構,用來描述一個對象的屬性的行為,控制它的行為。這被稱為“屬性描述對...
    zjh111閱讀 780評論 0 0
  • windows 下開啟openssl 擴展 1: 首先檢查php.ini中;extension=php_opens...
    geeooooz閱讀 1,549評論 0 0

友情鏈接更多精彩內容