本文深入解析一系列 JavaScript 经典面试题,涵盖 arguments 对象类型、命名函数表达式的可见性、delete 运算符对形参的影响、变量声明提升与赋值顺序、this 上下文丢失、逗号运算符优先级、函数声明提升以及 instanceof 机制等核心概念。通过具体代码示例,揭示 JavaScript 引擎在处理这些场景时的底层逻辑与常见陷阱,帮助开发者理解编译器行为并避免潜在错误。
JavaScript 经典陷阱题深度解析:从参数对象到作用域提升
参考资源:http://perfectionkills.com/javascript-quiz/
以下是我对这些题目的一些个人理解。如果存在不同意见或更深入的见解,欢迎交流指正。
arguments 对象类型与命名函数表达式
代码如下:
(function(){
return typeof arguments;
})(); 该题目相对基础,关键在于明确 arguments 并非真正的 Array 实例。
返回结果为 object。
代码如下:
var f = function g(){ return 23; };
typeof g(); 查阅相关书籍(如 Aiming 的著作)可知,书中对 function 的定义及多种创建方式有详尽描述。此陷阱在后续多个题目中反复出现,需格外小心。
返回结果为 undefined。
delete 运算符对形参的影响
代码如下:
(function(x){
delete x;
return x;
})(1); JavaScript 中的 delete 运算符并非删除引用或指向的对象,而是尝试删除对象本身的属性。
/*
参考资料:
delete 运算符
用于从对象中删除一个属性,或从数组中删除一个元素。
delete expression
expression 参数是一个有效的 JScript 表达式,通常是一个属性名或数组元素。
因此,函数形参值的改变与 delete 运算符无直接关联。
*/
关于 delete 的具体解析,有读者提供了原题的部分解释,此前因疏忽未查阅,特此补充。
参考链接:http://perfectionkills.com/understanding-delete/
返回结果为 1。
变量声明提升与赋值顺序
代码如下:
var y = 1, x = y = typeof x;
x; 此处存在一个细微陷阱。
已知 x 的值为 undefined,因此 typeof x 实际上等同于 typeof "undefined"。
返回结果自然是 string 类型。
函数参数遮蔽与返回值类型
代码如下:
(function f(f){
return typeof f();
})(function(){ return 1; }); 又一个陷阱。这里的 f 是什么?它与形参无关,且在函数内部无法直接获取到外部同名函数。
因此,形参 f 的值未发生任何改变。
返回结果为 number。
this 上下文丢失与函数引用
代码如下:
var foo = {
bar: function() { return this.baz; },
baz: 1
};
(function(){
return typeof arguments[0]();
})(foo.bar); 此处的关键在于 this 的上下文绑定。传入的是 foo.bar 函数本身,而非其所属对象 foo。
因此,返回结果为 undefined。
代码如下:
var foo = {
bar: function(){ return this.baz; },
baz: 1
}
typeof (f = foo.bar)(); 基于上一题的理解,此题逻辑类似。
返回结果为 undefined。
逗号运算符与函数声明提升
代码如下:
var f = (function f(){ return "1"; }, function g(){ return 2; })();
typeof f; 此处考察的是括号表达式 () 的返回值。逗号运算符返回最后一个表达式的值。
返回结果为 Number。
if 语句中的函数可见性与类型转换
代码如下:
var x = 1;
if (function f(){}) {
x += typeof f;
}
x; if 语句中的条件表达式肯定会被执行,因为它是一个实际存在的对象(非 null 或 undefined)。在非布尔上下文中,非 null/undefined 的对象均视为真值。然而,与前述题目原理相同,函数声明名 f 在此作用域内不可见。
因此返回结果为 1undefined。
嵌套数组与 typeof 链式调用
代码如下:
var x = [typeof x, typeof y][1];
typeof typeof x; 此处 typeof y 为 undefined。接着,typeof typeof undefined 等价于 typeof "undefined"。
返回结果为 "string"。
对象属性访问与函数参数遮蔽
代码如下:
(function(foo){
return typeof foo.bar;
})({ foo: { bar: 1 } }); 这是一个容易因阅读过快而忽略的小陷阱。
简要描述:var fo = {foo:{bar:1}}; function(foo){})(fo);
return typeof foo.bar 等价于 return typeof fo.bar。
因此返回结果为 "undefined"。
函数声明覆盖与返回值
代码如下:
(function f(){
function f(){ return 1; }
return f();
function f(){ return 2; }
})(); 此题涉及编译器对函数声明的解析机制。这与 return 语句无关,因为编译器在解析阶段会将所有函数声明提升并覆盖同名声明。这与 var f = function(){} 这种函数表达式的行为不同。
因此,返回结果为 2。
构造函数返回自身与 instanceof 判断
代码如下:
function f(){ return f; }
new f() instanceof f; 对此处理解尚浅,瓶子给出的解释看似合理。
new f() 返回的是 f 函数本身,而非一个由 f 构造函数创建的新对象实例,因此不满足 instanceof 的条件。
返回结果为 false。
with 语句与函数形参长度
with (function(x, undefined){}) length;
关于 with 语句,若将其视为类似 alert 的函数调用。
那么 length 即为 with 内部函数的形参数量。
返回结果为 2。
综上所述,许多 JavaScript 问题需要系统梳理。Aiming 的书籍对此部分讲解透彻,有助于规避陷阱和问题。其余部分则需依靠个人测试验证。
这些题目虽不能完全反映一个人的 JavaScript 能力,但至少能让人了解编译器处理 JavaScript 代码的方式。