defineProperty

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了defineProperty相关的知识,希望对你有一定的参考价值。

参考技术A

首先需要声明的是这个是ES5的特性,兼容大多数浏览器,除了ie8仍旧是被嫌弃的存在
Object.defineProperty ,顾名思义,为对象定义属性。在js中我们可以通过下面这几种方法定义属性

Object的 defineProperty defineProperties 这两个方法在主要功能就是用来定义或修改对象的内部属性,与之相对应的 getOwnPropertyDescriptor getOwnPropertyDescriptors 就是获取对象的内部属性的描述。

默认值false,表示能否通过delete删除此属性, 能否修改属性的特性,或能否修改把属性修改为访问器属性。
ps:如果直接使用字面量定义对象,默认值为true

默认值false,表示该属性是否可枚举,即是否通过for-in循环或Object.keys()返回属性。
ps:如果直接使用字面量定义对象,默认值为true

默认值false,能否修改属性的值。
ps:如果直接使用字面量定义对象,默认值为true

默认值undefined,表示该属性的值

同上

同上

默认值undefined,一个给属性提供 getter 的方法(访问对象属性时调用的函数,返回值就是当前属性的值),如果没有 getter 则为 undefined。该方法返回值被用作属性值

默认值undefined,一个给属性提供 setter 的方法(给对象属性设置值时调用的函数),如果没有 setter 则为 undefined。该方法将接受唯一参数,并将该参数的新值分配给该属性。

Object.defineProperty()
Object.defineProperties()

Object.getOwnPropertyDescriptor()
Object.getOwnPropertyDescriptors()

需要理解的是 设置是设置 取值是取值

利用set的方法和get的过程我们可以实行双向数据绑定

用途:set 可以优化属性设置
get可以优化输出:例如对象某些功能废弃,则可以使用get返回错误信息

如何使用javascript Object.defineProperty

【中文标题】如何使用javascript Object.defineProperty【英文标题】:how to use javascript Object.defineProperty 【发布时间】:2013-09-02 16:42:13 【问题描述】:

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

有人给我this snippet of code:

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

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

【问题讨论】:

developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… 这是一个很好的教程。 【参考方案1】:

既然你问了similar question,那就一步步来吧。它有点长,但它可能会比我花在写这篇文章上的时间来节省你更多的时间:

Property 是一种 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#中,将字段转换为属性是breaking change,因此如果您的代码可能在单独编译的客户端中使用,则应将公共字段编码为Auto-Implemented Properties。

在 Javascript 中,标准属性(具有上述 getter 和 setter 的数据成员)由 访问器描述符 定义(在您问题中的链接中)。排他地,您可以使用 data descriptor(因此您不能在同一属性上使用即 valueset):

访问器描述符 = get + set(见上面的例子) get 必须是函数;它的返回值用于读取属性;如果未指定,则默认为 undefined,其行为类似于返回 undefined 的函数 set 必须是函数;在给属性赋值时,它的参数用 RHS 填充;如果未指定,则默认为 undefined,其行为类似于空函数 数据描述符 = 值 + 可写(参见下面的示例) 默认未定义;如果 writableconfigurableenumerable(见下文)为真,则该属性的行为类似于普通数据字段 可写 - 默认false;如果不是 true,则该属性是只读的;尝试写入被忽略,没有错误*!

两个描述符都可以有这些成员:

可配置 - 默认false;如果不为真,则无法删除该属性;尝试删除被忽略,没有错误*! 可枚举 - 默认false;如果为真,将在for(var i in theObject) 中迭代;如果为 false,则不会被迭代,但仍可作为公共访问

* 除非在 strict mode - 在这种情况下 JS 会停止执行 TypeError 除非它被 try-catch block 捕获

要阅读这些设置,请使用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(&lt;yourObject&gt;) 检查是否在对象上使用了该方法。预防是浅的(阅读下文)。 Object.seal(yourObject) 同上,属性不能被移除(有效地将configurable: false设置为所有属性)。使用Object.isSealed(&lt;yourObject&gt;) 检测对象上的此功能。印章(请阅读下文)。 Object.freeze(yourObject) 同上,属性不能改变(有效地将writable: false设置为所有带有数据描述符的属性)。 Setter 的可写属性不受影响(因为它没有)。冻结是:这意味着如果属性是对象,它的属性不会被冻结(如果你愿意,你应该执行类似“深度冻结”的操作,类似于deep copy - cloning)。使用Object.isFrozen(&lt;yourObject&gt;) 进行检测。

如果你只写几行有趣的东西,你就不必为此烦恼。但是,如果您想编写游戏代码(正如您在链接问题中提到的那样),您应该关心良好的设计。尝试在 Google 上搜索有关 antipatterns代码气味 的信息。它将帮助您避免像“哦,我需要再次完全重写我的代码!”这样的情况,如果您想大量编码,它可以为您节省数月的绝望。祝你好运。

【讨论】:

这部分很清楚。 function Product(name,price) this.name = name; this.price = price; var _discount; // private member Object.defineProperty(this,"discount", get: function() return _discount; , set: function(value) _discount = value; if(_discount&gt;80) _discount = 80; ); 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【参考方案2】:

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

【讨论】:

【参考方案3】:

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

注意:您直接在 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/

【讨论】:

【参考方案4】:

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 对象,它的值将是一个函数和三个参数可写、可枚举和可配置。现在我只是将所有内容都声明为 true。

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

让我们明白为什么我们需要可写、可枚举和可配置这三个属性。

可写

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

如果你再次运行它,一切都会中断。 让我们将 writeable 更改为 false。如果再次运行相同的程序,您将获得正确的输出为“Jameel Moideen”。此属性将防止以后覆盖此属性。

可枚举

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

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

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

可配置

如果后来有人重新定义了对象,例如 enumerable 为 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 来限制此行为。

Orginal reference of this information is from my personal Blog

【讨论】:

我知道你的博客上有这个,只是把它贴在这里,但至少要知道这一点:屏幕截图在 SO 上并不流行。您不能复制粘贴代码进行尝试,搜索引擎或辅助技术将看不到代码。 @JacqueGoupil 你是对的。我将通过添加代码而不是屏幕截图来更新【参考方案5】:

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

【讨论】:

【参考方案6】:

总结:

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

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

Object.defineProperty(obj, prop, descriptor)

    我们要在其上定义新属性的对象 我们要定义的新属性的名称 描述符对象

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

    可配置 &lt;boolean&gt;:如果true,属性描述符可能会被更改,并且属性可能会从对象中删除。如果可配置为false,则Object.defineProperty 中传递的描述符属性无法更改。 可写 &lt;boolean&gt;:如果true,则可以使用赋值运算符覆盖该属性。 可枚举 &lt;boolean&gt;:如果true,则可以在for...in 循环中迭代该属性。此外,当使用Object.keys 功能时,该键将出现。如果属性为 false,则不会使用 for..in 循环对其进行迭代,并且在使用 Object.keys 时不会显示。 get &lt;function&gt; : 需要属性时调用的函数。调用此函数而不是直接给出值,并将返回值作为属性的值给出 set &lt;function&gt; :每当分配属性时调用的函数。调用此函数并使用返回值来设置属性的值,而不是设置直接值。

示例:

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

【讨论】:

【参考方案7】:

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

console.log(player.health);

实际上并没有太大的不同:

player.getHealth = function()
  return 10 + this.level*15;

console.log(player.getHealth());

get 的反义词是 set,当你赋值给 value 时会用到。由于没有二传手,似乎不打算分配给玩家的健康:

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

【讨论】:

它就像一个你不需要实际使用() 来调用的函数......我不明白他们发明这个东西时的想法是什么。功能完全相同:jsbin.com/bugipi/edit?js,console,output【参考方案8】:

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

【讨论】:

【参考方案9】:

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

【讨论】:

【参考方案10】:

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

【讨论】:

以上是关于defineProperty的主要内容,如果未能解决你的问题,请参考以下文章