javascript疑难解答

前段时间去听了一个同事的knowledge share session,他大致给我们讲了一些写javascript需要注意的一些细节。session本身并没有什么问题,但是后来大家由此引申出来的讨论引发了我的思考。

这些讨论的内容有点杂乱,边边角角都涉及一点。我花了些时间,写了一些testcase,试图找出其中的规律,并希望对大家有所帮助。

先来看一个简单的例子:

var test={
};

function test2() {
};

var test3 = function() {
}


这段代码可以说是最简单的javascript了,首先定义了一个名为test的object,一个名为test2的funciton,一个名为test3的function。

那么此时我们应该如何使用这三个变量呢?最简单的办法:直接引用。
而事实上,我们也可以用window这个包在他们外层的全局object名来引用他们, 即: window.test或者window.test2()。

那么,能不能用this来访问这些变量呢? 理论上讲,this现在应该就是指代window object才对。于是我试着这样访问this.test2().结果显示,成功。

第二个例子开始有点复杂:

var test={
t1: 1,
t2: this.t1,

myTest: function() {
var t1 = 11;
return this.t1;
}
};


在test中,只有一种定义语法是对的: variable: value,如果我想用var t1 = 1;这种方式的话,就会出现语法错误。

当我在test里面定义一个方法时,这个方法就从属于test object, 那么它内部的this就指向了test。所以myTest方法返回的值就应该是1,而不是11.如果我去掉this的话,则正好相反。

而当我尝试去获得t2的时候,却发现t2为undefine,也就是说t2是无法使用test中的其它变量来初始化的。

我试着把this改成test,结果却是test undefine。我把t2的初始化改成t2: test.myTest(),结果也是一样的,test undefine。
而我把之改成t2: this.myTest(),报出来的错又变成了this.myTest() undefine,哪怕我把该方法的定义放到了t2的前面。

由此可以得出的结论是,在定义一个对象时,该对象里面的对象是不能够获得this对象的,相应的,该对象里的object则没有问题。
因此,该对象可以改成这样:

var test={
t1: 1,
t2: 0,

myTest: function() {
var t1 = 11;
test.t2 = test.t1;
return this.t2;
}
};

通过这种方法,test里的t2就可以通过调用被成功的初始化了。

看起来似乎很完美...

真的是这样吗?我们真的是没有办法获取this类型么? 我们刚刚对于报错的发现:使用this和使用test得到的错误是不一样的!也许此时的this,根本就不是test。


var g = 25;
var test={
t1: 1,
t2: this.g,
};

alert(test.t2);

得到的结果是25.

是不是觉得很吃惊? 此时的this居然指向的是test的外层,即window。另一方面,我们认为此时的test对象尚未创建完毕,因此无法访问。

我们必须为这种怪异的行为提供一个具有概念完整性的解释。

以下是我的理解,因此而造成的一切后果,本人概不负责...

在任何情况下,this都是指向该对象的容器的父亲(容器这个概念是我自己发明的,其本质也是一个对象),对于某个对象,如t1或者t2,他们的容器是test,所以this便指向test的父亲,即window,而window是已经预定义好的。 然而对于某个函数,如myTest,它本身就是容器,而它的父亲则是test,那么当你位于myTest容器内时,其中的this便指向这个容器的父亲test。这就解决了第一个问题:this的指向问题。

而javascript是解释性的语句,它是边读源码边解释的。当它遇到一个函数的定义时,暂时是不解释的,而是直接把source code保存下来。这就造成了定义变量的时候无法使用test,而调用函数(此时test已经定义好了)时则没有问题。

因此当js解释器遇到test时,整个过程是这样的:

var test={
t1: 1,
t2: this.t1,

myTest: function() {
var t1 = 11;
return this.t1;
}
};

1. 解释test,开始定义一个新对象。
2. 解释t1,放入该对象中。
3. 解释t2,尝试寻找test对象,因为test对象还在定义中,因此无法找到。
4. 解释myTest,是个方法,直接把代码拷贝到对象中。
5. 完成test对象的定义。

所以此时你再去调用myTest的时候,test对象已经定义完全,可以正确使用了。


接下来,我们看看function test2() {}这种定义形式:

function test2() {
var t3 = 1;
var t4 = t3;
return t4;
};

我们之前魔咒一般的代码,在这里便轻松搞定,具体就不解释了,本身也是很简单的。

同样,根据前面的例子,可以猜出,此时的this指向的是window:


var g = 25;
function test2() {
var t3 = 1;
var t4 = t3;
return this.g;
};


虽然我们知道,在js中,函数也是对象,但是像这种方法定义的函数,是无法获得其内部定义的局部变量的。不过有意思的是,我们还是可以写这样的代码:

function test2() {
var t3 = 1;
var t4 = t3;
return test2.t22;
};

test2.t22 = 12;


此时,你可以把test2当作一个方法来使用test2();又或者当作一个对象来使用test2.t22,都是可以访问的。唯一要记住的就是,在test2中this指向的是window而不是test2.

那么如果我在这个方法里面定义对象或者方法,它的this是不是就是指test2这个function呢?


var g = 25;
function test2() {
var t3 = 1;
var t4 = {
t5: t3
};

var t6 = function() {
return t3;
}
return t4.t5;
};


结果显示,我们是可以访问的t3的,而t4和t6中的this,指向的居然也是window。

var g = 25;
function test2() {
var t3 = 1;
var t4 = {
t5: this.g
};

var t6 = function() {
return this.g;
}
return t4.t5;
};
alert(test2())

我们可以这样理解,当我们把test2当作函数来使用的时候,js解释器就一句一句执行该函数,而test2中的this显然是指向window的,所以test2里面的一切,也都指向window
了。而对于该环境中的任何变量,也都是可以直接访问到的了。

如果我们的猜想正确的话,如果我们把test2当作对象来使用,得到的应该就是另一种结果:

var g = 25;
function test2() {
var t3 = 1;
};

test2.t4 = {t5: this.g};
test2.t6 = function() {
return this.g;
};
alert(test2.t4.t5);
alert(test2.t6());

我们发现,t4.t5是可以访问的,而t6()的返回值则是undefine。这又是为什么呢?

其实也很简单,我们只要记住,变量是定义时环境,而函数则是执行是环境。这样我们就不会困惑了,因为对于t5而言,他就是定义是环境,那么this其实是指向当前大括号这个容器的父亲,也就是window。
而函数则要到运行时才会解释,而在运行时,它的this应该是指向test2.

说到这里,我只能说js实在是太绕了,写js也是一件非常痛苦的事情。

但是我们仍然需要继续下去,我们来看最后一个case:

var test3 = function() {
var t7 = 1;
return t7;
}
alert(test3());


基本而言这个函数和上一个仅仅是在形式上有一些区别,我并没有发现他们在使用上的差别。根据js几本比较重要的书籍上的介绍,都是推荐使用这种函数定义方法,因为这样可以“提醒用户函数也是对象”。


最后想提一点是,在函数中定义一个变量,一定要记得加var,否则就会出现下面这种情况:


var test3 = function() {
var t7 = 1;
t8 = 13;
return t8;
}
alert(test3());
alert(t8);

t8已经变成了一个全局的变量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值