![](/img/trans.png)
[英]javascript 'use strict' and and Object.defineProperty setter
[英]how to use javascript Object.defineProperty
我四處尋找如何使用Object.defineProperty
方法,但找不到任何像樣的東西。
有人給了我這段代碼:
Object.defineProperty(player, "health", {
get: function () {
return 10 + ( player.level * 15 );
}
})
但我不明白。 主要是, get
是我無法得到的(雙關語)。 它是如何工作的?
既然你問了類似的問題,讓我們一步一步來。 它有點長,但它可能比我花在寫這個上的時間節省更多的時間:
屬性是一種 OOP 功能,旨在清晰分離客戶端代碼。 例如,在某些電子商店中,您可能有這樣的對象:
function Product(name,price) {
this.name = name;
this.price = price;
this.discount = 0;
}
var sneakers = new Product("Sneakers",20); // {name:"Sneakers",price:20,discount:0}
var tshirt = new Product("T-shirt",10); // {name:"T-shirt",price:10,discount:0}
然后在您的客戶代碼(電子商店)中,您可以為您的產品添加折扣:
function badProduct(obj) { obj.discount+= 20; ... }
function generalDiscount(obj) { obj.discount+= 10; ... }
function distributorDiscount(obj) { obj.discount+= 15; ... }
后來,網店老板可能會意識到折扣不能超過 80%。 現在您需要在客戶端代碼中找到每次出現的折扣修改並添加一行
if(obj.discount>80) obj.discount = 80;
那么網店老板可能會進一步改變他的策略,比如“如果客戶是經銷商,最大折扣可以達到90%” 。 而且您需要再次在多個地方進行更改,而且您需要記住在更改策略時隨時更改這些行。 這是一個糟糕的設計。 這就是為什么封裝是 OOP 的基本原則。 如果構造函數是這樣的:
function Product(name,price) {
var _name=name, _price=price, _discount=0;
this.getName = function() { return _name; }
this.setName = function(value) { _name = value; }
this.getPrice = function() { return _price; }
this.setPrice = function(value) { _price = value; }
this.getDiscount = function() { return _discount; }
this.setDiscount = function(value) { _discount = value; }
}
然后你可以改變getDiscount
( accessor ) 和setDiscount
( mutator ) 方法。 問題是大多數成員的行為就像公共變量,只是折扣在這里需要特別注意。 但是好的設計需要封裝每個數據成員以保持代碼的可擴展性。 所以你需要添加很多什么都不做的代碼。 這也是一個糟糕的設計,一個樣板反模式。 有時你不能只是在以后將字段重構為方法(eshop 代碼可能會變大或者一些第三方代碼可能依賴於舊版本),所以這里的樣板是不那么邪惡的。 但是,它仍然是邪惡的。 這就是為什么將屬性引入許多語言的原因。 您可以保留原始代碼,只需將折扣成員轉換為帶有get
和set
塊的屬性:
function Product(name,price) {
this.name = name;
this.price = price;
//this.discount = 0; // <- remove this line and refactor with the code below
var _discount; // private member
Object.defineProperty(this,"discount",{
get: function() { return _discount; },
set: function(value) { _discount = value; if(_discount>80) _discount = 80; }
});
}
// the client code
var sneakers = new Product("Sneakers",20);
sneakers.discount = 50; // 50, setter is called
sneakers.discount+= 20; // 70, setter is called
sneakers.discount+= 20; // 80, not 90!
alert(sneakers.discount); // getter is called
請注意最后一行:正確折扣值的責任從客戶代碼(電子商店定義)轉移到產品定義。 產品負責保持其數據成員的一致性。 好的設計是(粗略地說)代碼的工作方式與我們的想法相同。
這么多關於屬性。 但是 javascript 與純面向對象的語言(如 C#)不同,並且對功能進行了不同的編碼:
在 C# 中,將字段轉換為屬性是一項重大更改,因此如果您的代碼可能用於單獨編譯的客戶端,則應將公共字段編碼為自動實現的屬性。
在 Javascript 中,標准屬性(具有上述 getter 和 setter 的數據成員)由訪問器描述符(在您問題中的鏈接中)定義。 獨占地,您可以使用數據描述符(因此您不能使用 ie值並在同一屬性上設置):
兩個描述符都可以具有以下成員:
for(var i in theObject)
迭代; 如果為 false,它將不會被迭代,但它仍然可以作為 public 訪問* 除非在嚴格模式下- 在這種情況下,JS 會停止執行 TypeError,除非它在try-catch 塊中被捕獲
要讀取這些設置,請使用Object.getOwnPropertyDescriptor()
。
通過例子學習:
var o = {};
Object.defineProperty(o,"test",{
value: "a",
configurable: true
});
console.log(Object.getOwnPropertyDescriptor(o,"test")); // check the settings
for(var i in o) console.log(o[i]); // nothing, o.test is not enumerable
console.log(o.test); // "a"
o.test = "b"; // o.test is still "a", (is not writable, no error)
delete(o.test); // bye bye, o.test (was configurable)
o.test = "b"; // o.test is "b"
for(var i in o) console.log(o[i]); // "b", default fields are enumerable
如果您不希望允許客戶端代碼進行此類作弊,您可以通過三個級別的限制來限制對象:
Object.isExtensible(<yourObject>)
檢查對象是否使用了該方法。 預防很淺(閱讀下文)。configurable: false
為所有屬性)。 使用Object.isSealed(<yourObject>)
檢測對象上的此功能。 密封很淺(閱讀下文)。writable: false
為所有帶有數據描述符的屬性)。 Setter 的可寫屬性不受影響(因為它沒有)。 凍結是淺的:這意味着如果屬性是對象,它的屬性不會被凍結(如果你願意,你應該執行類似“深度凍結”的操作,類似於深度復制克隆)。 使用Object.isFrozen(<yourObject>)
來檢測它。如果你只寫幾行有趣的,你就不需要為此煩惱。 但是如果你想編寫一個游戲(正如你在鏈接問題中提到的),你應該關心好的設計。 嘗試在 google 上搜索有關反模式和代碼異味的內容。 它將幫助您避免諸如“哦,我需要再次完全重寫我的代碼!”之類的情況。 ,如果您想大量編碼,它可以為您節省數月的絕望。 祝你好運。
get
是一個在您嘗試讀取值player.health
時調用的函數,例如:
console.log(player.health);
它實際上與以下內容沒有太大不同:
player.getHealth = function(){
return 10 + this.level*15;
}
console.log(player.getHealth());
get 的反面是 set,它會在您分配給值時使用。 由於沒有設置器,似乎不打算分配給玩家的健康:
player.health = 5; // Doesn't do anything, since there is no set function defined
一個非常簡單的例子:
var player = { level: 5 }; Object.defineProperty(player, "health", { get: function() { return 10 + (player.level * 15); } }); console.log(player.health); // 85 player.level++; console.log(player.health); // 100 player.health = 5; // Does nothing console.log(player.health); // 100
defineProperty是 Object 上的一個方法,它允許您配置屬性以滿足某些條件。 這是一個簡單的示例,員工對象具有兩個屬性 firstName 和 lastName,並通過覆蓋對象上的toString方法來附加這兩個屬性。
var employee = {
firstName: "Jameel",
lastName: "Moideen"
};
employee.toString=function () {
return this.firstName + " " + this.lastName;
};
console.log(employee.toString());
您將獲得輸出為:Jameel Moideen
我將通過在對象上使用 defineProperty 來更改相同的代碼
var employee = {
firstName: "Jameel",
lastName: "Moideen"
};
Object.defineProperty(employee, 'toString', {
value: function () {
return this.firstName + " " + this.lastName;
},
writable: true,
enumerable: true,
configurable: true
});
console.log(employee.toString());
第一個參數是對象的名稱,然后第二個參數是我們要添加的屬性的名稱,在我們的例子中是 toString 然后最后一個參數是 json 對象,它的值將是一個函數和三個參數 writable,enumerable和可配置的。現在我只是宣布一切為真。
如果你運行這個例子,你會得到輸出: Jameel Moideen
讓我們理解為什么我們需要writable、enumerable 和 configure三個屬性。
可寫
javascript 中非常煩人的部分之一是,例如,如果您將 toString 屬性更改為其他內容
如果你再次運行它,一切都會中斷。 讓我們將可寫更改為 false。 如果再次運行,您將獲得正確的輸出 'Jameel Moideen' 。 此屬性將防止稍后覆蓋此屬性。
可枚舉的
如果您打印對象內的所有鍵,您可以看到包括 toString 在內的所有屬性。
console.log(Object.keys(employee));
如果將 enumerable 設置為 false ,則可以對其他人隱藏 toString 屬性。 如果再次運行,您將獲得 firstName,lastName
可配置
如果稍后有人重新定義了對象,例如可枚舉為 true 並運行它。 可以看到 toString 屬性又來了。
var employee = {
firstName: "Jameel",
lastName: "Moideen"
};
Object.defineProperty(employee, 'toString', {
value: function () {
return this.firstName + " " + this.lastName;
},
writable: false,
enumerable: false,
configurable: true
});
//change enumerable to false
Object.defineProperty(employee, 'toString', {
enumerable: true
});
employee.toString="changed";
console.log(Object.keys(employee));
您可以通過將可配置設置為 false 來限制此行為。
基本上, defineProperty
是一個接受 3 個參數的方法——一個對象、一個屬性和一個描述符。 在這個特定調用中發生的事情是player
對象的"health"
屬性被分配給玩家對象級別的 10 加 15 倍。
是的,不再為 setup setter & getter 擴展功能,這是我的示例Object.defineProperty(obj,name,func)
var obj = {};
['data', 'name'].forEach(function(name) {
Object.defineProperty(obj, name, {
get : function() {
return 'setter & getter';
}
});
});
console.log(obj.data);
console.log(obj.name);
Object.defineProperty() 是一個全局函數..它在聲明對象的函數內部不可用。你必須靜態使用它......
Object.defineProperty(player, "health", {
get: function () {
return 10 + ( player.level * 15 );
}
});
Object.defineProperty
用於在播放器對象上創建新屬性。 Object.defineProperty
是一個原Object.defineProperty
在於 JS 運行時Object.defineProperty
中的函數,它采用以下參數:
Object.defineProperty(obj, prop, descriptor)
描述符對象是有趣的部分。 在這里,我們可以定義以下內容:
<boolean>
:如果為true
則可能會更改屬性描述符並且可能會從對象中刪除該屬性。 如果configurable 為false
,則無法更改Object.defineProperty
中傳遞的描述符屬性。<boolean>
:如果為true
,則可以使用賦值運算符覆蓋該屬性。<boolean>
:如果為true
,則可以在for...in
循環中迭代該屬性。 此外,當使用Object.keys
函數時,鍵將出現。 如果屬性為false
它們將不會使用for..in
循環進行迭代,並且在使用Object.keys
時不會顯示。<function>
:在需要屬性時調用的函數。 調用此函數而不是給出直接值,並將返回值作為屬性值給出<function>
:每當分配屬性時調用的函數。 調用此函數而不是設置直接值,並使用返回值來設置屬性的值。 const player = { level: 10 }; Object.defineProperty(player, "health", { configurable: true, enumerable: false, get: function() { console.log('Inside the get function'); return 10 + (player.level * 15); } }); console.log(player.health); // the get function is called and the return value is returned as a value for (let prop in player) { console.log(prop); // only prop is logged here, health is not logged because is not an iterable property. // This is because we set the enumerable to false when defining the property }
import { CSSProperties } from 'react' import { BLACK, BLUE, GREY_DARK, WHITE } from '../colours' export const COLOR_ACCENT = BLUE export const COLOR_DEFAULT = BLACK export const FAMILY = "'Segoe UI', sans-serif" export const SIZE_LARGE = '26px' export const SIZE_MEDIUM = '20px' export const WEIGHT = 400 type Font = { color: string, size: string, accent: Font, default: Font, light: Font, neutral: Font, xsmall: Font, small: Font, medium: Font, large: Font, xlarge: Font, xxlarge: Font } & (() => CSSProperties) function font (this: Font): CSSProperties { const css = { color: this.color, fontFamily: FAMILY, fontSize: this.size, fontWeight: WEIGHT } delete this.color delete this.size return css } const dp = (type: 'color' | 'size', name: string, value: string) => { Object.defineProperty(font, name, { get () { this[type] = value return this }}) } dp('color', 'accent', COLOR_ACCENT) dp('color', 'default', COLOR_DEFAULT) dp('color', 'light', COLOR_LIGHT) dp('color', 'neutral', COLOR_NEUTRAL) dp('size', 'xsmall', SIZE_XSMALL) dp('size', 'small', SIZE_SMALL) dp('size', 'medium', SIZE_MEDIUM) export default font as Font
直接在對象上定義新屬性,或修改對象上的現有屬性,並返回該對象。
注意:您可以直接在 Object 構造函數上調用此方法,而不是在 Object 類型的實例上調用。
const object1 = {};
Object.defineProperty(object1, 'property1', {
value: 42,
writable: false, //If its false can't modify value using equal symbol
enumerable: false, // If its false can't able to get value in Object.keys and for in loop
configurable: false //if its false, can't able to modify value using defineproperty while writable in false
});
關於定義屬性的簡單解釋。
Object.defineProperty(Array.prototype, "last", { get: function() { if (this[this.length -1] == undefined) { return [] } else { return this[this.length -1] } } }); console.log([1,2,3,4].last) //returns 4
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.