以前很少写window间通讯的脚本,一直以为Global 变量的访问范围和var的一样。今天在写Triglav作弊工具的时候突然发现了个奇怪的问题,让我重新思考了Global变量的作用域,顺便复习一下var和this。
This的作用域:作用与当前实例,并且只能使用this关键字访问。比如 a = new Object,这时候a就是一个实例。那么当a下有一个方法,无论这个方法是实例化前就写好的,还是实例化后用a.foo= function(){}添加上去的,在这个方法里,this关键字都指向a。
Sample:
FooClass = function()
{
this.Variable = "This is a public variable.";
this.Say = function()
{
alert(this.Variable);
}
}
foo = new FooClass();
foo.Say();
foo.Say2 = function()
{
alert(this.Variable);
}
foo.Say2();
注:
1) 上面这个例子中可以把this.Variable替换一下试试看。
2) 另外,方法中的this指向(俗称“上下文”)是可以通过call, apply等方法改变,这点与下面的var不同。
Var的作用域:Var变量(下称私有变量)只作用于变量申明时的所在函数(updated 9/19/08) 的花括号 "{ }"以内。也就是说,它的作用域是依附与代码本身的,你的代码写在哪,作用域就在哪,而且在运行时是不可以改变的。
FooClass = function()
{
var Var = "This is a variable which defined by var.";
this.ThisVar = "This is a variable which defined by this.";
var self = this;
this.Say = function()
{
setTimeout(function()
{
alert(Var);
alert(typeof this.ThisVar);
alert(self.ThisVar);
}, 1000);
}
}
foo = new FooClass;
foo.Say();
大家可以在上面的例子中看到,无论有几层{}的嵌套var声明的变量始终可以穿越他们,并且被scope内的变量访问到。
同时,在这个例子里,我们可以看到setTimeout中function的this并不是foo,typeof this.ThisVar返回的是undefined,但是我们在这里利用了var的特性,耍了个花招(var self = this),让不同上下文的方法能够访问到同一个实例。
Global变量:global变量javascript中可以直接用“variable_name = variable_value”来声明,也可以用"this.variable_name=xx“或者"var variable_name=xx“来声明,后两者要求声明表达式必须在最外最顶层,而且第一种声明可以在代码中的任意位置。要理解Global的作用域,首先要知道javascript中,任何一个window其实都是一个实例,而所有的global变量,都是属于window这个实例的。
而我们当前代码的上下文,如果没有特殊指定,都是在window实例下。换句话说,你可以直接使用this.variable_name来访问一个global变量----但是,这又与我们平时的使用习惯不一样,我相信大多数的jser在书写代码的习惯都是直接使用variable_name。而这种不使用this关键字访问global变量的方法和访问var声明的变量很相似--确实,在javascript中global变量其实是有双重特性的,它既可以使用this访问,也可以不使用this。当你使用this访问时,会先确定当前的上下文,再找到上下文中的指定变量。而如果没有this,那么毫无疑问会访问到代码文本所属的window下的某个global变量。
现在来看这种情况:假设我们有两个页面,分别为win1和win2。win2是由win1打开的,所以我们可以拿到win2的window对象,而且他们是在同一个域名下,不存在跨域访问的问题。
这时候做这样一件事:在win1下想做一个小小的hack,把win2下的CallLocalVar函数修改一下,让win2的代码在调用CallLocalVar函数时,先调用帽子函数(这时帽子函数其实算在win2的实例下),再由帽子函数调用回win2下原来的CallLocalVar。
这时候会有很有趣的事情发生,因为帽子函数是用win2.CallLocalVar = function(){}定义的,所以他的上下文应当是win2,但是又由于代码本身是在win1下写的,那么当帽子函数中global变量的访问方法是用“通常习惯的方式(无this)”方式访问的, 那么帽子函数最后会访问回win1下的global变量。
请看下面的例子:
win1.htm里的内容:
<script>
local = "this is win1 global variable";
win2 = window.open("win2.htm","win2");
setTimeout(function()
{
local.test = win2.CallLocal;
win2.CallLocal = function()
{
this.alert(local);
if (local.test)
{
//local.test();
}
}
}, 4000);
</script>
win2.htm的内容:
<body>
<script>
local = "this is win2 global variable";
function CallLocal()
{
alert(local);
}
</script>
<button onclick="CallLocal()">Wait 4 sec and then click</button>
</body>
好了,最后有个终级问题:如果将win2.htm中的<button onclick="Call....</button>替换成setTimeout(CallLocal, 10000)会怎么样?
大牛们的相关讨论:http://bbs.51js.com/viewthread.php?tid=79018
http://www.jibbering.com/faq/faq_notes/closures.html


Comments:
You can leave a comment on this post if you login