如何使用 javascript Object.defineProperty

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18524652/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 12:16:11  来源:igfitidea点击:

how to use javascript Object.defineProperty

javascriptobjectdefineproperty

提问by Math chiller

I looked around for how to use the Object.definePropertymethod, but couldn't find anything decent.

我四处寻找如何使用该Object.defineProperty方法,但找不到任何像样的东西。

Someone gave me this snippet of code:

有人给了我这段代码

Object.defineProperty(player, "health", {
    get: function () {
        return 10 + ( player.level * 15 );
    }
})

But I don't understand it. Mainly, the getis what I can't get (pun intended). How does it work?

但我不明白。主要是,这get是我无法得到的(双关语)。它是如何工作的?

回答by Jan Turoň

Since you asked a similar question, let's take it step by step. It's a bit longer, but it may save you much more time than I have spent on writing this:

既然你问了类似的问题,我们一步一步来。它有点长,但它可能比我花在写这个上的时间节省更多的时间:

Propertyis an OOP feature designed for clean separation of client code. For example, in some e-shop you might have objects like this:

属性是一种 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}

Then in your client code (the e-shop), you can add discounts to your products:

然后在您的客户代码(电子商店)中,您可以为您的产品添加折扣:

function badProduct(obj) { obj.discount+= 20; ... }
function generalDiscount(obj) { obj.discount+= 10; ... }
function distributorDiscount(obj) { obj.discount+= 15; ... }

Later, the e-shop owner might realize that the discount can't be greater than say 80%. Now you need to find EVERY occurence of the discount modification in the client code and add a line

后来,网店老板可能会意识到折扣不能超过 80%。现在您需要在客户端代码中找到每次出现的折扣修改并添加一行

if(obj.discount>80) obj.discount = 80;

Then the e-shop owner may further change his strategy, like "if the customer is reseller, the maximal discount can be 90%". And you need to do the change on multiple places again plus you need to remember to alter these lines anytime the strategy is changed. This is a bad design. That's why encapsulationis the basic principle of OOP. If the constructor was like this:

那么网店老板可能会进一步改变他的策略,比如“如果客户是经销商,最大折扣可以达到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; } 
}

Then you can just alter the getDiscount(accessor) and setDiscount(mutator) methods. The problem is that most of the members behave like common variables, just the discount needs special care here. But good design requires encapsulation of every data member to keep the code extensible. So you need to add lots of code that does nothing. This is also a bad design, a boilerplate antipattern. Sometimes you can't just refactor the fields to methods later (the eshop code may grow large or some third-party code may depend on the old version), so the boilerplate is lesser evil here. But still, it is evil. That's why properties were introduced into many languages. You could keep the original code, just transform the discount member into a property with getand setblocks:

然后你可以改变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

Note the last but one line: the responsibility for correct discount value was moved from the client code (e-shop definition) to the product definition. The product is responsible for keeping its data members consistent. Good design is (roughly said) if the code works the same way as our thoughts.

请注意最后一行:正确折扣值的责任从客户代码(电子商店定义)转移到产品定义。产品负责保持其数据成员的一致性。好的设计是(粗略地说)代码的工作方式与我们的想法相同。

So much about properties. But javascript is different from pure Object-oriented languages like C# and codes the features differently:

这么多关于属性。但是 javascript 与纯面向对象的语言(如 C#)不同,并且对功能进行了不同的编码:

In C#, transforming fields into properties is a breaking change, so public fields should be coded as Auto-Implemented Propertiesif your code might be used in separatedly compiled client.

在 C# 中,将字段转换为属性是一项重大更改,因此如果您的代码可能用于单独编译的客户端,则应将公共字段编码为自动实现的属性

In Javascript, the standard properties (data member with getter and setter described above) are defined by accessor descriptor(in the link you have in your question). Exclusively, you can use data descriptor(so you can't use i.e. valueand seton the same property):

在 Javascript 中,标准属性(具有上述 getter 和 setter 的数据成员)由访问器描述符(在您问题中的链接中)定义。独占地,您可以使用数据描述符(因此您不能使用 ie并在同一属性上设置):

  • accessor descriptor= get + set (see the example above)
    • getmust be a function; its return value is used in reading the property; if not specified, the default is undefined, which behaves like a function that returns undefined
    • setmust be a function; its parameter is filled with RHS in assigning a value to property; if not specified, the default is undefined, which behaves like an empty function
  • data descriptor= value + writable (see the example below)
    • valuedefault undefined; if writable, configurableand enumerable(see below) are true, the property behaves like an ordinary data field
    • writable- default false; if not true, the property is read only; attempt to write is ignored without error*!
  • 访问描述符= get + set(见上面的例子)
    • get必须是一个函数;其返回值用于读取属性;如果未指定,则默认值为undefined,其行为类似于返回 undefined 的函数
    • set必须是一个函数;在给属性赋值时,它的参数用 RHS 填充;如果未指定,则默认值为undefined,其行为类似于空函数
  • 数据描述符= 值 + 可写(见下面的例子)
    • 默认未定义;如果writableconfigureenumerable(见下文)为真,则该属性的行为就像一个普通的数据字段
    • 可写- 默认为false;如果不是true,则该属性是只读的;尝试写入被忽略,没有错误*!

Both descriptors can have these members:

两个描述符都可以具有以下成员:

  • configurable- default false; if not true, the property can't be deleted; attempt to delete is ignored without error*!
  • enumerable- default false; if true, it will be iterated in for(var i in theObject); if false, it will not be iterated, but it is still accessible as public
  • 可配置- 默认false;如果不为真,则无法删除该属性;尝试删除被忽略,没有错误*!
  • 可枚举- 默认false; 如果为真,它将被迭代for(var i in theObject);如果为 false,它将不会被迭代,但它仍然可以作为 public 访问

* unless in strict mode- in that case JS stops execution with TypeError unless it is caught in try-catch block

* 除非在严格模式下- 在这种情况下,JS 会停止执行 TypeError,除非它在try-catch 块中被捕获

To read these settings, use Object.getOwnPropertyDescriptor().

要读取这些设置,请使用Object.getOwnPropertyDescriptor().

Learn by example:

通过例子学习:

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

If you don't wish to allow the client code such cheats, you can restrict the object by three levels of confinement:

如果您不想让客户端代码进行此类作弊,您可以通过三个级别的限制来限制对象:

  • Object.preventExtensions(yourObject)prevents new properties to be added to yourObject. Use Object.isExtensible(<yourObject>)to check if the method was used on the object. The prevention is shallow(read below).
  • Object.seal(yourObject)same as above and properties can not be removed (effectively sets configurable: falseto all properties). Use Object.isSealed(<yourObject>)to detect this feature on the object. The seal is shallow(read below).
  • Object.freeze(yourObject)same as above and properties can not be changed (effectively sets writable: falseto all properties with data descriptor). Setter's writable property is not affected (since it doesn't have one). The freeze is shallow: it means that if the property is Object, its properties ARE NOT frozen (if you wish to, you should perform something like "deep freeze", similar to deep copy - cloning). Use Object.isFrozen(<yourObject>)to detect it.
  • Object.preventExtensions(yourObject)防止将新属性添加到yourObjectObject.isExtensible(<yourObject>)用于检查对象是否使用了该方法。预防很(阅读下文)。
  • Object.seal(yourObject)同上,属性不能删除(有效设置configurable: false为所有属性)。使用Object.isSealed(<yourObject>)检测的物体此功能。密封很(阅读下文)。
  • Object.freeze(yourObject)同上,属性不可更改(有效设置writable: false为所有带数据描述符的属性)。Setter 的可写属性不受影响(因为它没有)。冻结是浅的:这意味着如果属性是对象,它的属性不会被冻结(如果你愿意,你应该执行类似“深度冻结”的操作,类似于深度复制克隆)。使用Object.isFrozen(<yourObject>)检测到它。

You don't need to bother with this if you write just a few lines fun. But if you want to code a game (as you mentioned in the linked question), you should really care about good design. Try to google something about antipatternsand code smell. It will help you to avoid situations like "Oh, I need to completely rewrite my code again!", it can save you months of despair if you want to code a lot. Good luck.

如果你只写几行有趣的,你就不需要为此烦恼。但是如果你想编写一个游戏(正如你在链接问题中提到的),你应该真正关心好的设计。尝试在 google 上搜索有关反模式代码异味的内容。它将帮助您避免诸如“哦,我需要再次完全重写我的代码!”之类的情况。,如果您想大量编码,它可以为您节省数月的绝望。祝你好运。

回答by Paul

getis a function that is called when you try to read the value player.health, like in:

get是一个在您尝试读取值时调用的函数player.health,例如:

console.log(player.health);

It's effectively not much different than:

它实际上与以下内容没有太大区别:

player.getHealth = function(){
  return 10 + this.level*15;
}
console.log(player.getHealth());

The opposite of get is set, which would be used when you assign to the value. Since there is no setter, it seems that assigning to the player's health is not intended:

get 的反面是 set,它会在您分配给值时使用。由于没有设置器,似乎不打算分配给玩家的健康:

player.health = 5; // Doesn't do anything, since there is no set function defined

A very simple example:

一个非常简单的例子:

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

回答by Code-EZ

definePropertyis a method on Object which allow you to configure the properties to meet some criterias. Here is a simple example with an employee object with two properties firstName & lastName and append the two properties by overriding the toStringmethod on the object.

defineProperty是 Object 上的一个方法,它允许您配置属性以满足某些条件。这是一个简单的示例,员工对象具有两个属性 firstName 和 lastName,并通过覆盖对象上的toString方法附加这两个属性。

var employee = {
    firstName: "Jameel",
    lastName: "Moideen"
};
employee.toString=function () {
    return this.firstName + " " + this.lastName;
};
console.log(employee.toString());

You will get Output as : Jameel Moideen

您将获得输出为:Jameel Moideen

I am going to change the same code by using defineProperty on the object

我将通过在对象上使用 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());

The first parameter is the name of the object and then second parameter is name of the property we are adding , in our case it's toString and then the last parameter is json object which have a value going to be a function and three parameters writable,enumerable and configurable.Right now I just declared everything as true.

第一个参数是对象的名称,然后第二个参数是我们要添加的属性的名称,在我们的例子中是 toString 然后最后一个参数是 json 对象,它的值将是一个函数和三个参数 writable,enumerable和可配置的。现在我只是宣布一切都是真的。

If u run the example you will get Output as : Jameel Moideen

如果你运行这个例子,你会得到输出:Jameel Moideen

Let's understand why we need the three properties such as writable,enumerable and configurable.

让我们来理解为什么我们需要writable、enumerable 和 configure三个属性

writable

可写

One of the very annoying part of the javascript is , if you change the toString property to something else for example

javascript 中非常烦人的部分之一是,如果您将 toString 属性更改为其他内容,例如

enter image description here

在此处输入图片说明

if you run this again , everything gets breaks. Let's change writable to false. If run the same again you will get the correct output as ‘Jameel Moideen' . This property will prevent overwrite this property later.

如果你再次运行它,一切都会中断。让我们将可写更改为 false。如果再次运行,您将获得正确的输出 'Jameel Moideen' 。此属性将防止稍后覆盖此属性。

enumerable

可枚举的

if you print all the keys inside the object , you can see all the properties including toString.

如果打印对象内的所有键,则可以看到包括 toString 在内的所有属性。

console.log(Object.keys(employee));

enter image description here

在此处输入图片说明

if you set enumerable to false , you can hide toString property from everybody else. If run this again you will get firstName,lastName

如果将 enumerable 设置为 false ,则可以对其他人隐藏 toString 属性。如果再次运行,您将获得 firstName,lastName

configurable

可配置

if someone later redefined the object on later for example enumerable to true and run it. You can see toString property came again.

如果稍后有人重新定义了对象,例如可枚举为真并运行它。可以看到 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));

enter image description here

在此处输入图片说明

you can restrict this behavior by set configurable to false.

您可以通过将可配置设置为 false 来限制此行为。

Orginal reference of this information is from my personal Blog

此信息的原始参考来自我的个人博客

回答by Cole Pilegard

Basically, definePropertyis a method that takes in 3 parameters - an object, a property, and a descriptor. What is happening in this particular call is the "health"property of the playerobject is getting assigned to 10 plus 15 times that player object's level.

基本上,defineProperty是一个接受 3 个参数的方法 - 一个对象、一个属性和一个描述符。在这个特定的调用中发生的事情是对象的"health"属性player被分配给玩家对象级别的 10 加 15 倍。

回答by Faizal Pribadi

yes no more function extending for setup setter & getter this is my example Object.defineProperty(obj,name,func)

是的,不再为 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);

回答by ISONecroMAn

Object.defineProperty() is a global function..Its not available inside the function which declares the object otherwise.You'll have to use it statically...

Object.defineProperty() 是一个全局函数..它在声明对象的函数内部不可用。你必须静态使用它......

回答by Willem van der Veen

Summary:

概括:

Object.defineProperty(player, "health", {
    get: function () {
        return 10 + ( player.level * 15 );
    }
});

Object.definePropertyis used in order to make a new property on the player object. Object.definePropertyis a function which is natively present in the JS runtime environemnt and takes the following arguments:

Object.defineProperty用于在播放器对象上创建新属性。Object.defineProperty是一个原生存在于 JS 运行时环境中的函数,它采用以下参数:

Object.defineProperty(obj, prop, descriptor)

Object.defineProperty(obj, prop, descriptor)

  1. The objecton which we want to define a new property
  2. The name of the new propertywe want to define
  3. descriptor object
  1. 对象上,我们要定义一个新的属性
  2. 我们要定义的新属性名称
  3. 描述符对象

The descriptor object is the interesting part. In here we can define the following things:

描述符对象是有趣的部分。在这里,我们可以定义以下内容:

  1. configurable<boolean>: If truethe property descriptor may be changed and the property may be deleted from the object. If configurable is falsethe descriptor properties which are passed in Object.definePropertycannot be changed.
  2. Writable<boolean>: If truethe property may be overwritten using the assignment operator.
  3. Enumerable<boolean>: If truethe property can be iterated over in a for...inloop. Also when using the Object.keysfunction the key will be present. If the property is falsethey will not be iterated over using a for..inloop and not show up when using Object.keys.
  4. get<function>: A function which is called whenever is the property is required. Instead of giving the direct value this function is called and the returned value is given as the value of the property
  5. set<function>: A function which is called whenever is the property is assigned. Instead of setting the direct value this function is called and the returned value is used to set the value of the property.
  1. 可配置<boolean>:如果true属性描述符可能会更改并且可能会从对象中删除该属性。如果可配置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>: 每当分配属性时调用的函数。调用此函数而不是设置直接值,并使用返回值来设置属性的值。

Example:

例子:

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
}

回答by Alvin Smith

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

回答by Mano

Defines a new property directly on an object, or modifies an existing property on an object, and return the object.

直接在对象上定义新属性,或修改对象上的现有属性,并返回该对象。

Note: You call this method directly on the Object constructor rather than on an instance of type Object.

注意:您可以直接在 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
   });

enter image description here

在此处输入图片说明

Simple explanation about define Property.

关于定义属性的简单解释。

Example code: https://jsfiddle.net/manoj_antony32/pu5n61fs/

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

回答by Jaeyson Anthony Y.

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