你真的已经搞懂JavaScript了吗?

题目一:

if (!("a" in window)) {
    var a = 1;
}
alert(a);

题目二:

var a = 1,
    b = function a(x) {
        x && a(--x);
    };
alert(a);

题目三:

function a(x) {
    return x * 2;
}
var a;
alert(a);

题目四:

function b(x, y, a) {
    arguments[2] = 10;
    alert(a);
}
b(1, 2, 3);

题目五:

function a() {
    alert(this);
}
a.call(null);

请不要借助任何帮助工具,心算答案。答案在下面。

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

答案:

题目1

if (!("a" in window)) {
    var a = 1;
}
alert(a);

代码含义:如果window不包含属性a,就声明一个变量a,然后赋值为1。

你可能认为alert出来的结果是1,然后实际结果是“undefined”。要了解为什么,需要知道JavaScript里的3个概念。

首先,所有的全局变量都是window的属性,语句 var a = 1;等价于window.a = 1; 你可以用如下方式来检测全局变量是否声明:

"变量名称" in window

第二,所有的变量声明都在范围作用域的顶部,看一下相似的例子:

alert("a" in window);
var a;

此时,尽管声明是在alert之后,alert弹出的依然是true,这是因为JavaScript引擎首先会扫墓所有的变量声明,然后将这些变量声明移动到顶部,最终的代码效果是这样的:

var a;
alert("a" in window);

这样看起来就很容易解释为什么alert结果是true了。

第三,你需要理解该题目的意思是,变量声明被提前了,但变量赋值没有,因为这行代码包括了变量声明和变量赋值。

你可以将语句拆分为如下代码:

var a;    //声明
a = 1;    //初始化赋值

当变量声明和赋值在一起用的时候,JavaScript引擎会自动将它分为两部以便将变量声明提前,不将赋值的步骤提前是因为他有可能影响代码执行出不可预期的结果。

所以,知道了这些概念以后,重新回头看一下题目的代码,其实就等价于:

var a;
if (!("a" in window)) {
    a = 1;
}
alert(a);

这样,题目的意思就非常清楚了:首先声明a,然后判断a是否在存在,如果不存在就赋值为1,很明显a永远在window里存在,这个赋值语句永远不会执行,所以结果是undefined。

提前这个词语显得有点迷惑了,你可以理解为:预编译。

题目2

var a = 1,
    b = function a(x) {
        x && a(--x);
    };
alert(a);

这个题目看起来比实际复杂,alert的结果是1;这里依然有3个重要的概念需要我们知道。

首先,在题目1里我们知道了变量声明在进入执行上下文就完成了;第二个概念就是函数声明也是提前的,所有的函数声明都在执行代码之前都已经完成了声明,和变

量声明一样。澄清一下,函数声明是如下这样的代码:

function functionName(arg1, arg2){
    //函数体
}

如下不是函数,而是函数表达式,相当于变量赋值:

var functionName = function(arg1, arg2){
    //函数体
};

澄清一下,函数表达式没有提前,就相当于平时的变量赋值。

第三需要知道的是,函数声明会覆盖变量声明,但不会覆盖变量赋值,为了解释这个,我们来看一个例子:

function value(){
    return 1;
}
var value;
alert(typeof value);    //"function"

尽快变量声明在下面定义,但是变量value依然是function,也就是说这种情况下,函数声明的优先级高于变量声明的优先级,但如果该变量value赋值了,那结果就完全不一样了:

function value(){
    return 1;
}
var value = 1;
alert(typeof value);    //"number"

该value赋值以后,变量赋值初始化就覆盖了函数声明。

重新回到题目,这个函数其实是一个有名函数表达式,函数表达式不像函数声明一样可以覆盖变量声明,但你可以注意到,变量b是包含了该函数表达式,而该函数表达式的名字是a;不同的浏览器对a这个名词处理有点不一样,在IE里,会将a认为函数声明,所以它被变量初始化覆盖了,就是说如果调用a(–x)的话就会出错,而其它浏览器在允许在函数内部调用a(–x),因为这时候a在函数外面依然是数字。基本上,IE里调用b(2)的时候会出错,但其它浏览器则返回undefined。

理解上述内容之后,该题目换成一个更准确和更容易理解的代码应该像这样:

var a = 1,
    b = function(x) {
        x && b(--x);
    };
alert(a);

这样的话,就很清晰地知道为什么alert的总是1了。

题目3

function a() {
    return 1 ;
}
var a;
alert(a);

这个题目比较简单:即函数声明和变量声明的关系和影响,遇到同名的函数声明,不会重新定义

题目4

function b(x, y, a) {
    arguments[2] = 10;
    alert(a);
}
b(1, 2, 3);

关于这个题目,ECMAsCRIPT 262-3的规范有解释的。

活动对象是在进入函数上下文时刻被创建的,它通过函数的arguments属性初始化。arguments属性的值是Arguments对象.

关于 Arguments对象的具体定义,看这里:ECMAScript arguments 对象

题目5

function a() {
    alert(this);
}
a.call(null);

这个题目可以说是最简单的,也是最诡异的!关于这个题目,我们先来了解2个概念。

这个问题主要考察 Javascript 的 this 关键字,具体看这里:

关于Javascript语言中this关键字的用法

关于 a.call(null);  根据ECMAScript262规范规定:如果第一个参数传入的对象调用者是null或者undefined的话,call方法将把全局对象(也就是window)作为this的值。所以,不管你什么时候传入null,其this都是全局对象window,所以该题目可以理解成如下代码:

function a() {
    alert(this);
}
a.call(window);

所以弹出的结果是[object Window]就很容易理解了。

—————

总结:

这5个题目虽然貌似有点偏,但实际上考察的依然是基本概念,只有熟知了这些基本概念才能写出高质量代码。

大致就这些,抛砖引玉。

时间: 2024-10-31 23:35:22

你真的已经搞懂JavaScript了吗?的相关文章

五个小例子教你搞懂 JavaScript 作用域问题

众所周知,JavaScript 的作用域和其他传统语言(类C)差别比较大,掌握并熟练运用JavaScript 的作用域知识,不仅有利于我们阅读理解别人的代码,也有助于我们编写自己的可靠代码. 下面笔者将使用五个小例子来给大家分析下 JavaScript 的作用域要注意的问题. 感谢 例子的来源 (这5个例子我做错了2个 [嘿嘿,尽情鄙视吧],笔者就是要 死磕自己,奉献大家!) 先给出五个例子: 每个例子旁边都会给出答案的链接,如果你全部都正确了,你可以忽略这篇短文,并深深的鄙视下笔者. 例一:

搞懂 JavaScript 继承原理

在理解继承之前,需要知道 js 的三个东西: 什么是 JS 原型链 this 的值到底是什么 JS 的 new 到底是干什么的 1. 什么是 JS 原型链? 我们知道 JS 有对象,比如 var obj = { name: "obj" }; 我们通过控制台把 obj 打印出来: 我们会发现 obj 已经有几个属性(方法)了.那么问题来了:valueOf / toString / constructor 是怎么来?我们并没有给 obj.valueOf 赋值呀. 上面这个图有点难懂,我手画

【转】一张图轻松搞懂javascript event对象的clientX,offsetX,screenX,pageX区别

转自:http://www.2cto.com/kf/201409/333401.html 总是会被javascript的event对象的clientX,offsetX,screenX,pageX 弄得头晕,于是决定做个图来区分一下(画得我手那个酸呀....) 先总结下区别: event.clientX.event.clientY 鼠标相对于浏览器窗口可视区域的X,Y坐标(窗口坐标),可视区域不包括工具栏和滚动条.IE事件和标准事件都定义了这2个属性 event.pageX.event.pageY

轻松搞懂javascript event对象的clientX,offsetX,screenX,pageX区别

总是会被javascript的event对象的clientX,offsetX,screenX,pageX 弄得头晕 先总结下区别: event.clientX.event.clientY 鼠标相对浏览器窗口可视区域的X,Y坐标(窗口坐标),可视区域不包括工具栏和滚动条.IE事件和标准事件都定义了这2个属性 event.pageX.event.pageY 类似于event.clientX.event.clientY,但它们使用的是文档坐标而非窗口坐标.这2个属性不是标准属性,但得到了广泛支持.IE

一分钟搞懂 JavaScript this 指向问题

关于Javascript的this指向问题,网络上有很多分析文章,写的很好,比如这里和这里 我这里做一个简单的总结. 箭头函数的 this 箭头函数内的this指向外层函数定义时所在的作用域.如果没有外层函数,则指向全局作用域. ES5 的 this ES5的this指向当前函数执行时所在的上下文. 注意:调用普通函数,它的this指向全局作用域. 什么是普通函数? // 这就是 funtion a() {console.log(this)} 原文地址:https://www.cnblogs.c

一张图轻松搞懂javascript event对象的clientX,offsetX,screenX,pageX区别

先总结下区别: event.clientX.event.clientY 鼠标相对于浏览器窗口可视区域的X,Y坐标(窗口坐标),可视区域不包括工具栏和滚动条.IE事件和标准事件都定义了这2个属性 event.pageX.event.pageY 类似于event.clientX.event.clientY,但它们使用的是文档坐标而非窗口坐标.这2个属性不是标准属性,但得到了广泛支持.IE事件中没有这2个属性. event.offsetX.event.offsetY 鼠标相对于事件源元素(srcEle

JavaScript内部原理实践——真的懂JavaScript吗?(转)

通过翻译了Dmitry A.Soshnikov的关于ECMAScript-262-3 JavaScript内部原理的文章, 从理论角度对JavaScript中部分特性的内部工作机制有了一定的了解.但是,邓爷爷说过:“实践才是检验真理的唯一标准”.所以,我打算通过从内部原理来解释一些经常在笔试或者面试中遇到的关于JavaScript语言层面的题目来进一步学习和掌握JavaScript内部工作原理. 那么,首先就是要去找那些题目,google了一圈终于找到了来自Dmitry Baranovskiy的

搞懂树状数组(转)

ps:转的,这篇写的不错. 写下这个标题,其实心里还是没底的,与其说是写博帖,不如说是做总结.第一个接触树状数组还是两年前,用什么语言来形容当时的感觉呢?……太神奇了!真的,无法表达出那种感觉,她是那么的优雅,10行不到的代码,却把事情干的如此出色!没有了解她原理的前提下即使把代码倒背如流也理解不了!其中,我就是一直没搞懂地在使用她.时隔两年,又无意遇到了她,可能是两年的代码经验的积累,有了些新的认识,可以自信的说理解了吧!下面我争取用自己的方式让更多人明白她,而不是背诵她.为了更方便的说明,文

彻底弄懂 JavaScript 执行机制

本文的目的就是要保证你彻底弄懂javascript的执行机制,如果读完本文还不懂,可以揍我. 不论你是javascript新手还是老鸟,不论是面试求职,还是日常开发工作,我们经常会遇到这样的情况:给定的几行代码,我们需要知道其输出内容和顺序.因为javascript是一门单线程语言,所以我们可以得出结论: javascript是按照语句出现的顺序执行的 看到这里读者要打人了:我难道不知道js是一行一行执行的?还用你说?稍安勿躁,正因为js是一行一行执行的,所以我们以为js都是这样的: let a