基础
-
什么是变量提升(函数提升)?
无论变量或函数写在何处,在编译阶段均会将它们提前到作用域的最顶端。
-
JavaScript解释器会自动提升哪些组件?
- 变量。
- 使用function关键字声明的、具名的函数。
-
JavaScript解释器不自动提升哪些组件?
使用函数表达式声明的函数。
如:
javascriptvar fn = function () {} -
变量提升(函数提升)有什么优缺点?
-
优点:
-
提高执行效率。
避免每次执行前的检查和编译,同时可直接为函数分配栈空间。
-
提高代码容错性。
对于(变量或函数的)调用先于声明的情况,JavaScript解释器的自动提升可避免执行时找不到(相应的变量或函数)而报错的问题。
-
-
缺点:
-
变量值被覆盖风险。
JavaScript解释器的自动提升特性具有两面性。其尽管能通过自动提升的方式来避免在程序运行时找不到相应变量或函数而报错的问题,但不加管控的肆意提升,也会因将变量值错误地覆盖而导致程序出现逻辑性错误。
-
内存泄漏风险。
如:
在传统for循环代码块中,迭代计数器变量应在for循环代码块整体迭代结束后一并销毁。但由于未使用闭包限定其作用域的缘故,JavaScript解释器会在编译时将其自动提升到作用域顶端,而并未被销毁,于是导致了内存泄漏。
-
-
-
如何禁止变量提升?
使用let或const关键字来声明变量。
-
如何解决因JavaScript解释器在编译时进行的自动变量提升(函数提升)而导致在传统for循环中的迭代计数器变量无法被销毁的问题?
使用闭包模拟块级作用域。
-
块级作用域是基于什么原理实现的?
利用JavaScript变量环境中词法环境的栈结构。
-
说说一个JavaScript函数在运行时的上下文结构?

注意事项:
- JavaScript函数执行时,会创建同时创建变量环境和词法环境(本质为一个栈(后进先出表))。
- 当块内的代码执行完毕后,位于词法环境栈顶的let变量将依次弹出,最终只剩下块外的let变量。
-
什么是暂时性死区?
在块级作用域中以先调用、后声明的方式使用let或const变量时,在这些变量声明之前的代码所构成的区域。
示例代码如下:
以下示例代码中的第5、6行,为暂时性死区:
javascriptvar name = 'JavaScript'; { // 暂时性死区开始位置 name = 'CSS'; // 位于暂时性死区的代码行 console.log(name); // 位于暂时性死区的代码行 // 暂时性死区结束位置 let name; // 后声明了一个let变量 } -
产生暂时性死区的原因是什么?
当程序执行到新作用域时,尽管let或const变量在此作用域中被创建,但还未进行词法绑定(即:在JavaScript变量环境中的词法环境内,这些变量还没有对应的值),此时访问这些变量时就会报错。
-
var、let和const变量的异同点?

-
const变量的不可变,是指什么的不可变?
其仅保证指针不可变,并不保证指针指向堆上存储的实际值不可变。
-
什么是闭包?
参见:JavaScript高级程序设计 读书笔记 - 第五章 引用类型 - 笔记 - 匿名函数和闭包的区别 - 关于闭包。
-
ECMAScript和JavaScript是什么关系?
ECMAScript是一套标准,JavaScript是此标准的其中一个具体实现。
-
JavaScript的基本数据类型有哪些?
5个基本数据类型:String、Boolean、Number、Undefined、Null。
1个复杂数据类型:Object。

-
基本数据类型中,Null和Undefined的异同点?

-
创建对象的方式有哪些?
-
通过构造函数。
-
通过字面量。
参见:JavaScript高级程序设计 读书笔记 - 第五章 引用类型 - 笔记 - 关于Object类型 - 创建Object对象的方式。
-
-
访问对象属性的方式有哪些?
- 使用点符号(.)。
- 使用方括号([])。 参见:JavaScript高级程序设计 读书笔记 - 第五章 引用类型 - 笔记 - 关于Object类型 - 创建Object对象的方式。
-
是否可以将匿名函数指向变量,并将其作为参数传递给另外的函数?
可以。
这种函数为回调函数。
-
什么是回调函数?
作为参数传递给另外的函数、且在另外的函数执行结束后才执行的一种普通的函数。
因为在JavaScript中规定,函数也是对象。故函数可接受函数作为参数来传入。
-
JavaScript中函数的参数对象是什么?
arguements对象。
其内存储了当前函数的参数列表。
-
JavaScript中函数参数的本质是什么?
是数组。
但不是Array的实例。
-
什么是类型化语言?
一个值,与其本身真正的类型有关(即:动态有关性),而与声明其定义的类型无关(即:静态无关性)。
例:
在JavaScript中,即使所有变量都用var、let或const关键字来声明,但在运行时编译器总能自动推断其的真实数据类型。故其为类型化语言。
而在早期版本的Java中,必须使用确切的数据类型来声明变量,无法自动推断类型。故其不是类型化语言。
-
比较符号==和===的区别?
==为相等比较符,若等号两边的数据类型不同,则先转为一致的数据类型,再比较值。
===为全等比较符,等号两边的数据直接比较值。
-
HTML元素中innerHTML和innerText属性的区别?
innerHTML属性获取的是同时含HTML标签和所有文本的目标DOM节点的字符串。
innerText属性获取的是仅含所有文本的、目标DOM节点的字符串。
-
什么是事件冒泡?
是DOM API中事件的一种传播方式。
-
NaN是什么?
即Not A Number,表示非合法数字,用于引用特殊的、非数字的值。
-
什么是严格模式?
即在更为严格的语法规范下执行JavaScript。
-
严格模式限制很多,但为什么还要使用它?
- 减少怪异行为。
- 提高编译效率和运行速度。
- 确保兼容性。
- 确保安全地运行。
-
call()和apply()的异同点?

参见:JavaScript高级程序设计 读书笔记 - 第五章 引用类型 - 关于this - this+能改变执行时作用域方法的常用方法。
-
什么是IIFE?
即:立即调用函数表达式(I mmediately I nvoked F unction E xpressions)。
指在函数定义后就立即执行的一类函数。
示例代码如下:
javascript(()=>{ // 业务逻辑代码 })() -
JavaScript支持异步执行吗?
仅在ECMAScript 2017(也称ES8)及以后的标准支持。
其引入了async和await关键字,可减少显式声明Promise函数的复杂性。
-
setTimeout()是异步代码还是同步代码?setTimeout()是异步执行还是同步执行?它在JavaScript中的执行时机是什么?
-
setTimeout()理论为异步代码。
-
setTimeout()实际为同步执行。
因为JavaScript引擎中的线程是基于事件驱动且单线程执行的,无法同时并发执行。
-
当JavaScript引擎中没有任何要执行的同步代码后,才会执行setTimeout()这种"异步"代码。
-
-
setTimeout(function(){}, 0)会在程序启动后立即执行吗?它能保证执行时机吗?其能否立即执行取决于什么?
-
不能确保立即执行。
-
不能确保执行时机。
setTimeout(0)仅表示JavaScript引擎在执行代码时将其立即插入到执行队列中,但并非立即执行。其需等到JavaScript引擎将位于其之前队列中的代码都执行完成后才会执行它。
-
立即执行与否,取决于JavaScript引擎的繁忙程度。
-
-
setInterval()和setTimeout()的异同点?

集合
-
如何创建数组?
-
使用方括号([])。
示例代码如下:
javascriptlet colors = ['red', 'yellow', 'blue'] // 方式1:通过方括号。输出为:Steve Jrong, 25, true -
使用Array类。
示例代码如下:
javascriptlet createArrayMethod2 = new Array(3) // 方式2:通过实例化Array类 createArrayMethod2.push('Steve Jrong') createArrayMethod2.push(25) createArrayMethod2.push(true) createArrayMethod2.push(true) // 输出为:Steve Jrong, 25, true
-
-
如何清空数组?

-
数组对象的reduce()是做什么的?
用于合计数组内所有项的值并返回单个结果。
参见:JavaScript高级程序设计 读书笔记 - 第五章 引用类型 - 笔记 - 关于Array类型 - 数组的常用属性及方法。
浏览器
- Cookie、LocalStorage和SessionStorage的异同点?

- window和document这两个浏览器内置对象的区别?
- window为JavaScript的全局对象,其包含变量、函数、history和location。
- document位于window对象内,为其内的一个属性。
运算题
-
下列代码执行后,会输出什么?为什么?它最终所执行的实际代码是怎样的?
javascriptconsole.log(num) var num = 1-
输出:undefined。
-
原因:全局变量num的声明被提到全局作用域的顶端。
-
最终所执行的实际代码:
javascriptvar num // 因num属变量,故遵循变量提升规则,JS解释器会将其自动提升到作用域顶端 console.log(num) // 因变量声明但未赋值时的值,就是undefined,故这里输出undefined num = 1 // 方法已经调用过了,失败了,这时JS解释器再去加载,已经没有意义了
-
-
下列代码执行后,会输出什么?为什么?它最终所执行的实际代码是怎样的?
javascriptfunction hello() { console.log(num); var num = 1; } (() => { hello() })()-
输出:undefined。
-
原因:局部变量num的声明被提到全局作用域的顶端。
-
最终所执行的实际代码:
javascriptfunction hello() { var num // 因num属变量,故遵循变量提升规则,JS解释器会将其自动提升到作用域顶端 console.log(num) // 因变量声明但未赋值时的值,就是undefined,故这里输出undefined num = 1 // 方法已经调用过了,失败了,这时JS解释器再去加载,已经没有意义了 } (() => { hello() })()
-
-
下列代码执行后,会输出什么?为什么?它的实际代码是什么样的?
javascript(() => { f1() })() var f1 = function () { console.log(100) }-
输出:报找不到f1()函数的错,且无输出。
-
原因:使用表达式的方式声明的函数,在方法调用先于其声明时,JavaScript解释器将不自动提升。
-
最终所执行的实际代码:
javascriptf1() // 因f1属变量,故遵循变量提升规则,JS解释器会将其自动提升到作用域顶端 (() => { f1() // 而调用此方法时,因JS不会对基于函数表达式的函数声明进行提升,所以会报错找不到f1()函数 })() var f1 = function () { // 方法已经调用过了,失败了,这时JS解释器再去加载,已经没有意义了 console.log(100) }
-
-
下列代码执行后,会输出什么?为什么?它最终所执行的实际代码是怎样的?
javascript(() => { func1() })() function func1() { console.log(100) }-
输出:100。
-
原因:使用表达式的方式声明的函数,在方法调用先于其声明时,JavaScript解释器将不自动提升。
-
最终所执行的实际代码:
javascriptfunction func1() { // 基于具名函数的声明,实际代码中,JS解释器会将其自动提升到作用域顶端 console.log(100) } (() => { func1() // 此时,方法已提前加载到JS解释器中,即可成功调用 })()
-
-
下列代码执行后,会输出什么?为什么?它的实际代码是什么样的?
javascriptvar name = 'JavaScript' function showName() { console.log(name) if (0) var name = 'CSS' } (() => { showName() })()-
输出:undefined。
-
原因:使用var关键字声明的变量,会被JavaScript解释器自动提升,从而导致变量被覆盖。
-
最终所执行的实际代码:
javascriptvar name = 'JavaScript' function showName() { /** * 此处输出undefined * showName()函数内,具有函数作用域。而根据JS变量提升的规则,无论变量声明在何处,JS解释器都将其自动提升到作用域顶端。 * 而变量name的作用域顶端,就是showName()函数的第一行。 */ // 实际的JS代码 // var name; console.log(name) if (0) var name = 'CSS' // 因为这里使用了var关键字声明了变量,故实际代码中,会多出来第11行的代码,所以最终输出为undefined } (() => { showName() })()
-
-
NaN == NaN的结果是什么?为什么?
- 结果:false。
- 原因:NaN与包括自己在内的任何值均不等。
-
Number.NaN == NaN的结果是什么?为什么?
- 结果:false。
- 原因:NaN与包括自己在内的任何值均不等。
-
2 + 5 + '3'的结果是什么?为什么?
- 结果:73。
- 原因:2+5为求和运算;7+'3'为字符串拼接。
-
下列代码执行后,会输出什么?为什么?
javascript(() => { delete X.foo console.log(X.foo) X.fn('张三') })() var X = { foo: '1', fn: function(arg) { console.log(`My name is ${arg}.`) } }- 输出:
- undefined。
- My name is 张三。
- 原因:
- delete关键字删除了对象X中的foo属性,故再次输出为undefined。
- 对象X中的fn()并不受影响,正常输出。
- 输出:
-
下列代码执行后,会输出什么?为什么?
javascript(() => { 'use strict'; var a = b = 5; console.log(`a value is ${a}.`); console.log(`b value is ${b}.`); })()- 输出:报变量b未定义的错误。
- 原因:在严格模式下必须明确声明和引用全局变量。
-
上述代码应该怎么改?
将 var a = b = 5; 改为 var a = window.b = 5; 即可。
-
\] == false的结果是什么?为什么? 1. 结果:true。 2. 原因:使用相等符(==)将任意值与布尔值比较,均会先转换为Number类型后再比较。
-
\] === false的结果是什么?为什么? 1. 结果:false。 2. 原因:使用全等符(===)做比较,是直接比较。
-
下列代码执行后,会输出什么?为什么?
javascript(() => { obj.fn() })() var obj = { foo: 'bar', fn: function() { var self = this console.log(this.foo) console.log(self.foo) (function() { console.log(this.foo) console.log(self.foo) })() } }-
输出:bar、bar、undefined、bar。
-
原因:
javascriptvar obj = { foo: 'bar', fn: function() { var self = this // 变量self指向了obj对象 /** * 输出:bar。 * 因为此处的this,位于对象obj内的函数中,并且是由对象obj调用的。故这里的this,指向对象obj。 */ console.log(this.foo) console.log(self.foo) // 输出:bar。这里的self,指向对象obj。 (function() { /** * 输出:undefined。 * 这里的this,指向window。 * 因为在此闭包函数的末尾使用了()表示立即调用,这个调用的动作是由全局对象(window)来触发的,故指向window。 * * 而window上没有foo这个变量,故为undefined。 */ console.log(this.foo) /** * 输出:bar。 * 闭包内部可访问到其的外部作用域,即变量self。 * 而变量self又指向对象obj,存在foo属性,故为bar。 */ console.log(self.foo) })() } }注意事项:
谁调用了this,this就指向谁。
-
-
如何实现一个isInteger(x),以判断入参x是否是一个整数?
javascriptfunction isInteger(x) { return parseInt(x, 10) === x // 使用parseInt()函数,将x转换为十进制,再和自身比较 }注意事项:
Number构造函数自带了isInteger(),可方便地判断入参是否为整数。
-
下列代码执行后,会输出什么?为什么?
javascript(() => { console.log(1) setTimeout(function() { console.log(2) }, 1000) setTimeout(function() { console.log(3) }, 0) console.log(4) })()- 输出:1、4、3、2。
- 原因:JavaScript引擎是单线程执行的。
-
下列代码执行后,会输出什么?为什么?
javascriptvar flag = true setTimeout(function() { flag = false }, 1000) while (flag) { } console.log('结束循环。')- 输出:什么也不输出,死循环下去。
- 原因:
由于JS引擎是单线程执行的,且setTimeout()会在JavaScript引擎将所有同步代码全部执行完毕后才会执行,故会先执行第7、9行代码,后执行setTimeout()。
而第7行代码由于先于setTimeout()执行,故变量flag迟迟无法被设置为false,则会永远死循环下去。
-
下列代码执行后,会输出什么?为什么?
javascriptconsole.log(1 + '2' + '2') console.log(1 + +'2' + '2') console.log(1 + -'1' + '2') console.log(+'1' + 1 + '2') console.log('A' - 'B' + '2') console.log('A' - 'B' + 2)-
输出:122、32、02、22、NaN2、NaN。
-
原因:
当数字与字符串混合运算时,将按照以下规则进行:javascriptconsole.log(1 + "2" + "2"); // 122。数字在前,字符串在后时,前面的数字也转为字符串,一起拼接。 console.log(1 + +"2" + "2"); // 32。字符串前有正负号时,会将此字符串转为数值。故1+2=3,3再和最后的2拼接。 console.log(1 + -"1" + "2"); // 02。字符串前有正负号时,会将此字符串转为数值。故-1+1=0,0再和最后的2拼接。 console.log(+"1" + 1 + "2"); // 22。字符串前有正负号时,会将此字符串转为数值。故1+1=2,2再和最后的2拼接。 console.log("A" - "B" + "2"); // NaN2。当字符串的运算结果是非数字时,转为NaN,然后再和最后的2拼接。 console.log("A" - "B" + 2); // NaN。当字符串的运算结果是非数字时,转为NaN。
-