Javascript自动getter / setters(John Resig Book)

时间:2008-12-18 12:46:00

标签: javascript metaprogramming

我正在阅读John Resig的“Pro Javascript Techniques”,我对一个例子感到困惑。这是代码:

// Create a new user object that accepts an object of properties
function User( properties ) {
  // Iterate through the properties of the object, and make sure
  // that it's properly scoped (as discussed previously)
  for ( var i in properties ) { (function(){
  // Create a new getter for the property
  this[ "get" + i ] = function() {
    return properties[i];
  };
  // Create a new setter for the property
  this[ "set" + i ] = function(val) {
    properties[i] = val;
  };
})(); }
}

// Create a new user object instance and pass in an object of
// properties to seed it with
var user = new User({
  name: "Bob",
  age: 44
});

// Just note that the name property does not exist, as it's private
// within the properties object
alert( user.name == null );

// However, we're able to access its value using the new getname()
// method, that was dynamically generated
alert( user.getname() == "Bob" );

// Finally, we can see that it's possible to set and get the age using
// the newly generated functions
user.setage( 22 );
alert( user.getage() == 22 );

现在在Firebug控制台上运行它(在FF3上)会抛出user.getname()不是函数。我试过这样做:

var other = User
other()
window.getname() --> this works!

它有效!

知道为什么吗?谢谢大家!

PS:我强烈推荐这本书。

修改

做的:

var me = this;

似乎工作得更好,但在执行“getname()”时,它返回'44'(第二个属性)......

我也觉得奇怪的是,它没有修改就在窗口对象上工作了......

和第三个问题,PEZ解决方案与原始解决方案有什么区别? (他不使用匿名函数)

感谢大家的反馈! 1

8 个答案:

答案 0 :(得分:4)

编辑:现在,根据杰森的回答,它有效:

我们需要对值进行闭包。这是一种方式:

function bindAccessors(o, property, value) {
  var _value = value;
  o["get" + property] = function() {
    return _value;
  };
  o["set" + property] = function(v) {
    _value = v;
  };
}

然后User构造函数如下所示:

function User( properties ) {
  for (var i in properties ) {
    bindAccessors(this, i, properties[i]);
  }
}

答案 1 :(得分:4)

我认为在使用JavaScript时最好不要使用new关键字。

这是因为如果你然后错误地使用new关键字(例如:var user = User())实例化没有的对象,*会发生非常糟糕的事情... *原因在于该函数(如果没有new关键字实例化),this将引用全局对象,即window ...

因此,我建议如何使用类似对象的更好方法。

考虑以下示例:

var user = function (props) {
    var pObject = {};
    for (p in props) {
        (function (pc) {
            pObject['set' + pc] = function (v) {
                props[pc] = v;
                return pObject;
            }
            pObject['get' + pc] = function () {
                return props[pc];
            }
        })(p);
    }
    return pObject;
}

在上面的例子中,我在函数内部创建一个新对象,然后将getter和setter附加到这个新创建的对象。

最后,我将返回这个新创建的对象。 请注意this关键字未在任何地方使用

然后,要“实例化”user,我会执行以下操作:

var john = user({name : 'Andreas', age : 21});
john.getname(); //returns 'Andreas'
john.setage(19).getage(); //returns 19

避免陷入陷阱的最佳方法是不首先创建它们 ...在上面的示例中,我避免了new关键字陷阱(正如我所说的那样,在{strong>根本不使用new时,不使用new关键字时应该使用

答案 2 :(得分:3)

你可能想要这样的东西,它更具可读性:(一旦你练习,闭包很容易学习)

function User( properties ) {
  // helper function to create closures based on passed-in arguments:
  var bindGetterSetter = function(obj,p,properties)
  {
    obj["get"+p]=function() { return properties[p]; }
    obj["set"+p]=function(val) { properties[p]=val; return this; }
  };
  for (var p in properties)
    bindGetterSetter(this, p, properties);
}

我还添加了“return this;”所以你可以这样做:

u=new User({a: 1, b:77, c:48});
u.seta(3).setb(20).setc(400)

答案 3 :(得分:3)

我开始这篇文章的唯一目的是为什么事情发生了,我终于做到了。所以如果有人对这里的“为什么”感兴趣,他们就是:

为什么'this'会在匿名函数中发生变化?

一个新函数,即使它是一个匿名函数,在一个对象或另一个函数内声明,总是改变范围,在这种情况下返回全局范围(窗口)

解决方案:在帖子中都说明了,我认为更清楚的是用.call执行匿名函数(这个)

为什么getname()始终返回年龄?

当匿名函数立即执行时,getter / setter会在调用时第一次执行。在那一刻, i 的值将始终是最后一个,因为它已经为所有属性迭代了......并且将始终返回属性[i],这是最后一个值,在这种情况下是年龄。

解决方案:将i值保存在类似

的变量中
 for ( i in properties ) { (function(){ 
  var j = i
  //from now on use properties[j]

基本上就是这样,如果我说错了,请纠正我,因为我正在努力学习这个......

再次感谢。

答案 4 :(得分:2)

如在OP中所写,循环中的this并不是指应该使用的User对象。如果您在循环外捕获该变量,则可以使其工作:

function User( properties ) {
  // Iterate through the properties of the object, and make sure
  // that it's properly scoped (as discussed previously)
 var me = this;
 for ( i in properties ) { (function(){
  // Create a new getter for the property
  me[ "get" + i ] = function() {
    return properties[i];
  };
  // Create a new setter for the property
  me[ "set" + i ] = function(val) {
    properties[i] = val;
  };
 // etc

答案 5 :(得分:2)

我刚刚修改了这个代码..这个应该可以工作..这与设置me = this相同;但是需要一个闭包来正确设置每个属性的值,否则最后一个值将被分配给所有属性。

    // Create a new user object that accepts an object of properties
    var User = function( properties ) {
      // Iterate through the properties of the object, and make sure
      // that it's properly scoped (as discussed previously)
      var THIS = this;
      for ( var i in properties ) { (function(i){
      // Create a new getter for the property
      THIS[ "get" + i ] = function() {
        return properties[i];
      };
      // Create a new setter for the property
      THIS[ "set" + i ] = function(val) {
        properties[i] = val;
      };
    })(i); }
    }

    // Create a new user object instance and pass in an object of
    // properties to seed it with
    var user = new User({
      name: "Bob",
      age: 44
    });

// Just note that the name property does not exist, as it's private
// within the properties object
alert( user.name == null );

// However, we're able to access its value using the new getname()
// method, that was dynamically generated
alert( user.getname() == "Bob" );

// Finally, we can see that it's possible to set and get the age using
// the newly generated functions
user.setage( 22 );
alert( user.getage() == 22 );

答案 6 :(得分:1)

可能变量 i 与迭代中的最后一个值“封闭”(“年龄”)?然后所有的getter和setter都将访问属性[“age”]。

答案 7 :(得分:0)

我发现了一些似乎是答案的东西,它完全是关于背景的。使用for内部的匿名函数,更改上下文使'this'引用窗口对象,奇怪不是吗?

这样:

function User( properties ) {

  for ( var i in properties ) { 
     // here this == User Object
    (function(){
     // inside this anonymous function this == window object
    this[ "get" + i ] = function() {
      return properties[i];
    };

    this[ "set" + i ] = function(val) {
      properties[i] = val;
    };
    })(); 
  }
}

我不知道为什么这个函数改变了执行的上下文,我不确定它应该这样做,无论如何你可以测试它在那里运行代码并尝试window.getname()并且它神奇地工作! :S

之前的解决方案是更改上下文,它可以像J Cooper所说的那样完成,传递变量'me'并使函数成为闭包,或者你可以这样做:

(function(){
     // inside this anonymous function this == User because we called it with 'call'
    this[ "get" + i ] = function() {
      return properties[i];
    };

    this[ "set" + i ] = function(val) {
      properties[i] = val;
    };
 }).call(this); 

无论如何,我在运行'getname'时仍然会得到44 ...任何想法?