19

我正在阅读 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

4

8 回答 8

5

我认为在 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关键字不在任何地方使用

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

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

避免陷入陷阱的最好方法是首先不创建它们......在上面的示例中,我正在避免new关键字陷阱(正如我所说,new在应该使用关键字时不使用它会导致不好的事情发生完全不使用new

于 2008-12-19T13:57:08.120 回答
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]);
  }
}
于 2008-12-18T12:58:40.193 回答
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)
于 2008-12-18T16:26:43.720 回答
3

我开始这篇文章的唯一目的是了解为什么会发生这种情况,我终于做到了。因此,如果有人对这里的“为什么”感兴趣,他们是:

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

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

解决方案:帖子中的所有内容,我认为更清楚的是使用 .call(this) 执行匿名函数

为什么 getname() 总是返回年龄?

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

解决方案:将 i 值保存在这样的变量中

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

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

再次感谢。

于 2008-12-19T13:14:30.403 回答
2

正如 OPthis中所写的那样,循环中并没有像应有的那样引用 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
于 2008-12-18T13:08:57.253 回答
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 );
于 2011-10-31T06:21:26.783 回答
1

也许变量i被迭代中的最后一个值“封闭”(“年龄”)?然后所有的 getter 和 setter 都将访问 properties["age"]。

于 2008-12-18T15:15:39.323 回答
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......有什么想法吗?

于 2008-12-18T14:12:36.860 回答