【翻译】Zakas解答Baranovskiy的JavaScript测验题
原文:http://www.nczonline.net/blog/2010/01/26/answering-baranovskiys-javascript-quiz/
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
上周我在Dmitry Baranovskiy的一篇博客里看到一些关于JavaScript的测验题,博客标题叫"你觉得你真的懂JavaScript吗"。像这种类型的题目,你只要回答一个问题:结果是什么?这些例子用来测试JavaScript引擎一些诡异的特性行为。我以前见过这种类型的测验题,有些人会拿来当做面试题,我认为这不仅不尊重面试者,而且毫无用处。你不会每天都遇到这种类型的怪异问题,所以用这样的问题来考察面试者,当做是最低的指标,就跟面试空姐时叫人家解释飞机飞行原理一样没用。
不过我仍然喜欢这些例子,它们可以很好的用来解释JavaScript语言的有趣现象,下面我会深入分析每个测验题是怎么执行的。
测验1
if(!('a' in window)){
var a=1;
}
alert(a);
这段看起来很怪的代码好像是说:"如果window对象没有属性'a',那么就定义一个给它,并把它的值设置为1"。你可能会以为弹出的结果是1,事实上结果是'undefined',要了解原因的话,你需要知道关于JavaScript的三件事。
首先,所有全局变量都是window对象的一个属性,你写'var a=1;'相当于写'window.a=1;',你可以用下面的方法来检测一个全局变量是否声明了:
'variable-name' in window
其二,所有变量声明都会提前到当前作用域的顶端,请看下面的代码:
alert('a' in window);
var a;
上面的结果是'true',虽然变量声明语句在检测语句之后,但是因为JavaScript引擎首先会扫描变量声明,然后把它们移动当前作用域的顶端,所以最终解析出来的代码如下:
var a;
alert('a' in window);
看这段代码,更容易理解为什么结果是'true'。
第三,你需要知道,虽然变量声明提前了,但是赋值操作却没有。下面的语句是变量声明和赋值在一起的:
var a=1;
你可以把它分为声明和赋值两部分:
var a;//声明
a=1; //赋值
当JavaScript引擎发现声明和赋值是一起时,就会自动把它们分开,然后把声明提前。为什么赋值不提前呢?因为在代码执行的时候这会影响变量的值并且产生不可预期的结果。
现在了解了JavaScript的这些特性,重新看下案例1的代码,实际上被解析成了如下代码:
var a;
if(!('a' in window)){
a=1;
}
alert(a);
看这段代码后,答案就显而易见了。首先声明变量'a',接着是if语句,判断说:"如果'a'没有声明,那么就初始化为1"。当然了,条件判断不可能为'true',所以变量的值还是默认值'undefined'。
测验2
var a=1,
b=function a(x){
x&&a(--x);
};
alert(a);
这段代码比表面上看起来更复杂,以上结果为'1',就是初始化的值。但是为什么会这样?这仍然需要了解JavaScript的三个要点。
第一点就是声明提前,在案例1已经讲过了。
第二点,函数声明提前。所有的函数声明跟变量声明一样,都会被提前至当前作用域的顶端,但是要明白函数声明是这样子的:
function functionName(arg1,arg2){
//function body
}
以下不是函数声明,而是函数表达式,相当于变量赋值:
var functionName=function(arg1,arg2){
//function body
}
要清楚,函数表达式是没有被提前的。这一点现在要明白,因为变量的初始化和赋值位置的改变,都会显著的影响程序的执行。
第三点,函数声明的优先级高于变量声明,但是不会覆盖变量的赋值。为了理解这句话的意思,请看下面的代码:
function value(){
return 1;
}
var value;
alert(typeof value); //'function'
即使变量声明在函数声明之后,但变量'value'的值依然是一个'function',在这种情况下,函数声明的优先级更高。但是,如果变量初始化了,那么结果又会不同。
function value(){
return 1;
}
var value=1;
alert(typeof value); //number
现在变量的值被设置为1,变量的初始化会覆盖函数声明。
回到测验2的代码来,那个函数是一个有名字的函数表达式。带有名字的函数表达式不是函数声明,所以不会覆盖变量声明。然而,你要注意变量b的值是函数表达式,而函数表达式的函数名是a,不同的浏览器处理方法不同。IE浏览器会把它当做是函数声明,所以会被变量的初始化覆盖,那么调用a(--x)就会造成错误。至于其它浏览器,虽然a在函数外还是一个数字,但在函数内还是可以调用a(--x)。基本上,在IE中调用b(2)会抛出JavaScript异常,但在其它浏览器会返回'undefined'。
理解上述内容之后,把题目换成更准确和更容易理解的版本:
var a=1,
b=function(x){
x&&b(--x);
};
alert(a);
这样的话,就很清楚了,变量a的值总是1。
测验3
function a(x){
return x*2;
}
var a;
alert(a);
如果你理解了上面那个例子,那么这道题就很简单了。有一点要记住的就是函数声明总是优先于变量声明,也就是说变量声明语句被忽略了,除非遇到变量初始化。这里没有初始化,所以弹出结果是函数a的源代码。
同名的变量不会重复声明。
测验4
function b(x,y,a){
arguments[2]=10;
alert(a);
}
b(1,2,3);
这段代码更好理解,只要判断结果是'3'或者'10'就可以了,所有的浏览器结果都是10。只要知道一个概念你就知道为什么结果是这样了,就是ECMA-262第三版10.1.8章节关于arguments对象的描述:
For each non-negative integer, arg, less than the value of the length property, a property is created with name ToString(arg) and property attributes { DontEnum }. The initial value of this property is the value of the corresponding actual parameter supplied by the caller. The first actual parameter value corresponds to arg = 0, the second to arg = 1, and so on. In the case when arg is less than the number of formal parameters for the Function object, this property shares its value with the corresponding property of the activation object. This means that changing this property changes the corresponding property of the activation object and vice versa.
太TM难理解了:对于每个非负整数,传进来的参数个数比'length'属性值还小,那么就会使用ToString(arg)创建一个属性和属性变量'{DontEnum}'。这个属性的初始值为调用者传进来的参数个数。第一个实参对应arg=0,第二个对应arg=1,依次类推。当实参的个数小于形参时,这个属性就会和激活的对象共享值。这意味着,改变属性的值,相应的激活对象的属性也会改变,反过来也一样。
简而言之,arguments对象中的每个实体都是形参的一个拷贝,虽然值是共享了,但是内存空间没有共享。两个内存空间由JavaScript引擎来保存同步,也就是说arguments[2]和a总是拥有相同的值,所以最终a的值是10。
测验5
function a(){
alert(this);
}
a.call(null);
我认为这是5道题中最简单的了,只需要理解两个概念。
首先,你要知道this的值是怎么定义的,当一个方法被对象调用时,this就指向该对象了,例如:
var object={}
method:function (){
alert(this==object) //true
}
;
object.method();
在这段代码里,当object.method()被调用的时候,this就指向了object。在全局作用域,this相当于window(也就是全局对象),所以如果一个function的定义不是属于一个对象属性的时候(也就是单独定义的函数),函数内部的this等于window,例如:
function method(){
alert(this===window); //true
}
method();
在这里,this指向的是全局对象window。
了解了上述概念之后,我们再来了解一下call()方法是干什么的。call方法可以让一个方法当做是其它对象的方法被调用,第一个参数是调用者,其它参数是被调用的方法的实参,例如:
function method(){
alert(this===window);
}
method(); //true
method.call(document); //false
这里因为method()方法被document调用,所以结果是'false'。
一个有趣的问题是当传入call方法的一个参数是null会发生什么,以下是ECMA-262第三版对这个问题的描述:
如果传入的第一个参数是'null'或'undefined',那么call方法会把全局对象(window)作为this的值,否则this的值就等于ToObject(thisArg)。
所以无论什么时候call()和apply()方法第一个参数传入null,其this的值都是window。根据以上知识,将案例5代码重新改为如下更容易理解:
function a(){
alert(this);
}
a.call(window);
很明显结果就是[object Window]。
总结
Dmitry把这些有趣的测验题放一起,你可以从中学习JavaScript的一些怪异特性,我希望这篇文章可以帮助每个人理解这些例子运行时所必须知道的细节,更重要的是,理解为什么会这样子。重申一下,我反对使用这类测验题当做面试题来考验面试者的能力,我认为这在实际中是没有多大用处的(如果你想知道我是如何面试前端工程师的,请看我前一篇文章)。
【翻译】Zakas解答Baranovskiy的JavaScript测验题的更多相关文章
- 六道JavaScript测验题
1.找出数字数组中最大的元素(使用Match.max函数) var a=[123,23432,345,3,34]; console.log(Math.max.apply(null,a)); 2.转化一 ...
- 小试牛刀3之JavaScript基础题
JavaScript基础题 1.让用户输入两个数字,然后输出相加的结果. *prompt() 方法用于显示可提示用户进行输入的对话框. 语法: prompt(text,defaultText) 说明: ...
- 小试牛刀2:JavaScript基础题
JavaScript基础题 1.网页中有个字符串“我有一个梦想”,使用JavaScript获取该字符串的长度,同时输出字符串最后两个字. 答案: <!DOCTYPE html PUBLIC &q ...
- JavaScript算法题之–随机数的生成
JavaScript算法题之–随机数的生成 需求描述:从一组有序的数据中生成一组随机并且不重复的数,类似于简单的抽奖程序的实现. 先来生成一个有序的数组: 1 var arr = [], 2 ...
- 44个 Javascript 变态题解析 (下)
承接上篇 44个 Javascript 变态题解析 (上) 第23题 [1 < 2 < 3, 3 < 2 < 1] 这个题也还可以. 这个题会让人误以为是 2 > 1 & ...
- FCC上的javascript算法题之中级篇
FCC中的javascript中级算法题解答 中级算法的题目中用到了很多js的知识点,比如迭代,闭包,以及对json数据的使用等等,现在将自己中级算法的解答思路整理出来供大家参考讨论.欢迎大家提出新的 ...
- USACO翻译:USACO 2014 MARCH Silver三题
USACO 2014 MARCH 一.题目概览 中文题目名称 农田灌溉 懒牛 牛叫 英文题目名称 irrigation lazy mooomoo 可执行文件名 irrigation lazy mooo ...
- USACO翻译:USACO 2014 US Open 三题
USACO 2014 US Open 一.题目概览 中文题目名称 牧场装饰 里程表 牛像展览 英文题目名称 decorate odometer fairphoto 可执行文件名 decorate od ...
- 汤姆大叔的6道javascript编程题题解
看汤姆大叔的博文,其中有篇(猛戳这里)的最后有6道编程题,于是我也试试,大家都可以先试试. 1.找出数字数组中最大的元素(使用Math.max函数) var a = [1, 2, 3, 6, 5, 4 ...
随机推荐
- .NET书籍推荐
任何语言的学习,要快速掌握,不在看书,而在实践.——题记 .NET技术从1.1发展到2.0,内核基本完善,从.NET 2.0开始学习是个明智的选择.而NET 3.5以及即将推出的.NET 4.0所新加 ...
- WCF + EF 遇到的问题
此文承接上一文章 基于WCF的API实现 http://www.cnblogs.com/heyixiaoran/p/4000695.html 由于上一次Entity部分没怎么写,到Services里识 ...
- 1103. Integer Factorization (30)
The K-P factorization of a positive integer N is to write N as the sum of the P-th power of K positi ...
- Vim配置IDE开发环境
我的vim IDE界面: 1.安装Vim和Vim基本插件首先安装好Vim和Vim的基本插件.这些使用apt-get安装即可:lingd@ubuntu:~/arm$sudo apt-get instal ...
- [转]win7+ubuntu 13.04双系统安装方法
win7+ubuntu 13.04双系统安装方法 http://jingyan.baidu.com/article/60ccbceb18624464cab197ea.html 当需要频繁使用ubunt ...
- Python环境搭建和开发工具的配置
本文转自http://237451446.blog.51cto.com/2307663/766781 因为要学习python了,第一步当然是环境搭建和开发工具的配置了,下边开始了. 我的开发环境是在w ...
- Careercup - Facebook面试题 - 4907555595747328
2014-05-02 07:49 题目链接 原题: Given a set of n points (coordinate in 2d plane) within a rectangular spac ...
- System.IO.StreamWriter
string path = @"D:\a.txt"; System.IO.StreamWriter swOut = new System.IO.StreamWriter(path, ...
- 3240: [Noi2013]矩阵游戏
Description 婷婷是个喜欢矩阵的小朋友,有一天她想用电脑生成一个巨大的n行m列的矩阵(你不用担心她如何存储).她生成的这个矩阵满足一个神奇的性质:若用F[i][j]来表示矩阵中第i行第j列的 ...
- 以“图片渐入渐出”为例讲述jQuery插件的具体实现
首先声明,此代码以网友“斯迈欧”原创作为此例的讲解: 在这之前我们先看看我们要做的效果是什么样的: 解析下面的样式:我们要图片在过“一定时间”后自动切换,在右下角处有小方块似数字1,2,3,4,这些数 ...