资讯专栏INFORMATION COLUMN

【转载】十三道JavaScript基础题,你是否都做对了?

raoyi / 3191人阅读

摘要:题目二答案会报错未定义这段代码中混合了函数声明和函数表达式的形式,而函数实际上是绑定到了上而不是。除此之外函数声明与函数表达式的语法其实是等价的。因此,在外层函数函数体内的两个函数声明,都会提升到之前执行。

这是我在Javascript微信公众号上看到的一篇文章,觉得挺有意思的,所以转载过来跟大家分享一下,同时,对这些题目也加上了一些我个人的理解,如果有不对的地方,请大家指正。

题目一

(function(){
   return typeof arguments;
})();

答案:Object

typeof所返回的可能值为:numberstringbooleanundefinedobjectfunction。而我们知道arguments是一个伪数组,而伪数组也是一个对象,所以答案应该是Object。那么,如果把typeof arguments改成arguments instanceof Array呢?

可以看到,返回的是false。因此,伪数组不是一个数组,只有在使用了Array.prototype.slice.call()进行了转换后,才能转化成一个真数组。

题目二

var f = function g(){ return 23; };
typeof g();

答案:会报错(g()未定义)

这段代码中混合了函数声明和函数表达式的形式,而函数实际上是绑定到了f上而不是g。但是这么写是否符合Javascript的要求呢?在《Javascript高级程序设计》中:

也可以同时使用函数声明和函数表达式,例如:var sum = function sum(){}。不过,这种语法在safari中会导致错误。

既然在除了safari以外的浏览器可以同时使用函数声明和函数表达式来声明函数,那么为什么题目中的代码会报错呢?我的理解是,这样写的结果是导致了函数名g被包装进了f中,变成了f的一个局部变量,只能在f函数内部访问到g,我把代码修改了一下:

var f = function g(){ 
            alert(g);
            return 23; 
        };
f();

我在f函数内部访问g,弹框弹出的内容为function g(){ alert(g);return 23; },没有报错并且访问成功,g的函数体内容与f是一样的,只是函数名不一样。

————————我是一条分割线——————————

我们再来回顾一下函数声明与函数表达式吧(因为在之前的一次面试中被问到了它们的区别,我一下没打上来……所以还是增强一下记忆)

函数声明的形式:

function funcName(){ /*some code*/}

函数表达式的形式:

var funcName = function(){/*some code*/}

在《Javascript高级程序设计》中有一段话:

解析器在向执行环境中加载数据时,对函数声明和函数表达式并非一视同仁的。解析器会率先读取函数声明,并使其在执行任何代码之前可用(可以访问);至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解释执行。

也就是说,在函数声明的代码段之前访问函数,是能够访问到的(函数声明提升)。而在函数表达式的代码段之前访问函数,则会发生错误,因为在执行到函数所在语句之前,函数表达式声明的变量名中不会保存对函数的的引用。
除此之外函数声明与函数表达式的语法其实是等价的。

题目三:

(function(x){
  delete x;
  return x;
})(1);

答案:1

注意:delete是用来删除对象的属性,无法删除普通变量。

题目四

var y = 1, x = y = typeof x;
x;

答案:undefined
因为赋值运算是从右往左的,因此题目中的代码展开来等价于:

var y = 1;
y = typeof x;
var x = y;
x;

第二行代码在访问x时,x并没有声明,值为undefined,所以typeof x也就是undefined了。

题目五

(function f(f){
  return typeof f();
})(function(){ return 1; });

答案:number

把代码分开来看会比较清楚:

var baz = function(){ return 1; };
(function f(f){
  return typeof f();
})(baz);

先将function(){ return 1; }赋值给baz,则baz()应该是1。再把baz当成参数传入到立即执行函数中,立即执行函数返回的是传入的参数的类型,即baz()的类型,baz()返回1类型为number

题目六

var foo = {
  bar: function() { return this.baz; },
  baz: 1
};
 
(function(){
  return typeof arguments[0]();
})(foo.bar);

答案:undefined
这里关键要理解this的所指。arguments[0]foo.bar,而函数中的this指的就是调用arguments[0]()这个函数的对象,即window对象。但是window对象并没有baz这个属性,因此,返回的是undefined

题目七

var foo = {
   bar: function(){ return this.baz; },
   baz: 1
} 
typeof (f = foo.bar)();

答案:undefined
将代码改写一些变成:

var foo = {
   bar: function(){ return this.baz; },
   baz: 1
} 
var f = foo.bar;
typeof f();

foo.bar是一个函数,将foo.bar赋值给了f,那么f变成了一个函数名。直接调用f()时,调用它的对象为window,而window中并没有baz属性,所以输出的是undefined

题目八

var f = (function f(){ 
            return "1";
         }, function g(){ 
              return 2; 
         })();
typeof f;

答案:number
在这里,我们需要了解一下逗号操作符的作用:

在用于赋值时,逗号操作符总会返回表达式中的最后一项。

所以,这个立即执行函数返回的应该是后面那个函数的返回值,返回值为2,类型为number

题目九

var x = 1;
if (function f(){}) {
  x += typeof f;
}
x;

答案:1undefined
首先,第一个问题是:函数在判断语句中判断的结果是true还是false?,我们可以运行一下以下代码:

Boolean(function f(){});//true

将一个空函数转换为布尔型,结果为true,所以题中的代码是能够进入if语句的。那么第二个问题是:为什么typeof f会是undefined?因为if判断条件中并不能真正的声明一个函数,因此,f是一个没有被声明过的函数名,它的类型为undefined

题目十

(function f(){
  function f(){ return 1; }
  return f();
  function f(){ return 2; }
})();

答案:2
我们在题目四里说过:Javascript解析器会率先读取函数声明,使它在任何代码之间可用。因此,在外层函数f函数体内的两个函数声明,都会提升到return f()之前执行。而又因为Javascript没有重载,因此以最后执行的同名函数输出结果为准。

题目十一

function f(){ return f; }
new f() instanceof f;

答案:false
1、首先我们要了解instanceof的作用:instanceof是用来检测变量是否是给定引用类型的实例。
2、然后再看f()函数体中return f,返回了自己。
3、通过new来调用构造函数生成实例的过程为:1)创建一个空对象;2)将类的prototype中的属性和方法复制到实例中;3)将第一步创建的空对象做为类的参数调用类的构造函数;4)返回一个新的实例对象。
然而,在以new形式调用构造函数f()时,f()返回了自身,将new返回的实例对象覆盖掉了,因此,new之后的结果不是f的一个引用了。

题目十二

var x = [typeof x, typeof y][1];
typeof typeof x;

答案:string
很明显的可以看出typeof x = undefinedtypeof y = undefined,那么xundefined。其实其他的都不看,光看typeof typeof就能确定输出的是string了。

题目十三

(function(foo){
  return typeof foo.bar;
})({ foo: { bar: 1 } });

答案:undefined
这道题目有一个小陷阱,一不注意还真被忽悠进去了。我们仔细看一个参数传入的内容最外面有一对{},那么我们把题目修改一下就一目了然了:

var temp = { foo: { bar: 1 } };
(function(foo){
  return typeof foo.bar;
})(temp);

传入的参数为temp这个对象,因此函数中的foo.bar实际上为temp.bar,但是temp里只有foo一个属性,没有bar,所以应该是undefined

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/91727.html

相关文章

  • JavaScript 设计模式(三):代理模式

    摘要:虚拟代理延迟执行虚拟代理的目的,是将开销大的运算延迟到需要时再执行。 showImg(https://segmentfault.com/img/bVbuitm?w=800&h=600); 代理模式:为一个对象提供一个代用品或占位符,以便控制它的访问。 当我们不方便直接访问某个对象时,或不满足需求时,可考虑使用一个替身对象来控制该对象的访问。替身对象可对请求预先进行处理,再决定是否转交给...

    Keagan 评论0 收藏0
  • 前端开发应届生面试指南(含各大公司具体指南及面试真

    摘要:先介绍一下本人应届前端开发一枚,非科班出身,专业是化学,大学期间开始自学前端开发,在今年春招实习和秋招的时候投了一些公司,拿到一些京东拼多多虎牙等,总体来说还算满意,特地写一篇文章来总结一下面试的那些套路。 showImg(https://segmentfault.com/img/remote/1460000011897700); 先介绍一下本人应届前端开发一枚,非科班出身,专业是化学...

    sunnyxd 评论0 收藏0
  • 前端开发应届生面试指南(含各大公司具体指南及面试真

    摘要:先介绍一下本人应届前端开发一枚,非科班出身,专业是化学,大学期间开始自学前端开发,在今年春招实习和秋招的时候投了一些公司,拿到一些京东拼多多虎牙等,总体来说还算满意,特地写一篇文章来总结一下面试的那些套路。 showImg(https://segmentfault.com/img/remote/1460000011897700); 先介绍一下本人应届前端开发一枚,非科班出身,专业是化学...

    jeyhan 评论0 收藏0
  • 前端开发应届生面试指南(含各大公司具体指南及面试真

    摘要:先介绍一下本人应届前端开发一枚,非科班出身,专业是化学,大学期间开始自学前端开发,在今年春招实习和秋招的时候投了一些公司,拿到一些京东拼多多虎牙等,总体来说还算满意,特地写一篇文章来总结一下面试的那些套路。 showImg(https://segmentfault.com/img/remote/1460000011897700); 先介绍一下本人应届前端开发一枚,非科班出身,专业是化学...

    lentoo 评论0 收藏0
  • <LeetCode天梯>Day028 回文链表(双指针+递归+栈+数组) | 初级算法 | Pyth

    摘要:先实现栈操作遍历链表,把每个节点都进中然后再遍历链表,同时节点依次出栈,二者进行比较。 ?作者简介:大家好,我是车神哥,府学路18号的车神? ?个人主页:应无...

    miguel.jiang 评论0 收藏0

发表评论

0条评论

raoyi

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<