我四处寻找如何使用Object.defineProperty
方法,但找不到任何体面的东西。
有人给了我this snippet of code:
Object.defineProperty(player, "health", {
get: function () {
return 10 + ( player.level * 15 );
}
})
但我不明白。主要是get
是我无法得到的(双关语)。它是如何工作的?
答案 0 :(得分:451)
由于您询问了similar question,请让我们一步一步地采取行动。它有点长,但它可能比我花在写这篇文章上的时间多得多:
属性是一种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
(访问者)和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的数据成员)由访问者描述符定义(在您的问题中的链接中)。仅此而言,您可以使用数据描述符(因此您无法在同一属性上使用ie value 和 set ):
两个描述符都可以包含以下成员:
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.isExtensible(<yourObject>)
检查方法是否在对象上使用。预防浅(见下文)。configurable: false
设置为所有属性)。使用Object.isSealed(<yourObject>)
检测对象上的此功能。印章浅(见下文)。writable: false
设置为具有数据描述符的所有属性)。 Setter的可写属性不受影响(因为它没有一个)。冻结浅:这意味着如果属性是Object,它的属性不会被冻结(如果你愿意,你应该执行类似&#34;深度冻结&#34;类似于{ {3}})。使用Object.isFrozen(<yourObject>)
进行检测。如果你只想写几行乐趣,你就不需要为此而烦恼。但是如果你想编写游戏代码(正如你在链接问题中提到的那样),你应该真的关心好的设计。尝试谷歌关于反模式和代码嗅觉的东西。它可以帮助你避免像&#34;哦,我需要再次完全重写我的代码!&#34; ,如果你想要编写很多代码,它可以为你节省数月的绝望。祝你好运。
答案 1 :(得分:23)
get
是您尝试读取值player.health
时调用的函数,如:
console.log(player.health);
实际上并没有太大的不同:
player.getHealth = function(){
return 10 + this.level*15;
}
console.log(player.getHealth());
设置了与get相反的值,在分配给值时将使用该值。由于没有制定者,似乎不打算分配给玩家的健康状况:
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
答案 2 :(得分:10)
defineProperty 是Object上的一个方法,它允许您配置属性以满足某些标准。 这是一个简单的示例,其中一个雇员对象具有两个属性firstName&amp; 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对象,它具有一个函数值和三个参数可写,可枚举和可配置。现在我只是声明一切都是真的。
如果您运行该示例,您将获得输出为: Jameel Moideen
让我们理解为什么我们需要三个属性,例如可写,可枚举和可配置。
<强>可写强>
javascript的一个非常烦人的部分是,如果你将toString属性改为其他东西,例如
如果再次运行,一切都会中断。 让我们把可写改为假。如果再次运行相同,您将得到正确的输出为'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));
您可以通过将configurable设置为false来限制此行为。
Orginal reference of this information is from my personal Blog
答案 3 :(得分:3)
基本上,defineProperty
是一个接受3个参数的方法 - 对象,属性和描述符。此特定调用中发生的事情是"health"
对象的player
属性被分配为该玩家对象级别的10加15倍。
答案 4 :(得分:0)
是的,没有更多的功能扩展设置setter&amp;吸气 这是我的例子 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);
答案 5 :(得分:0)
Object.defineProperty()是一个全局函数..它在函数内部不可用,否则会声明该对象。你必须静态使用它...
答案 6 :(得分:0)
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
时不会显示。<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
}
答案 7 :(得分:0)
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
答案 8 :(得分:0)
直接在对象上定义新属性,或在对象上修改现有属性,然后返回对象。
注意:您可以直接在Object构造函数上调用此方法 比在Object类型的实例上要高。
const object1 = {};
Object.defineProperty(object1, 'property1', {
value: 42,
writable: true,
enumerable: true,
configurable: true
});
有关定义属性的简单说明。
答案 9 :(得分:0)
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