我正在阅读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
答案 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 ...任何想法?