前段时间去听了一个同事的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已经变成了一个全局的变量。
分享到:
相关推荐
21.如何在页面中应用javascript脚本-示例1 21.如何在页面中应用javascript脚本-示例2 22.如何实现从服务器端向页面动态添加javascript脚本-示例1 22.如何实现从服务器端向页面动态添加javascript脚本-示例2 24....
1. **网页开发**:西路疑难解答v1.0作为一个在线应用,其前端界面的开发可能涉及到HTML、CSS和JavaScript。HTML用于构建页面结构,CSS用于美化页面样式,而JavaScript则用于实现交互效果,如表单验证、动态加载等。 ...
在《ASP.NET专家疑难解答200问》中,涵盖了多个关键知识点,这些知识点是ASP.NET开发过程中常见的问题和挑战。 1. **Web服务控件使用**:ASP.NET提供了多种内置控件,如Button、TextBox、GridView等,用于构建用户...
以上只是"ASP.NET专家疑难解答200问"中的一部分问题,实际涵盖的范围还包括ASP.NET Core、Entity Framework、SignalR、WCF、ASP.NET MVC的高级特性,以及与前端技术如HTML、CSS、JavaScript的交互等。学习和掌握这些...
"ASP.NET专家疑难解答200问"这个压缩包文件很可能是针对ASP.NET开发中遇到的各种问题和挑战的集合,包含了200个具有代表性和常见性的问题及其解决方案。 首先,我们要理解ASP.NET的核心概念。ASP.NET不仅支持HTML、...
《Asp.Net专家疑难解答200问》的示例代码涵盖了Asp.Net开发中的诸多常见问题和解决方案,是开发者日常工作中不可或缺的参考资料。以下是一些关键知识点的详细解析: 1. **Asp.Net基础**:Asp.Net是微软推出的Web...
已发布的练习参考了BOOTCAMP的培训-NodeJS开发人员-JavaScript疑难解答( ) 挑战说明: 在今年年底,学校有一个聚会。 参加聚会的报名将于7月初开始。 注册后,学生可以选择是否要在聚会上成为“巴勃罗的朋友”。 ...
已发布的练习参考了BOOTCAMP的培训-NodeJS开发人员-JavaScript疑难解答( ) 挑战说明: 您有义务测试计算器,以便它们仅执行乘法和除法运算。 此外,在每个操作中要输入的术语(显示屏上显示的数字将被相除或相乘...
标题 "疑难解答前端" 暗示我们即将探讨的是与前端开发相关的常见问题及解决方案,特别是涉及JavaScript这一重要编程语言的使用。在这个领域,开发者经常遇到各种挑战,如语法错误、浏览器兼容性问题、性能优化等。让...
### 实习二动态网页制作基础与发布中疑难解答 #### 关键知识点解析 在本次实习“动态网页地图制作”的过程中,遇到了一个与`<input type=image>`相关的疑问,特别是关于`image.x`和`image.y`这两个属性的应用,...
PhotoShop常见的50个疑难问题及解答(20211118115316).pdf
这个压缩包中的内容可能包括具体的代码示例、步骤指南和可能的疑难解答,帮助开发者熟练掌握这一技能。在学习过程中,建议结合官方文档和其他相关教程进行深入研究,同时注意实践和测试,以确保理解和应用的正确性。
“在线指导2”可能是一个补充资料,提供了额外的实践项目或疑难解答,帮助学习者巩固所学知识并解决实际问题。 总结PPT则是一个全面回顾,它总结了所有章节的主要知识点,帮助学习者梳理学习路径,确保他们能够系统...
在IT领域,"疑难解答NG"可能是指针对各种复杂问题的高级解决策略,尤其是在网络、系统管理和编程中遇到的问题。这个标题暗示了我们将探讨一种针对技术难题的高级方法论,特别是与HTML相关的技术问题。 HTML...
本文将针对"Cookie疑难解答-案例研究"这一主题,结合相关标签,如HTML、.NET、Windows、ASP.NET、IIS、Visual Studio、WebForms、ASP以及QA和Dev,深入探讨Cookie常见问题及其解决方案。 1. **Cookie不被发送到...
#### 十二、脚本疑难解答 在开发过程中,常见的脚本错误包括语法错误、逻辑错误等。 **解决方法**: - 使用浏览器开发者工具检查错误消息。 - 仔细检查代码,确保语法正确。 - 添加调试代码,如`console.log()`,...
【标题】:“疑难解答:带有最终响应和打字稿的最终顶峰旅行应用程序的移动覆盖范围” 这篇描述可能指的是一个关于移动应用开发的项目,特别是针对“最终顶峰旅行应用程序”的问题解决过程。这个应用程序可能是一款...