JavaScript中一些你不一定知道的问题(持续更新中。。。。)
一些js的问题与解析
1) ["1","2","3"].map(parseInt);
的运行结果是?
A.["1","2","3"] B.[1,2,3] C.[0,1,2] D.其他
分析:
D
map对数组的每个元素调用定义的回调函数并返回包含结果的数组。["1","2","3"].map(parseInt)对于数组中每个元素调用paresInt。但是该题目不同于:
function testFuc(a){ return parseInt(a); } console.info(["1","2","3"].map(testFuc));
题目等同于:
function testFuc(a,x){ return parseInt(a,x); } console.info(["1","2","3"].map(testFuc));
map中回调函数的语法如下所示:function callbackfn(value, index, array1)
,可使用最多三个参数来声明回调函数。第一参数value,数组元素的值;第二个参数index,数组元素的数组所以;array1,包含该元素的数组对象。 因此,题目等同于[parseInt(1,0),parseInt(2,1),parseInt(3,2)]
最终返回[1, NaN, NaN].
parseInt():
parseInt(string, radix);
接受两个参数string
和radix
。
string
需要解析的一串‘类数字’string。
(The value to parse. If string is not a string, then it is converted to one. Leading whitespace in the string is ignored.)
radix
解析string的基数设置,可取值范围在2~36之间[闭区间]。
(An integer between 2 and 36 that represents the radix (the base in mathematical numeral systems) of the above mentioned string. Specify 10 for the decimal numeral system commonly used by humans. Always specify this parameter to eliminate reader confusion and to guarantee predictable behavior. Different implementations produce different results when a radix is not specified.)
从parseInt的定义中可以得出一个方便的理解,parseInt的第一个参数一定要小于第二个参数,当然它们都是数字的String的比较。否则结果就是NaN。
一个意外:当radix
取值为0的时候,相当于默认的parseInt(string)而没有radix
.eg:
parseInt(1,0);//相当于parseInt(1); ->1 parseInt(0123,0);//相当于parseInt(0123); ->83
2) [typeof null, null instanceof Object]
的运行结果是?
A.["object",false] B.[null,false] C.["object",true] D.其他
分析
A
typeof用以获取一个变量或者表达式的类型,typeof一般只能返回如下几个结果:
number,boolean,string,function(函数),object(NULL,数组,对象),undefined。
instanceof 表示某个变量是否是某个对象的实例,null是个特殊的Object类型的值 ,表示空引用的意思 。但null返回object这个其实是最初JavaScript的实现的一个错误, 然后被ECMAScript沿用了,成为了现在的标准,不过我们把null可以理解为尚未存在的对象的占位符,这样就不矛盾了 ,虽然这是一种“辩解”。
对于我们开发人员 还是要警惕这种“语言特性”。最终返回:["object", false]
3) [[3,2,1].reduce(Math.pow),[].reduce(Math.pow)]的运行结果是?
A.报错 B.[9,0] C.[9,NaN] D.[9,undefined]
分析
A
pow() 方法可返回 x 的 y 次幂的值。[3,2,1].reduce(Math.pow);等同于:
function testFuc(x,y){ console.info(x +" : "+y); return Math.pow(x,y); } console.info([3,2,1].reduce(testFuc));
执行Math.pow(3,2)
和Math.pow(9,1)[].reduce(Math.pow)
,等同于执行Math.pow();
会导致错误。
这里要介绍下arr.reduce(callback[, initialValue])
了
国际惯例,官方解释:
Parameters allback Function to execute on each value in the array, taking four arguments: previousValue The value previously returned in the last invocation of the callback, or initialValue, if supplied. currentValue The current element being processed in the array. index The index of the current element being processed in the array. array The array reduce was called upon. initialValue Optional. Object to use as the first argument to the first call of the callback.
4) 以下代码的运行结果是:
var name = 'World'; (function(){ if(typeof name === 'undefined'){ var name = "Jack"; console.info('Goodbye '+ name); }else{ console.info('Hello ' + name); } })();
A.Goodbye Jack B.Hello Jack C.Goodbye undefined D.Hello undefined
分析
A
js函数内有个叫做【声明提前】的东西。故选A。
5) 以下代码的运行结果是:
var arr = [0,1,2]; arr[10] = 10; arr.filter(function(x){return x === undefined});
A.[undefined x 7]
B.[0,1,2,10]
C.[]
D.[undefined]
分析
C
filter会接触到没有被赋值的元素,即在arr中,长度为10但实际数值元素列表为[0, 1, 2, 10],因此,最终返回一个空的数组[]
又有机会解释js函数了哈arr.filter(callback[, thisArg])
国际惯例,官方解释:
Parameters callback Function to test each element of the array. Invoked with arguments (element, index, array). Return true to keep the element, false otherwise. thisArg Optional. Value to use as this when executing callback.
简单的来。就是filter返回arr中callback(value)结果为true的值。over
6) 以下代码运行结果是:
var two = 0.2; var one = 0.1; var eight = 0.8; var six = 0.6; [two -one == one,eight- six == two];
A.[true,true] B.[false,false] C.[true,false] D.其他
分析
C
两个浮点数相加或者相减,将会导致一定的正常的数据转换造成的精度丢失问题eight-six = 0.20000000000000007。 JavaScript中的小数采用的是双精度(64位)表示的,由三部分组成: 符 + 阶码 + 尾数,在十进制中的 1/10,在十进制中可以简单写为 0.1 ,但在二进制中,他得写成:0.0001100110011001100110011001100110011001100110011001…..(后面全是 1001 循环)。因为浮点数只有52位有效数字,从第53位开始,就舍入了。这样就造成了“浮点数精度损失”问题。
更严谨的做法是(eight-six ).totoFiexd(1)或者用用Math.round方法回归整数运算。判断两个浮点数是否相等,还是建议用逼近的比较,比如if((a-b) < 1E-10)
值得注意的是,0.2-0.1却是==0.1的。
7) 以下代码运行的结果是:
function showCase(value){ switch(value){ case 'A': console.info('Case A'); break; case 'B': console.info('Case B'); break; case undefined : console.info('undefined'); break; default: console.info('Do not know!'); } } showCase(new String('A'));
A.Case A B.Case B C.Do not know D.undefined
分析
C
使用new String()使用构造函数调用讲一个全新的对象作为this变量的值,并且隐式返回这个新对象作为调用的结果,因此showCase()接收的参数为String {0: "A"}为不是我们所认为的“A”
但是,其实显然,此时的new String('A') == 'A';虽然new出来的是个String对象。
var a = new String('A'); //->a == String {0: "A", length: 1, [[PrimitiveValue]]: "A"} a == 'A'; //-> true
从上面我们可以知道,即使把题中的showCase(new String('A'));
改为var a = new String('A');showCase(a);
,它传进去的依然是个String{0:'A'...}
对象。结果依然是C。
8) 以下表达式的运行结果是:
Array.isArray(Array.prototype)
A.true B.false C.报错 D.其他
分析
A
Array.prototype为[],Array.isArray(a)是一个判断a是否为数组的方法。 判断对象是否为数组的方法:
- 1)ES5函数isArray(),该函数测试对象的内部[[Class]]属性是否为Array:
Arrray.isArray(a);
- 2)判断对象的构造函数是否为Array:
a.constructor === Array
- 3)使用对象内部[[Class]]属性创建结果字符串:
Object.prototype.toString.call(a)
- 4)使用instanceof操作符测试对象是否继承自Array: (但由于,一个页面的iframe不会继承自另外一个页面的iframe,该方法不可靠)
a instanceof Array
9) [] == [] 的运行结果是:
A.true B.false C.报错 D.其他
分析
B
数组,在 Javascript 中是对象,对象使用 == 比较都是比较的引用。简单的说,就是,如果是同一个对象,就相等,如果不是同一个对象,就不等。每次使用 [] 都是新建一个数组对象,所以 [] == [] 这个语句里建了两个数据对象,它们不等。
那么问题来了:
[] > []
[] == []
[] >= []
输出分别为 false false true
js中比较‘>=’时是使用‘!([] < [])’的结果,所以为true;
那么又问题来了,如何判断两个数组相等呢?
lodash中用_.isEqual(value, other, [customizer], [thisArg])来比较即可。
10) [3.toString(),3..toString(),3...toString()]
的运行结果是:
A.["3",error,error] B.["3","3.0",error] C.[error,"3",error] D.其他
分析
C
Number中的toString(a)
能够将数值转化成为a进制的值。但a缺省时,默认转化为十进制,一般使用方法为:
var n = 3;
n.toString();
执行3.toString()
,因为3只是为数值型变量,为非Number实例,因此对于3不能直接调用Number方法。而执行3..toString()
,会强制将3转化为数字实例,因此能够被解释,输出'3',同样可以使用(3).toString()
。
11) 列举IE和FF脚本兼容性的问题:
分析
列举IE和FF脚本兼容性的问题
- (1)window.event 表示当前的事件对象,IE有这个对象,FF没有
- (2)获取事件源 IE用srcElement获取事件源,而FF用target获取事件源
- (3)添加、移除事件
IE:element.attachEvent("onclick",function) element.detachEvent("onclick",function) FF: element.addEventListener("click",function,true) element.removeEventListener("click",function,true)
11) 以下函数有什么问题?如何改进?
function initButtons(){ var body = document.body,button,i; for(i =0;i<5;i++){ button = document.createElement("button"); button.innerHTML = "Button" + i; button.addEventListener("click",function(e){ alert(i); },false); body.appendChild(button); } } initButtons();
分析
题目所给出的代码,除了有addEventListener不兼容IE浏览器的问题之外,最突出的一个问题是:
虽然在页面上会显示值为button+i的按钮,但是点击任意一个按钮,最终都会显示5。 要想点击相关按钮,弹出相应的1,2,3,4,5的值,需要理解闭包原理实现和使用立即回调函数。修改后的代码如下:
function initButtons(){ var body = document.body,button,i; for(i =0;i<5;i++){ (function(i){ button = document.createElement("button"); button.innerHTML = "Button" + i; button.addEventListener("click",function(e){ alert(i); },false); body.appendChild(button); })(i); } } initButtons();
涉及绑定和赋值得到区别。在运行时,进入一个作用域,javascript会为每一个绑定到该作用域的变量在内存中分配一个“槽”(slot)。 函数中,创建变量document.body,button,i,因此当函数体(创建按钮,并为按钮绑定事件)被调用时,函数会为每个变量分配一个“槽”。 在循环的每次迭代中,循环体都会为嵌套函数分配一个闭包。我们可能理解为,该函数存储的是嵌套函数创建时变量i的值。 但事实上,他存储的是i的引用。由于每次函数创建后变量i的值都发生变化,因此函数内部最终看到的是变量i的引用。闭包存储的是外部变量的引用而非值。 立即调用的函数表达式,是一种不可或缺的解决javascript缺少块级作用域的方法。
如此这样,我们就要来说一下js中的闭包了啊。
闭包
鉴于这个篇幅可能有点长,放另外一个Closures文件中。
12) 写一段代码,判断一个字符串中出现次数最多的字符,并统计出现的次数。
分析
/*写一段代码。判断一个字符串中出现次数最多的字符串,并统计出现的次数*/ //常规方法 function toGetTheMostCharsByArray(s){ var r={}; for(var i=0;i< s.length;i++){ if(!r[s[i]]){ r[s[i]] = 1; }else{ r[s[i]]++; } } var max = { "value": s[0], "num": r[s[0]] }; for(var n in r){ if(r[n]>max.num){ max.num = r[n]; max.value = n; } } return max; } //使用正则方法 function toGetTheMostCharsByRegex(s){ var a = s.split(''); a.sort(); s = a.join(''); var regex = /(\w)\1+/g ; // \1+代表重复的 var max = { "value" :s[0], "num" : 0 }; s.replace(regex,function(a,b){//a是重复的string:eg:'aaa',b是重复的字母char:eg:'a'; if(max.num < a.length){ max.num = a.length; max.value= b; } }); return max; } var test = "efdfssssfrhth"; console.info("使用常规方法,出现最多的字符串为:"+toGetTheMostCharsByArray(test).value+" ,出现次数:"+toGetTheMostCharsByArray(test).num); console.info("使用字符串匹配,出现最多的字符串为:"+toGetTheMostCharsByRegex(test).value+" ,出现次数:"+toGetTheMostCharsByRegex(test).num);
注意这里的正则判断重复字母,先留个位置为介绍正则......
13) 请问一下两段代码有什么不同?
//1. setTimeout(function(){ /*代码块*/ setTimeout(arguments.callee,10); },10); //2. setInterval(function(){ /*代码块*/ },10);
分析
javascript的引擎是单线程的
javascript的引擎是基于事件驱动的
setTimeout和setInterval都是往事件队列中增加一个待处理时间而已,setTimeout只触发一次,而setInterval是循环触发
setTimeout(function(){
//代码块
setTimeout(arguments.callee,10);
},10);
//上段代码可使得setTimeout循环触发。但是,执行完这段代码块才挂起时间,所以两次执行时间会大于10ms
setInterval(function(){
/*代码块*/
},10);
//而上段代码,是自动在10ms的时候挂上这个事件,所以两次事件的相隔会小于等于10ms。
当线程阻塞在一个事件的时候,不管是使用setInterval还是setTimeout都需要等待当前事件处理完才能执行。
由这个问题,我们引入下面这个话题:
14)以下代码弹出的是?
alert(1&&2);
分析
a && b : 若a为真,返回b,若a为假,返回a。故弹出2.
同理,|| 前面为假取后,前面为真取前。
15)f能取到a,b吗?原理是什么??
var F = function(){}; Object.prototype.a = function(){}; Function.prototype.b = function(){}; var f = new F();
分析:
f.a === Object.prototype.a //=> true f.b === Function.prototype.b //=> false
f 的原型链:
f -----> F.prototype -----> Object.prototype -----> null
F 的原型链:
F -----> Function.prototype -----> Object.prototype -----> null
----图来自《Node.js 开发指南》
f(F的实例对象)能访问a
F(函数对象)能访问a,b