如何使用 javascript Object.defeProperty

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

有人给了我 这段代码:

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

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

125060 次浏览

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

基本上,defineProperty是一个包含3个参数的方法——一个对象、一个属性和一个描述符。在这个特定的调用中发生的是 player对象的 "health"属性被分配到10加15倍的玩家对象的水平。

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

属性 是一个面向对象的特性,用于对客户端代码进行干净的分离。例如,在某些电子商店里,你可能会有这样的物品:

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% 。你需要在多个地方再次进行改变,并且你需要记住在策略改变的任何时候都要改变这些线条。这是个糟糕的设计。这就是为什么 < strong > 封装 是面向对象程序设计的基本原则。如果构造函数是这样的:

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(变种人)方法。问题是,大多数成员的行为像公共变量,只是折扣需要特别注意这里。但是良好的设计需要封装每个数据成员,以保持代码的可扩展性。因此,您需要添加大量不执行任何操作的代码。这也是一个糟糕的设计,一个 样板反模式样板反模式。有时候,您不能只是在以后将字段重构为方法(esshop 代码可能会变大,或者一些第三方代码可能依赖于旧版本) ,因此样板文件在这里就不那么糟糕了。但它仍然是邪恶的。这就是为什么将属性引入到许多语言中的原因。您可以保留原始代码,只需将折扣成员转换为具有 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

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

但是 javascript 不同于纯面向对象的语言,比如 C # ,并且它的代码也不同:

在 C # 中,将字段转换为属性是 突破性的改变,因此如果您的代码可能在单独编译的客户机中使用,则公共字段应该编码为 自动实现的属性

在 Javascript 中,标准属性(上面描述的带有 getter 和 setter 的数据成员)由 访问器描述符访问器描述符定义(在问题中的链接中)。只能使用 数据描述符(因此不能在同一属性上使用即 价值准备好了) :

  • 访问器描述符 = get + set (参见上面的示例)
    • Get 必须是一个函数; 它的返回值用于读取属性; 如果未指定,则默认值为 未定义,其行为类似于返回未定义的函数
    • Set 必须是一个函数; 它的参数在为 property 赋值时用 RHS 填充; 如果未指定,则默认为 未定义,其行为类似于空函数
  • 数据描述符 = value + writable (参见下面的示例)
    • Value default 未定义; 如果 可写的可配置的数不胜数(参见下面)为 true,则该属性的行为类似于普通数据字段
    • 可写的 -默认的 假的; 如果不是 没错,该属性是只读的; 尝试写入被忽略,没有错误 * !

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

  • 可配置 -default 假的; 如果不为 true,则不能删除该属性; 忽略删除尝试,不会出现错误 * !
  • 可枚举 -default 假的; 如果为 true,它将在 for(var i in theObject)中迭代; 如果为 false,它将不被迭代,但它仍然可以作为 public 访问

* 除非在 严格模式中——在这种情况下,JS 使用 TypeError 停止执行,除非它在 尝试捕捉块中被捕获

要读取这些设置,请使用 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

如果你不想让客户编码这样的欺骗,你可以限制对象的三个限制级别:

如果你只写了几行有趣的内容,你就不必为此烦恼了。但是如果你想编写一个游戏(正如你在链接问题中提到的) ,你应该关心好的设计。尝试谷歌一些关于 反模式代码的味道。它可以帮助你避免像 “哦,我需要再次彻底重写我的代码!”这样的情况,如果你想编写很多代码,它可以帮助你避免几个月的绝望。祝你好运。

是的,没有更多的函数扩展设置 setter 和 getter 这是我的示例 < strong > Object.defeProperty (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);

DefeProperty ()是一个全局函数。.它在声明对象的函数中不可用。你必须静态地使用它..。

DefeProperty 是 Object 上的一个方法,它允许您配置属性以满足某些条件。 下面是一个简单的示例,其中一个员工对象具有两个属性 firstName 和 lastName,并通过重写对象上的 ToString方法追加这两个属性。

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

您将得到输出: Jameel Moideen

我将通过在对象上使用 defeProperty 来更改相同的代码

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 object,它的值是一个函数,三个参数是可写的、可枚举的和可配置的。现在我宣布一切都是真的。

如果您运行这个示例,您将得到 Output: Jameel Moideen

让我们来理解为什么需要 可写,可枚举,可配置。这样的三个属性

可写的

例如,如果将 toString 属性更改为其他属性,那么 javascript 的一个非常恼人的部分就是

enter image description here

如果你再这么做,一切都会失败。 让我们将可写性更改为 false。如果再次运行相同的命令,您将得到正确的输出为“ Jameel Moideen”。此属性将防止以后重写此属性。

数不胜数

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

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

enter image description here

如果将可枚举设置为 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));

enter image description here

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

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

摘要:

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

使用 Object.defineProperty是为了在玩家对象上创建一个新属性。Object.defineProperty是一个本地存在于 JS 运行时环境中的函数,它采用以下参数:

Object.defineProperty(obj, prop, descriptor)

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

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

  1. 可配置的 <boolean>: 如果 true属性描述符可能被更改,并且该属性可能从对象中删除。如果可配置为 false,则不能更改在 Object.defineProperty中传递的描述符属性。
  2. Writable <boolean>: 如果 true属性可能被赋值操作符覆盖。
  3. 枚举 <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
});

enter image description here

关于定义 Property 的简单说明。

示例代码: 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