如何使用javascript Object.defineProperty [英] how to use javascript Object.defineProperty

查看:143
本文介绍了如何使用javascript Object.defineProperty的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

有人给了我这段代码

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

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

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

推荐答案

因为你问了一个类似的问题,让我们一步一步来。它有点长,但它可以比我写这篇文章花费更多的时间:

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:

属性是一个专为此设计的OOP功能清理客户代码分离。例如,在某些电子商店中,您可能有以下对象:

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

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; ... }

稍后,电子商店老板可能会意识到折扣不能更大比说80%。现在您需要在客户端代码中找到折扣修改的每一个,并添加一行

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

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

然后电子商店所有者可能会进一步改变他的策略,如如果客户是经销商,最大折扣可以是90%。而且您需要再次对多个位置进行更改,并且需要记住在策略更改时随时更改这些行。这是一个糟糕的设计。这就是为什么 封装 是OOP的基本原则。如果构造函数是这样的:

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 encapsulation is the basic principle of OOP. If the constructor was like this:

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代码可能会变大或某些第三方代码可能依赖于旧版本),因此样板文件在这里不那么邪恶。但是,它仍然是邪恶的。这就是为什么属性被引入到许多语言中的原因。您可以保留原始代码,只需将折扣成员转换为获取设置块的属性:

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 get and set blocks:

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.

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

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

在C#中,将字段转换为属性是< a href =http://blogs.msdn.com/b/abhinaba/archive/2006/04/11/572694.aspx =noreferrer>重大变更,因此公共字段应编码为如果您的代码可能在分开编译的客户端中使用,请自动实现的属性

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

在Javascript 中,标准属性(具有上述getter和setter的数据成员)由访问者描述符定义(在你的问题链接中)。另外,您可以使用数据描述符(因此您不能在同一属性上使用ie value set ):

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. value and set on the same property):


  • 访问者描述符 =获取+设置(参见上面的示例)


    • 获取 必须是一个功能;其返回值用于阅读财产;如果未指定,则默认为 undefined ,其行为类似于返回未定义的函数

    • set 必须是一个功能;在为属性赋值时,其参数由RHS填充;如果未指定,则默认为 undefined ,其行为类似于空函数

    • accessor descriptor = get + set (see the example above)
      • get must 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
      • set must 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

      • 默认未定义;如果可写可配置可枚举(见下文)为真,则该属性的行为类似于普通数据字段

      • 可写 - 默认 false ;如果不是 true ,则该属性是只读的;写入的尝试被忽略而没有错误*!

      • value default undefined; if writable, configurable and 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*!

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

      Both descriptors can have these members:


      • 可配置 - 默认 false ;如果不是,则不能删除该属性;删除尝试删除没有错误*!

      • 可枚举 - 默认 false ;如果为true,则将在中迭代(对象中的var i);如果为false,则不会进行迭代,但仍然可以公开访问

      • 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

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

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

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

      To read these settings, use 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
      

      如果您不希望允许客户端代码这样的作弊,你可以通过三级限制来限制对象:

      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) 阻止将新属性添加到 yourObject 。使用 Object.isExtensible(< yourObject>)检查该对象是否使用了该方法。预防(见下文)。

      • Object.seal(yourObject) 与上述相同,无法删除属性(有效设置 configurable:false 到所有属性)。使用 Object.isSealed(< yourObject>)来检测对象上的此功能。印章(见下文)。

      • Object.freeze(yourObject) 与上述相同,属性无法更改(有效设置 writable:false 到具有数据描述符的所有属性)。 Setter的可写属性不受影响(因为它没有)。冻结是:这意味着如果属性是Object,它的属性不会被冻结(如果你愿意,你应该执行类似深度冻结的操作,类似于深层复制 - 克隆)。使用 Object.isFrozen(< yourObject>)来检测它。

      • 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: false to 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: false to 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.

      如果你只是写几行乐趣,你不需要为此烦恼。但是如果你想编写游戏代码(正如你在链接问题中提到的那样),你应该真的关心好的设计。尝试谷歌关于反模式代码嗅觉的东西。它可以帮助你避免像哦,我需要完全重写我的代码!这样的情况,如果你想要编写很多代码,它可以为你节省数月的绝望。祝你好运。

      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 antipatterns and 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.

      这篇关于如何使用javascript Object.defineProperty的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆