JavaScript基本的面试题及答案
1、使用typeof bar==="object"来确定bar是否是对象的潜在陷阱是什么?如何避免这个陷阱?
例:
var bar=null;
console.log(typeof bar==="object");
输出结果:true;
解释:在javascript中null被认为是对象
如果要避免该陷阱,则还需要判断bar是否为null。
例:
var bar=null;
console.log((bar!==null)&&(typeof bar==="object"));
输出结果:false
2、下面的代码在控制台输出什么结果,为什么?
(function(){ var a=b=3;
console.log(typeof a=='undefined');//输出结果:false
})();
console.log("a defined?"+(typeof a!=='undefined'));//输出结果:true
console.log("b defined?"+(typeof b!=='undefined'));//输出结果:false
解释:
var a=b=3的声明是b=3,var a=b的简写,b是全局变量(因为它没有前缀var关键字),因此它的作用域为匿名函数之内和匿名函数之外;而a的定义为局部变量,因此它的作用域只在匿名函数之内,而输出结果语句是在闭包之外,因此typeof a!=='undefined'的输出结果为true,在匿名函数之内的输出结构则为false.
注:1、这个题的关键在var a=b=3这句话的理解
2、在严格模式下(即使用use strict),语句var a=b=3将生成ReferenceError:b is not defined的运行时错误,从而避免任何否则可能导致的headfakes/bug.
3、下面的代码将输出什么到控制台,为什么?
<script> var myObject={ foo:"bar", func:function(){ var self=this; console.log("outer func:this foo="+this.foo);//输出结果为:outer func:this foo=bar console.log("outer func:self.foo="+self.foo);//输出结果为:outer func:this foo=bar (function(){ console.log("inner func:this.foo="+this.foo);//输出结果为:inner func:this.foo=undefined console.log("inner func:self.foo="+self.foo);//输出结果为:inner func:self.foo=bar }()); } }; myObject.func(); </script>
解释:在外部函数中,this和self都指向的是myObject对象,所以输出结果都指向的是bar;在内部函数中,this指向的是其匿名函数,而匿名函数中并没有定义foo,因此输出结果为undefined,而self变量相对于内部函数而言是全局变量,其所指向的是myObject对象,因此其输出结果为:bar.
注:该题的关键点在于对this的理解,但是this在JavaScript中又是一个很难理解的东西,通过学习和别人的总计,大致对this在不同的环境中的意义做了总结,大致如下:
1、在HTML标签中(例:<button onclick="calc(this)">提交</button>),this指代当前触发事件元素对象本身;
2、所有匿名函数的回调或者自调,内部的this指向的是window;
3、在程序运行时,this指代正在调用方法的对象(.之前的对象);
4、在JavaScript源文件的开头包含use strict有什么意义和好处?
答:use strict是一种在JavaScript代码运行时自动实行更严格解析和错误处理的方法,那些被忽略或默默失败了的代码错误,会产生错误或抛出异常。通常而言,这是一个很好的做法。
严格模式的主要优点包括:
1、使调试更加容易。那些被忽略或默默失败了的代码错误,会产生错误或抛出异常,因此今早提醒你代码中的问题,你才能更快的指引到它们的源代码
2、防止意外的全局变量。如果没有严格模式,将值分配给一个未声明的变量会自动创建该名称的全局变量。这是JavaScript中最常见的错误之一。在严格模式下,这样做的话会抛出错误。
3、消除this强制。如果没有严格模式,引用null或未定义的值到this值会自动强制到全局变量。这可能会导致许多令人头痛的问题和让人恨不得拔自己头发的bug。在严格模式下,引用null或未定义的this值会抛出错误。
4、不允许重复的属性名称或者参数值。当检测到对象(例如:var object={foo:“bar”,foo:"baz"})中重复命名的属性,或检测到函数中(例如:function foo(var1,var2,var1){})重复命名的参数时,严格模式会抛出错误,因此捕捉几乎可以肯定是代码中的bug可以避免浪费大量的跟踪时间。
5、使用eval()更安全。在严格模式和非严格模式下,eval()的行为方式有所不同。最显而易见的是,在严格模式下,变量和声明在eval()语句内部的函数不会再包含范围内创建(它们会在严格模式下的包含范围中被创建,这也是一个常见的问题源。)
6、在delete使用无效时抛出错误。delete操作符(用于从对象中删除属性)不能用再对象不可配置的属性上。当试图删除一个不可配置的属性时。非严格代码将默默地失败,而严格模式将在这样的情况下抛出异常。
5、考虑以下两个函数,它们会返回相同的东西吗?为什么相同或为什么不同?
<script> function foo1(){ return { bar:"hello" }; } function foo2(){ return { bar:"hello" }; } console.log(foo1());//输出结果为Object {bar: "hello"} console.log(foo2());//输出结果为undefined </script>
解释:
分号在JavaScript中是一个可选项(尽管省略它们通常是非常糟糕的形式)。其结果就是,当碰到foo2()中包含return语句的代码行(代码行上没有任何其它代码),分号会立即自动插入到返回语句之后。也不会抛出错误,因为代码的其余部分是完全有效的,即使它没有得到调用或做任何事情(相当于它就是一个未使用的代码块,定义了等同于字符串“hello”的属性bar)。这种行为也支持放置左括号于JavaScript代码行的末尾,而不是新代码行开头的约定。正如这里所示,这不仅仅是JavaScript中的一个风格偏好。
6、NaN是什么?它的类型是什么?你如何可靠的测试一个值是否等于NaN?
答:NaN属性代表一个“不是数字”的值。这个特殊的值是因为运算不能执行而导致的,不能执行的原因要么是因为其中的运算对象之一非数字(例如:“abc”/3),要么是因为运算结果为非数字(例如:除数为0)。虽然这看上去很简单,但NaN有一些令人惊讶的特点,如果你不知道它的话,可能会导致令人头痛的bug。
首先,NaN和任何东西做全等比较,包括它自身,结果返回的都为false,一种可靠的办法来测试一个数字是否是NaN,是使用函数isNaN(),但即使使用isNaN()依然并非是一个完美的解决方案。一个更好的解决办法是使用value!=value,如果值等于NaN,只会产生true。另外,ES6提供了一个新的Number.isNaN()函数,这是一个不同的函数,并且比老的全局isNaN()函数更可靠。
7、下列代码输出什么?为什么?
console.log(0.1+0.2);//输出结果为:0.30000000000000004
console.log(0.1+0.2==0.3);//输出结果为:false
JavaScript中的数字和浮点精度的处理相同,因此可能不会总产生预期的结果。
8、判断值x是否为整数的方法?
答:1、在ECMAScript6中使用number.isInteger()方法判断,如果是整数,则返回true,如果不是,则返回false.
例:number.isInteger(25)//true;
number.isInteger(25.0)//true;
number.isInteger(25.1)//false;
number.isInteger("25")//false;
number.isInteger(true)//false;
2、function isInteger(x){return (x^0)===x;}
3、function isInteger(x){return Math.round(x)===x};
注:Math.ceil()和Math.floor()等同于Math.round()
4、function isInteger(x){return (typeof x==="number")&&(x%1===0)}
错误方法:function isInteger(x){return parseInt(x,10)===x;}
解释:因为当x的取值足够大时,这个方法就会出错,因为当X值足够大时,它的字符串就会表达为指数形式,例如:1e+21,当使用parseInt()去判断时,输出的结果为1.
9、下列代码的输出结果是什么?为什么?
(function(){ console.log(1); setTimeout(function (){console.log(2)},1000); setTimeout(function (){console.log(3)},0); console.log(4); })();//输出结果:1 4 3 2
解释:1和4之所以放到前面,是因为他们通过简单调用console.log()而没有任何延迟输出的
2之所以放在3的后面,是因为2是延迟了1000毫秒(即1秒)之后输出的,而3是延迟了0毫秒之后输出的。
但是,既然3是0毫秒延迟之后输出的,那么是否意味着它是立即输出呢?如果是的话,那么它是不是应该在4之前输出,既然4是在第二行输出的?原因是:浏览器有一个事件循环,会检查事件队列和处理未完成的事件。例如:如果事件发生在后台(例如:脚本的onload事件)时,浏览器正忙(例如,处理一个onclick),那么事件会添加到队列中。当onclick处理程序完成后,检查队列,然后处理该事件(例如,执行onload脚本)。同样的,setTimeout()也会把其引用的函数的执行放到事件队列中,如果浏览器正忙的话。当setTimeout()的第二个参数为0的时候,它的意思是“尽快”执行指定的函数。具体而言,函数的执行会放置在事件队列的下一个计时器开始。但是请注意,这不是立即执行:函数不会被执行除非下一个计时器开始。这就是为什么上述例子中,调用console.log(4)发生在console.log(3)是之前调用了。