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 代码可能会变大,或者某些第三方代码可能依赖于旧版本),因此这里的样板文件不那么邪恶。但是,它仍然是邪恶的。这就是为什么将属性引入许多语言的原因。您可以保留原始代码,只需将折扣成员转换为带有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#中,将字段转换为属性是breaking change,因此如果您的代码可能在单独编译的客户端中使用,则应将公共字段编码为Auto-Implemented Properties。
在 Javascript 中,标准属性(具有上述 getter 和 setter 的数据成员)由 访问器描述符 定义(在您问题中的链接中)。排他地,您可以使用 data descriptor(因此您不能在同一属性上使用即 value 和 set):
访问器描述符 = get + set(见上面的例子) get 必须是函数;它的返回值用于读取属性;如果未指定,则默认为 undefined,其行为类似于返回 undefined 的函数 set 必须是函数;在给属性赋值时,它的参数用 RHS 填充;如果未指定,则默认为 undefined,其行为类似于空函数 数据描述符 = 值 + 可写(参见下面的示例) 值默认未定义;如果 writable、configurable 和 enumerable(见下文)为真,则该属性的行为类似于普通数据字段 可写 - 默认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(<yourObject>)
检查是否在对象上使用了该方法。预防是浅的(阅读下文)。
Object.seal(yourObject) 同上,属性不能被移除(有效地将configurable: false
设置为所有属性)。使用Object.isSealed(<yourObject>)
检测对象上的此功能。印章浅(请阅读下文)。
Object.freeze(yourObject) 同上,属性不能改变(有效地将writable: false
设置为所有带有数据描述符的属性)。 Setter 的可写属性不受影响(因为它没有)。冻结是浅:这意味着如果属性是对象,它的属性不会被冻结(如果你愿意,你应该执行类似“深度冻结”的操作,类似于deep copy - cloning)。使用Object.isFrozen(<yourObject>)
进行检测。
如果你只写几行有趣的东西,你就不必为此烦恼。但是,如果您想编写游戏代码(正如您在链接问题中提到的那样),您应该关心良好的设计。尝试在 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>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)
-
我们要在其上定义新属性的对象
我们要定义的新属性的名称
描述符对象
描述符对象是有趣的部分。在这里我们可以定义以下内容:
-
可配置
<boolean>
:如果true
,属性描述符可能会被更改,并且属性可能会从对象中删除。如果可配置为false
,则Object.defineProperty
中传递的描述符属性无法更改。
可写 <boolean>
:如果true
,则可以使用赋值运算符覆盖该属性。
可枚举 <boolean>
:如果true
,则可以在for...in
循环中迭代该属性。此外,当使用Object.keys
功能时,该键将出现。如果属性为 false
,则不会使用 for..in
循环对其进行迭代,并且在使用 Object.keys
时不会显示。
get <function>
: 需要属性时调用的函数。调用此函数而不是直接给出值,并将返回值作为属性的值给出
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
【讨论】:
【参考方案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的主要内容,如果未能解决你的问题,请参考以下文章