一、setTimeout 输出 10 个 10
for (var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
分析:涉及到 JavaScript 的执行机制,JavaScript 是一个单线程的解释器,setTimeout
是异步执行函数,本质是间隔一定时间将任务添加到任务队列(Event Queue
)中。for
循环作为主线程先执行完毕,按序执行 10 次输出i
,就会输出 10 个 10。
如何实现按序输出?
方法一:闭包
for (var i = 0; i < 10; i++) {
(function (i) {
setTimeout(function () {
console.log(i);
}, 1000);
})(i);
}
方法二:ES6 的let
声明变量
for (let i = 0; i < 10; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}
方法三:使用setTimeout
的第三个参数
for (var i = 0; i < 10; i++) {
setTimeout(function () {
console.log(i);
}, 1000, i);
}
二、意外全局变量
function foo () {
let a = b = 0;
a++;
return a;
}
foo();
typeof a; // => ???
typeof b; // => ???
分析:let a = b = 0;
该语句声明一个局部变量a
,以及全局变量b
。
在foo()
范围或全局范围中都没有声明变量b
。因此 JavaScript 将b = 0
解释为window.b = 0
。
在浏览器中,以上代码片段等效于:
function foo () {
let a;
window.b = 0;
a = window.b;
a++;
return a;
}
foo();
typeof a; // => 'undefined'
typeof b; // => 'number'
typeof a
等于'undefined'
,变量a
存在于foo()
范围内,而在外部范围内不使用。
b
是一个值为 0 的全局变量。
三、数组的 length 属性
const clothes = ['jacket','t-shirt'];
clothes.length = 0;
clothes[0]; // => ???
分析:减少 length 属性的值的结果是删除自己的数组元素。因此,clothes[0]
等于undefined
,因为clothes
数组已被清空。
四、鹰眼测试
// numbers 数组的内容是什么?
const length = 4;
const numbers = [];
for (var i = 0; i < length; i++);{
numbers.push(i + 1);
}
numbers; // => ???
分析:让我们仔细看一下分号;
出现在左大括号{
之前。这个分号创建了一个空语句。上面的代码等效于以下代码:
const length = 4;
const numbers = [];
var i;
for (i = 0; i < length; i++) {
// does nothing
}
{
// a simple block
numbers.push(i + 1);
}
numbers; // => [5]
for()
将i
变量递增到 4,然后 JavaScript 一次进入块{numbers.push(i + 1);}
,将4 + 1
推入数字数组。
因此,numbers
数组的内容为[5]
。
五、自动分号插入
// arrayFromValue() 返回什么值?
function arrayFromValue (item) {
return
[item];
}
arrayFromValue(10); // => ???
分析:很容易错过return
关键字和[item]
表达式之间的换行符。此换行符使 JavaScript 自动在return
和[item]
表达式之间插入分号。
return;
函数内部使其返回undefined
。因此arrayFromValue(10)
的值为undefined
。
六、浮点数计算
0.1 + 0.2 === 0.3 // => ???
分析:浮点数计算会产生误差,并不等于0.3
,结果是false
。
七、变量提升
myVar; // => ???
myConst; // => ???
var myVar = 'value';
const myConst = 3.14;
分析:在声明之前访问myVar
的结果为undefined
。在初始化之前,提升的var
变量具有undefined
的值。
然而,在声明行之前访问myConst
会引发ReferenceError
。const
变量处于临时死区,直到声明行const myConst = 3.14
。
八、下列表达式结果
""==0 // true,0 和空字符串都是 false,值相等
{}==={} // false,引用数据类型,是两个独立的对象
0===0 // true
!"" // true
[]==![] // true,相当于 []==false,类型转换 0==0,所以是 true
1+'1' // 11
九、逻辑运算符
var a=1,b=true,c=2,d=3;
a = b && d && c;
console.log(a); // 2
a && b
:如果 a 是 false,那么 b 不管是 true 还是 false,都返回 false,因此不用判断 b 了,这个时候刚好判断到 a,因此返回 a。
如果 a 是 true,那么就要再判断 b,和刚刚一样,不管 b 是 true 是 false,都返回 b。
a || b
:如果 a 是 true,那么 b 不管是 true 还是 false,都返回 true。因此不用判断 b 了,这个时候刚好判断到 a,因此返回 a。
如果 a 是 false,那么就要判断 b,如果 b 是 true,那么返回 true,如果 b 是 false,返回 false,其实不就是返回 b 了吗。
十、运算符优先级
// 假设 val 已经声明,可定义为任何值
console.log('Value is ' + (val != '0') ? 'define' : 'undefine'); // => ???
分析:加号优先级高于三目运算,低于括号。所以括号中无论真假,加上前边的字符串都为 true,三目运算为 true 是输出 define
。
十一、遍历数组 arr,剔除数组中为 0 的元素,最终会被剔除掉几个 0
var arr = [0,0,2,0,0,4,0,1,0,0,0,2];
for (var i = 0; i < arr.length; i++) {
if (arr[i] == 0)
arr.splice(i, 1);
}
分析:splice()
方法会改变原始数组。
第一次:i=0
,arr[0]
为 0,删除,数组[0,2,0,0,4,0,1,0,0,0,2]
。
第二次:i=1
,arr[1]
为 2,不删除。
第三次:i=2
,arr[2]
为 0,删除,数组[0,2,0,4,0,1,0,0,0,2]
。
第四次:i=3
,arr[3]
为 4,不删除。
第五次:i=4
,arr[4]
为 0,删除,数组[0,2,0,4,1,0,0,0,2]
。
第六次:i=5
,arr[5]
为 0,删除,数组[0,2,0,4,1,0,0,2]
。
第七次:i=6
,arr[6]
为 0,删除,数组[0,2,0,4,1,0,2]
。
第八次:i=7
,arr[7]
不存在,结束循环。
总共删除了 5 个 0。
// 解决:删除所有 0
var arr = [0,0,2,0,0,4,0,1,0,0,0,2];
for (var i = 0; i < arr.length; i++) {
if (arr[i] == 0)
arr.splice(i--, 1);
}
十二、有函数 F,以下 call 及 apply 方法写法错误的是
A: F.apply(this, "")
B: F.apply(undefined, [])
C: F.call(null, [])
D: F.call(null, null)
正确答案:A
分析:apply()
方法需要以数组形式一次性传入所有调用函数。
call()
方法必须详细列出每个参数,C 选项中只传了一个参数,只不过是数组形式。
十三、toString()
3.toString() // => ???
3..toString() // => ???
3...toString() // => ???
分析:因为小数点是数字的有效部分,所以第一个点被认为是数字,第二个点是链接。得到的结果分别是ERROR
,3
,ERROR
。
数字转换为字符串:toString()
或者String()
num.toString();
num.toString(2) // 二进制
(1)null
和undefined
转换需要用String()
(2)对于Number
和Boolean
,String()
和toString()
相同
十四、[“1”,”2”,”3”].map(parseInt)
(1)map()
方法:按照原始数组元素顺序依次处理元素,返回一个新数组。
(2)parseInt(string, radix)
string:必须,要被解析的字符串
radix:可选,表示要解析的数字基数
当忽略参数radix
,默认数字的基数如下:
- 如果
string
以 “0x” 开头,parseInt()
会把 string 的其余部分解析为十六进制的整数。 - 如果
string
以 0 开头,那么 ECMAScript v3 允许parseInt()
的一个实现把其后的字符解析为八进制或十六进制的数字。 - 如果
string
以 1 ~ 9 的数字开头,parseInt()
将把它解析为十进制的整数。
parseInt(1, 0); //将十进制数 1 转化为十进制数,仍为 1
parseInt(2, 1); //将一进制数 2(不存在)转化为十进制数,NaN
parseInt(3, 2); //NaN
parseInt(4, 3); //NaN
parseInt(10, 4); //将四进制数 10 转化为十进制数为 4
arr.map(parseInt)
含义为对数组arr
的每一项调用parseInt()
方法,传入的参数为每一项的值和该值的索引。
["1", "2", "3"].map(parseInt) 等价于
[parseInt("1", 0), parseInt("2", 1), parseInt("3", 2)]
结果为:[1,NaN,NaN]