一、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]