簡體   English   中英

如何使用 javascript Object.defineProperty

[英]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 代碼可能會變大或者一些第三方代碼可能依賴於舊版本),所以這里的樣板是不那么邪惡的。 但是,它仍然是邪惡的。 這就是為什么將屬性引入許多語言的原因。 您可以保留原始代碼,只需將折扣成員轉換為帶有getset塊的屬性:

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並在同一屬性上設置):

  • 訪問器描述符= get + set(見上面的例子)
    • get必須是一個函數; 它的返回值用於讀取屬性; 如果未指定,則默認值為undefined ,其行為類似於返回 undefined 的函數
    • set必須是一個函數; 在給屬性賦值時,它的參數用 RHS 填充; 如果未指定,則默認值為undefined ,其行為類似於空函數
  • 數據描述符= 值 + 可寫(見下面的例子)
    • 默認未定義 如果writableconfigureenumerable (見下文)為真,則該屬性的行為就像一個普通的數據字段
    • 可寫- 默認為false 如果不是true ,則該屬性是只讀的; 嘗試寫入被忽略,沒有錯誤*!

兩個描述符都可以具有以下成員:

  • 可配置- 默認false 如果不為真,則無法刪除該屬性; 嘗試刪除被忽略,沒有錯誤*!
  • 可枚舉- 默認false ; 如果為真,它將在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.preventExtensions(yourObject)防止將新屬性添加到yourObject 使用Object.isExtensible(<yourObject>)檢查對象是否使用了該方法。 預防很(閱讀下文)。
  • Object.seal(yourObject)同上,屬性不能被刪除(有效地將configurable: false為所有屬性)。 使用Object.isSealed(<yourObject>)檢測對象上的此功能。 密封很(閱讀下文)。
  • Object.freeze(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)

  1. 上,我們要定義一個新的屬性的對象
  2. 我們要定義的新屬性名稱
  3. 描述符對象

描述符對象是有趣的部分。 在這里,我們可以定義以下內容:

  1. 可配置<boolean> :如果為true則可能會更改屬性描述符並且可能會從對象中刪除該屬性。 如果configurable 為false ,則無法更改Object.defineProperty中傳遞的描述符屬性。
  2. Writable <boolean> :如果為true ,則可以使用賦值運算符覆蓋該屬性。
  3. Enumerable <boolean> :如果為true ,則可以在for...in循環中迭代該屬性。 此外,當使用Object.keys函數時,鍵將出現。 如果屬性為false它們將不會使用for..in循環進行迭代,並且在使用Object.keys時不會顯示。
  4. get <function> :在需要屬性時調用的函數。 調用此函數而不是給出直接值,並將返回值作為屬性值給出
  5. set <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
   });

在此處輸入圖片說明

關於定義屬性的簡單解釋。

示例代碼: https : //jsfiddle.net/manoj_antony32/pu5n61fs/

 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM