摘要:理解的函數在進入今天的內容之前我們可以先考慮這么一個場景在你的項目中你有這么一個對象如下所示我們的要求就是你要給添加一個屬性當的或者發生變化的時候也要隨之變化而且當我們設置了的值的時候那么相應的它的和也隨之發生變化那么我們應該怎么做呢如果你
理解JavaScript的Object.defineProperty()函數
在進入今天的內容之前,我們可以先考慮這么一個場景,在你的項目中你有這么一個對象如下所示:
var dreamapple = { firstName: "dream", lastName: "apple" };
我們的要求就是你要給dreamapple添加一個fullName屬性,當dreamapple的firstName或者lastName發生變化的時候,fullName也要隨之變化;而且當我們設置了fullName的值的時候,那么相應的它的firstName和lastName也隨之發生變化; 那么我們應該怎么做呢?
如果你使用過Vue.js的話,那么你可以使用它的計算屬性來達到這個目的,大概的代碼應該是下面這個樣子:
// ... computed: { fullName: { // getter get: function () { return this.firstName + " " + this.lastName }, // setter set: function (newValue) { var names = newValue.split(" ") this.firstName = names[0] this.lastName = names[names.length - 1] } } } // ...
如果你使用過Angular 1.x的話,那么你可能會使用$watch來達到這個目的,大概的代碼應該是下面這個樣子:
// 在控制器中使用 var vm = this; $scope.$watch("vm.firstName", function() { vm.fullName = vm.firstName + " " + vm.lastName; }); $scope.$watch("vm.lastName", function() { vm.fullName = vm.firstName + " " + vm.lastName; }); $scope.$watch("vm.fullName", function() { var names = vm.fullName.trim().split(" "); if(2 === names.length) { vm.firstName = names[0]; vm.lastName = names[1]; } else { // TODO } });
那我們使用原生的JavaScript可不可以達到這個目的呢?當然可以了;那么我們需要怎么做呢?比較簡單的做法就是給這個對象的屬性fullName設置一個getter和一個setter,因為這是ES5的特性所以較低版本的瀏覽器不支持這種特性,但是基本所有的現代瀏覽器都已經支持.我們只需要寫出下面的代碼就可以了:
var dreamapple = { firstName: "dream", lastName: "apple", get fullName() { return this.firstName + " " + this.lastName; }, set fullName(fullName) { var names = fullName.trim().split(" "); if(2 === names.length) { this.firstName = names[0]; this.lastName = names[1]; } } }; dreamapple.firstName = "Dream"; dreamapple.lastName = "Apple"; console.log(dreamapple.fullName); // Dream Apple dreamapple.fullName = "Jams King"; console.log(dreamapple.firstName); // Jams console.log(dreamapple.lastName); // King
是不是很方便呢?我們通過給dreamapple這個對象設置了屬性fullName的getter和setter方法,就達到了我們想要的那種效果.
當然更好的一種方法就是使用Object.defineProperty()這個函數了,下面我們就來好好的探討一下這個函數.這個方法的作用就是直接在一個對象上定義一個新屬性,或者修改一個已經存在的屬性,并返回這個對象;我們先來看一下怎么使用這個方法:
Object.defineProperty(obj, prop, descriptor)
其中參數obj表示的是需要定義屬性的那個對象,參數prop表示需要被定義或者修改的屬性名,參數descriptor就是我們定義的那個屬性prop的描述;我們接下來主要講解這個descriptor.它是一個對象,它有許多的屬性,我們接下來來分析這些屬性都是干什么用的:
value 該屬性對應的值,可以是任何有效的JavaScript值(數值,對象,函數等),默認為undefined.我們可以看下面的一個小例子:
var dream = {}; Object.defineProperty(dream, "name", { value: "dreamapple" }); console.log(dream.name); // dreamapple dream.name = "apple"; // 修改name屬性 console.log(dream.name); // 并不是apple,依舊是dreamapple
從上面的代碼中我們可以看到,我們給dream定義了一個新的屬性name,然后我們打印出這個屬性就是我們預期的那樣,得到的是dreamapple;但是,當我們嘗試改變這個屬性的時候,卻發現這個屬性并沒有改變,還以第一次我們賦給它的值;這是為什么呢?原來,只有當我們這個屬性的writable修飾為true時,我們這個屬性才可以被修改.
writable 當且僅當僅當該屬性的writable為true時,該屬性才能被賦值運算符改變;它的默認值為false.我們來修改一下上面的代碼,讓屬性name可以被修改:
Object.defineProperty(dream, "name", { value: "dreamapple", writable: true }); console.log(dream.name); // dreamapple dream.name = "apple"; // 修改name屬性 console.log(dream.name); // apple
我們可以看到,當我們把writable修改為true時,我們就可以修改name屬性了.
enumerable 這個特性決定了我們定義的屬性是否是可枚舉的類型,默認是false;只有我們把它設置為true的時候這個屬性才可以使用for(prop in obj)和Object.keys()中枚舉出來.就像下面這樣:
Object.defineProperty(dream, "a", { value: 1, enumerable: false // 不可枚舉 }); Object.defineProperty(dream, "b", { value: 2, enumerable: true // 可枚舉 }); // 只會輸出 b for(prop in dream) { console.log(prop); } console.log(Object.keys(dream)); // ["b"] console.log(dream.propertyIsEnumerable("a")); // false console.log(dream.propertyIsEnumerable("b")); // true
所以當我們想給你個對象添加一個不可枚舉的屬性的時候,就應該把enumerable設置為false.
configurable 這個特性決定了對象的屬性是否可以被刪除,以及除writable特性外的其它特性是否可以被修改;并且writable特性值只可以是false我們可以寫一個代碼示例來演示一下這個特性:
Object.defineProperty(dream, "c", { value: 3, configurable: false }); //throws a TypeError Object.defineProperty(dream, "c", { configurable: true }); //throws a TypeError Object.defineProperty(dream, "c", { writable: true }); //won"t throws a TypeError Object.defineProperty(dream, "c", { writable: false }); delete dream.c; // 屬性不可以被刪除 console.log(dream.c); // 3
get 一個給屬性提供getter的方法,如果沒有getter則為undefined;該方法返回值被用作屬性值,默認為undefined.
set 一個給屬性提供setter的方法,如果沒有setter則為undefined;該方法將接受唯一參數,并將該參數的新值分配給該屬性,默認為undefined.知道了這些之后我們就可以使用更標準的一種方式去解決我們在文中開頭的問題了:
Object.defineProperty(dreamapple, "fullName", { enumerable: true, get: function () { return this.firstName + " " + this.lastName; }, set: function (fullName) { var names = fullName.trim().split(" "); if (2 === names.length) { this.firstName = names[0]; this.lastName = names[1]; } } });
還有一點需要注意的是,value和get,set是不可以共存的,就是說你定義了value后就不能夠再定義get,set特性了.
好啦,今天的文章就寫到這里了,相信大家對于Object.defineProperty(obj, prop, descriptor)這個方法應該掌握了;還有一點需要提及的是其實Vue.js的計算屬性也是在這個函數的基礎上進行的一些改進,詳情可以看這里計算屬性的奧秘.
如果你對這篇文章有什么意見或者建議可以在這里提出來issues
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/90886.html
摘要:深入理解中的屬性和特性中屬性和特性是完全不同的兩個概念,這里我將根據自己所學,來深入理解中的屬性和特性。其中第三個參數描述符對象是對象字面量的方法創建的,里面的屬性和屬性值實際上保存的是要修改的特性和特性值。 深入理解JavaScript中的屬性和特性 JavaScript中屬性和特性是完全不同的兩個概念,這里我將根據自己所學,來深入理解JavaScript中的屬性和特性。 主...
摘要:面向對象的程序設計理解對象前言最近在細讀高級程序設計,對于我而言,中文版,書中很多地方翻譯的差強人意,所以用自己所理解的,嘗試解讀下。總結如果英語水平足夠好的話,建議看英文原版書籍或者國外大師的博客。 JS面向對象的程序設計_理解對象 前言:最近在細讀Javascript高級程序設計,對于我而言,中文版,書中很多地方翻譯的差強人意,所以用自己所理解的,嘗試解讀下。 如有紕漏或錯誤,會非...
摘要:今天結合高編第六章開始回顧和深入學習面向對象部分包括對象原型原型鏈繼承等部分。二對象的屬性類型勾鑫宇,數據屬性訪問器屬性書上講到屬性類型時,只是簡單提了一下是為了表示對象的特性,描述了屬性的特征,并且在中不能直接訪問。 前言 JavaScript發明之始,從技術上來講就是一門面向對象的語言,但在ES6之前,JS的很多特性和傳統的面向對象語言有所不同,比如沒有類的概念(ES6有了clas...
摘要:返回值被傳遞給函數的對象。描述該方法允許精確添加或修改對象的屬性。描述符必須是兩種形式之一不能同時是兩者。可以是任何有效的值數值,對象,函數等。該方法返回值被用作屬性值。該方法將接受唯一參數,并將該參數的新值分配給該屬性。 Object.defineProperties() Object.defineProperty() 方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性...
摘要:上一篇面向對象版塊之理解對象下一篇面向對象版塊之定義多個對象屬性以及讀取屬性特性 這是 javascript 面向對象版塊的第二篇文章,主要講解的是對象的屬性,首先創建一個對象: var person = { name: Nicholas, age: 29, job: Software Engineer, sayName: function () { conso...
閱讀 3021·2021-11-12 10:36
閱讀 4726·2021-09-22 10:57
閱讀 1558·2021-09-22 10:53
閱讀 2636·2019-08-30 15:55
閱讀 3493·2019-08-29 17:00
閱讀 3352·2019-08-29 16:36
閱讀 2463·2019-08-29 13:46
閱讀 1348·2019-08-26 11:45