我不明白以下内容:
var x = function() {
this.foo="foo";
return function() {
this.bar = "bar";
return foo+bar;
};
}(); // returns inner
alert(x()); // 'foobar', so both 'this' variables are set
alert(x.bar); // undefined - but wasn't it used correctly?
alert(new x().bar); // ok, works
我的假设是第一次生成并使用默认的'this'范围/变量映射,然后当调用'new'时,通过新的'this'发送一个新对象(function?)并返回。或者,也许x不是一个合适的对象?但是,“这个”最终如何设置并用于制作“foobar”?
了解这一点需要了解什么?
答案 0 :(得分:9)
首先让我们来看看JavaScript的一些优点,然后我们可以处理你的例子。
误解的一个方面是背景。每个函数都在上下文中调用,可以使用关键字this
来获取。让我们编写一个可用于检查上下文的函数:
var probe = function(){
// if the context doesn't have a name, let's name it
if(!this.name){
this.name = "lumberjack";
}
// print the name of my context
console.log(this.name);
};
我们走了:
name = "global!";
// when we call a function normally it still have a context:
// the global context
probe(); // prints: global!
var ctx = {name: "ctx"};
// we can set a context explicitly using call()
probe.call(ctx); // prints: ctx
// we can set a context explicitly using apply()
probe.apply(ctx); // prints: ctx
// it is set implicitly, if we call a function as a member
ctx.fun = probe;
ctx.fun(); // prints: ctx
// or we can create a brand new object and set it as a context:
// that's what "new" does
var t = new probe(); // prints: lumberjack
// let's sum it up:
console.log(name); // prints: global!
console.log(ctx.name); // prints: ctx
console.log(t.name); // prints: lumberjack
这就是为什么容易陷入困境并无意中陷入全球背景的原因。
许多人在看到构造函数返回值时感到困惑。这是合法的。构造函数可以返回对象,函数或数组。该值将用作实例。旧实例将被丢弃。
var myClass = function(){
// if it is called as a constructor, "this" will be a new instance
// let's fill it up:
this.a = 42;
this.b = "Ford";
this.c = function(){ return "Perfect"; };
// done? let's discard it completely!
// and now for something completely different...
return {
owner: "Monty Python",
establishment: "Flying Circus"
};
};
var t = new myClass();
alert(t.owner + "'s " + t.establishment);
正如所料,它展示了“Monty Python的飞行马戏团”。
如果构造函数返回其他内容(例如,数字,字符串,null,undefined),则返回的结果将被丢弃,并且将使用旧实例。
你的例子很难理解,主要是因为它的编写方式。让我们通过重写来简化它。
首先让我们处理x
:
var x = function() {
this.foo = "foo";
return function() {
this.bar = "bar";
return foo + bar;
};
}(); // returns inner
我们可以看到匿名函数(1 st function
)立即执行,所以我们可以内联它:
// next assignment can be simplified because
// top "this" is window or the global scope
//this.foo = "foo"; =>
foo = "foo";
x = function() {
this.bar = "bar"; // this line depends on its context, or "this"
return foo + bar; // this line uses global "foo" and "bar"
};
所以最后我们有两个全局变量:foo
(一个字符串)和x
(一个函数)。
现在让我们回顾1 st 警告:
alert(x()); // 'foobar', so both 'this' variables are set
再次,让我们内联x()
:
// next assignment can be simplified because
// top "this" is window or the global scope
//this.bar = "bar"; =>
bar = "bar";
// at this moment both global "foo" and "bar" are set
alert(foo + bar); // => "foo" + "bar" => "foobar"
2 nd 警报同样简单:
alert(x.bar); // undefined - but wasn't it used correctly?
它不需要太多重写。 x
是一个函数,我们没有向其添加任何属性,因此x.bar
未定义。如果添加它,您可以看到结果:
x.bar = "bar2";
alert(x.bar); // bar2
3 rd 警告演示了JavaScript的OOP:
alert(new x().bar); // ok, works
(旁注:它的作用只是因为您先运行x()
,否则会因为bar
未定义而爆炸。
让我们改写它:
var t = new x();
alert(t.bar); // bar
现在让我们分析构造函数。它有两个语句:赋值和返回。后者被忽略,因为它返回一个字符串。所以我们可以像这样重写它:
x = function(){
this.bar = "bar";
};
var t = new x();
alert(t.bar); // bar
我希望现在看起来都很简单。
答案 1 :(得分:4)
这是new运营商的主要抱怨......
该运算符创建一个继承自操作数构造函数原型的新对象,然后调用该函数,将新对象分配给this
。
如果在调用构造函数时忘记使用new运算符,则会调用普通函数调用,this
绑定到全局对象(window
)而不是新对象。
只要使用this
尝试初始化自己的实例,您的函数就会附加全局变量。
在您的示例中,全局对象最终会有两个新变量:
window.foo
window.bar
因此,有些人更喜欢prototypal inheritance而不是伪经典方法。
答案 2 :(得分:3)
要回答标题中提出的问题:this
将引用全局对象。
请记住,this
在JavaScript中的行为与在Java或C ++等语言中的行为不同;它纯粹用于上下文,并且不必在每次调用给定函数时引用相同类型的对象。 From the MDC documentation:
this
有四种方式可以传递 [...]
如果没有使用上述方法,则将全局对象作为上下文对象传递,例如,当这发生在任何构造函数之外的顶层时,或者调用函数而不将其作为对象的方法调用时,在func(arg1, arg2)
。
有关其余问题的答案,请参阅:Just when I think I finally understand Javascript scope…
答案 3 :(得分:3)
正如Shog9所说,这与您在Java等中看到的正常范围不同。
AFAIK Javascript使用动态作用域,就像Common LISP / eLisp那样,而不是像Scheme / Lisp-1那样的词法作用域。