1.使用 typeof bar === "object" 来确定 bar 是否是对象的潜在陷阱是什么?如何避免这个陷阱?
陷阱:
typeof null === "object" //true
避免:
console.log((bar !== null) && (typeof bar === "object")); // false
2.下面的代码将输出什么到控制台,为什么?
(function(){
var a=b=3;
})();
console.log("a defined? "+(typeof a!=='undefined'));
console.log("b defined? "+(typeof b!=='undefined'));
输出:
a defined?false
b defined?true
原因:
var a = b = 3; 实际是以下声明的简写:
b=3;
var a=b;
在闭包外访问内部变量,正常应该输出undefined。b不是undefined,是因为没有var声明,则b=3,默认定义的是全局变量。
3.封装JavaScript源文件的全部内容到一个函数块有什么意义及理由?
创建一个私有的命名空间,避免不同JavaScript模块和库之间潜在的名称冲突。
4.在JavaScript源文件的开头包含 use strict 有什么意义和好处?
意义:
有利于那些被忽略或默默失败了的代码错误,产生或抛出异常。
严格模式的主要优点:
(1)使调试更加容易。
(2)防止意外的全局变量。
(3)消除 this 强制。如果没有严格模式,引用null或未定义的值到 this 值会自动强制到全局变量。在严格模式下,引用 null或未定义的 this 值会抛出错误。
(4)不允许重复的属性名称或参数值。有利于bug的定位。
(5)使eval() 更安全。
(6)在 delete使用无效时抛出错误。
5.考虑以下两个函数。它们会返回相同的东西吗? 为什么相同或为什么不相同?
function foo1(){
return {
bar: "hello"
};
}
function foo2(){
return
{
bar: "hello"
};
}
返回不同的东西。
foo1(); //Object{bar:"hello"}
foo2(); //undefined
原因:
foo2的return语句后面空白,默认会被添加一个分号。导致运行后返回undefined。
6.NaN 是什么?它的类型是什么?你如何可靠地测试一个值是否等于 NaN ?
NaN:not a number
类型:number。typeof NaN,返回number
可靠地测试:
由于NaN !== NaN,可使用value !== value测试。
内置函数(全局函数)isNaN(),或ES6新增的Number.isNaN() 函数。注:后者更可靠。
7.讨论写函数 isInteger(x) 的可能方法,用于确定x是否是整数。
es6提供Number.isInteger() 函数。
es5时代,自行实现:
function isInteger(x) {
return (x^0) === x; //或 return Math.round(x) === x;
}
或
function isInteger(x) {
return (typeof x === 'number') && (x % 1 === 0);
}
8.写一个简单的函数(少于80个字符),要求返回一个布尔值指明字符串是否为回文结构。
function isPalindrome(str) {
str = str.replace(/W/g, '').toLowerCase();
return (str == str.split('').reverse().join(''));
}
9.请看下面的代码片段:
for (var i = 0; i < 5; i++) {
var btn = document.createElement('button');
btn.appendChild(document.createTextNode('Button ' + i));
btn.addEventListener('click', function(){ console.log(i); });
document.body.appendChild(btn);
}
(a)当用户点击“Button 4”的时候会输出什么到控制台,为什么?
(b)提供一个或多个备用的可按预期工作的实现方案。
答案:
(a)输出:5。原因:当 onclick 方法被调用(对于任何按钮)的时候, for 循环已经结束,变量 i 已经获得了5的值。变量i是全局变量,在click事件被触发的时候,执行响应函数function,打印i,则都会打印出5。
(b)
for (var i = 0; i < 5; i++) {
var btn = document.createElement('button');
btn.appendChild(document.createTextNode('Button ' + i));
btn.addEventListener('click', (function(i) {
return function() { console.log(i); };
})(i));
document.body.appendChild(btn);
}
或
for (var i = 0; i < 5; i++) {
var btn = document.createElement('button');
btn.appendChild(document.createTextNode('Button' + i));
(function (i) {
btn.addEventListener('click', function() { console.log(i); });
})(i);
document.body.appendChild(btn);
}
10.下面的代码将输出什么到控制台,为什么?
var arr1 = "john".split('');
var arr2 = arr1.reverse();
var arr3 = "jones".split('');
arr2.push(arr3);
console.log("array 1: length=" + arr1.length + " last=" + arr1.slice(-1));
console.log("array 2: length=" + arr2.length + " last=" + arr2.slice(-1));
输出:
"array 1: length=5 last=j,o,n,e,s"
"array 2: length=5 last=j,o,n,e,s"
原因:
reverse()操作会改变原数组,并返回改变后的数组。即:var a = [1,2,3]; a.reverse(); console.log(a);//[3,2,1]
同时,数组是引用类型,arr2指向arr1的地址,二者的值完全相同
11.下面的代码将输出什么到控制台,为什么?
console.log(1 + "2" + "2"); //"122"
console.log(1 + +"2" + "2"); //"32"
console.log(1 + -"1" + "2"); //"02"
console.log(+"1" + "1" + "2"); //"112"
console.log( "A" - "B" + "2"); //"NaN2"
console.log( "A" - "B" + 2); //NaN
12.下面的递归代码在数组列表偏大的情况下会导致堆栈溢出。在保留递归模式的基础上,你怎么解决这个问题?
var list = readHugeList();
var nextListItem = function() {
var item = list.pop();
if (item) {
// process the list item...
nextListItem();
}
};
解决:
var list = readHugeList();
var nextListItem = function() {
var item = list.pop();
if (item) {
// process the list item...
setTimeout( nextListItem, 0);
}
};
堆栈溢出之所以会被消除,是因为事件循环操纵了递归,而不是调用堆栈。当 nextListItem 运行时,如果 item不为空,timeout函数(nextListItem)就会被推到事件队列,该函数退出,因此就清空调用堆栈。当事件队列运行其timeout事件,且进行到下一个 item 时,定时器被设置为再次调用 nextListItem。因此,该方法从头到尾都没有直接的递归调用,所以无论迭代次数的多少,调用堆栈保持清空的状态。
13.JavaScript中的“闭包”是什么?请举一个例子。
闭包是一个可以访问外部(封闭)函数作用域链中的变量的内部函数。
闭包可以访问三种范围中的变量:
这三个范围具体为:(1)自己范围内的变量,(2)封闭函数范围内的变量(3)全局变量。
例子:
var globalVar = "xyz";
(function outerFunc(outerArg) {
var outerVar = 'a';
(function innerFunc(innerArg) {
var innerVar = 'b';
console.log(
"outerArg = " + outerArg + "n" +
"innerArg = " + innerArg + "n" +
"outerVar = " + outerVar + "n" +
"innerVar = " + innerVar + "n" +
"globalVar = " + globalVar);
})(456);
})(123);
输出:
outerArg = 123
innerArg = 456
outerVar = a
innerVar = b
globalVar = xyz
14.以下代码行将输出什么到控制台?
console.log("0 || 1 = "+(0 || 1));
console.log("1 || 2 = "+(1 || 2));
console.log("0 && 1 = "+(0 && 1));
console.log("1 && 2 = "+(1 && 2));
输出:
0 || 1 = 1
1 || 2 = 1
0 && 1 = 0
1 && 2 = 2
15.以下代码将输出什么?并解释你的答案。
var a={},
b={key:'b'},
c={key:'c'};
a[b]=123;
a[c]=456;
console.log(a[b]);
输出:456
原因:
当设置对象属性时,JavaScript会暗中字符串化参数值。在这种情况下,由于 b 和 c都是对象,因此它们都将被转换为"[object Object]"。结果就是, a[b]和a[c]均相当于a["[object Object]"] ,并可以互换使用。因此,设置或引用 a[c]和设置或引用 a[b]完全相同。
16.创建一个函数,给定页面上的一个DOM元素,就会去访问元素本身及其所有子元素(不只是它的直接子元素)。对于每个被访问的元素,函数应该传递元素到提供的回调函数。
此函数的参数为:
DOM元素
回调函数(将DOM元素作为其参数)
访问树(DOM)的所有元素是经典的深度优先搜索算法应用。下面是一个示范的解决方案:
function Traverse(p_element,p_callback) {
p_callback(p_element);
var list = p_element.children;
for (var i = 0; i < list.length; i++) {
Traverse(list[i],p_callback); // recursive call
}
}