前端技术的发展速度大家有目共睹,js的ECMA标准也不再是3的天下,但不管再怎么山雨欲来风满楼,基础语法还是得温故而知新。无论你是初学则还是多年js的编程者,都可以试着做做下面的测试题,我相信总还是会有些收获的。因为全部是自己总结和手打的,有纰漏错误之处请留言,谢谢。
一:考察基本数据类型与运算符
(1)未定义变量问题
var a;console.log(typeof a); ==>undefined
先以一个最常见也是最简单的测试案例开始,未定义的变量或者未赋值则为undefined
(2)++/--运算符问题
var a = '1a';console.log(a++); ==>NaN
++操作符将对a变量隐式转换为number类型,字符串'1a'无法正常转换为数字,所以返回NaN。
这里可以总结出以下几点:
1. 除了+操作符以外,-、*、/、%、++、--都将隐式转换参与运算的变量为number类型,如果能正常转换为数字则返回该数字,否则返回NaN;
2. 能正常转换为数字的有如下几种情况:
纯数字类字符串如'123'、'1e2'(转换为1*10的2次方等于100);
null参与运算转换为0(null-4等于-4,null+null等于0);
boolean类型的true和false分别可转换为1和0;
3. 只有能够转换为数字的变量才能参与++或--运算,否则将报错,比如:
console.log(3++); ==>报错,常量不能直接运算++var a = 3;console.log(a++); ==>3
其中有个特例:NaN++可以正常返回结果,仍然是NaN
(3)+运算符之四则运算与字符合并
console.log(2+'1'); ==>'21'console.log(2-'1'); ==>1console.log(1+NaN); ==>NaNconsole.log('1'+NaN); ==>'1NaN'
1. +运算符有必要单独一提,因为除了+以外的其它算术运算符,都会对参与运算的变量或值默认隐式转换为number,但+因为在js中还肩负着字符串合并的重大任务,所以它有特殊'国情':
1.1 当+的左右两边均是数字类型时,则执行算术运算,比如1+2返回3、NaN+1返回NaN;
1.2 当+的左右两边均是字符串类型时,则执行字符串合并,比如'1'+'2'返回'12';
1.3 当+的左右两边一个是数字、一个是字符串类型时候,则先将数字转换为字符串再合并,比如1+'1'返回'11';
2. 在实际开发过程中,我们使用+运算符基本上都是处理字符串类型或者数字类型的,所以上面的三种情况能够应对大部分开发场景。但是当+运算符的左右两边出现了第三种数据类型时,情况就会显得复杂了(以下其它数据类型是指除了string和number的以外类型):
2.1 其它数据类型+字符串类型
console.log(null+'1'); ==>'null1'console.log(undefined+'1'); ==>'undefined1'console.log([1,2]+'1'); ==>'121'console.log((function(){})+'1'); ==>'function (){}1'console.log(({})+'1'); ==>'[object Object]1'
执行办法:其它数据类型将被转换为字符串再合并,且对象类型将调用自身toString()方法转换结果。另外,其实对象类型+运算无论另外一个参与运算的值是不是字符串类型,都只能与它进行字符串合并操作,而不能是四则运算,下面会具体讲到。
2.3 其它数据类型+数字类型
console.log(null+1); ==>1console.log(undefined+1); ==>NaNconsole.log(true+1); ==>2console.log([1]+1); ==>'11'console.log((function(){})+1); ==>'function (){}1'console.log(({})+1); ==>'[object Object]1'
执行办法:非对象类型将进行四则运算,而对象类型场景则仍然是字符串合并的操作。
2.2 其它数据类型+其它数据类型
console.log(null+null); ==>0console.log(undefined+undefined); ==>0console.log(true+true); ==>2console.log([1]+[2]); ==>'12'console.log((function(){})+(function(){})); ==>'function (){}function (){}'console.log(({})+({})); ==>'[object Object][object Object]'
执行办法:非对象类型将进行四则运算,而对象类型场景则仍然是字符串合并的操作。
所以,关于有其它类型参与+运算的场景,我们可以再稍总结下最终的结果:只要有对象类型参与+运算,则永远是做字符串合并;如果是null/undefined/boolean类型参与+运算,当另一个参与运算的值为字符串,则进行字符串合并,否则进行四则运算。
思考题:alert({name:'mofei'})
(4)+运算符之转换数字问题
console.log(1+ +1); ==>2console.log(1e+1+1); ==>11
这个测试仍然是+运算,但是并没有和上一个测试案例合并在一起而是单独出来,原因很简单:此+非彼+。
+运算符实际上在js中有三种应用场景:转换为数字、四则运算加、字符串连接。在上一个测试案例中我们讲了后两种应用场景,而这里是则是对第一个应用场景的补充。关于转换为数字的理解也不难,我们经常用a+''的写法将一个变量转换为字符串类型,所以我们也可以利用+运算符将其它类型转换为数值类型,比如+null返回结果为0。所以在这个案例中,1+ +1等同与1+1,因为后面那个+1其实就是将1转换为number类型,仍然是1,而1e+1+1等效于1e1+1,也就是11。
(5)除/取模运算符的极端场景问题
console.log(10/0); ==>Infinityconsole.log(10%0); ==>NaN
除0返回Infinity,对0取模(求除法运算的余数,对2取模常用在判断奇偶)返回NaN。
(6)!运算符问题
console.log(!!false); ==>falseconsole.log(!!4); ==>trueconsole.log(!!'false'); ==>true
这里考察了取反运算符。双取反实际上就是将运算对象强制转换为boolean型,其中最后一个要注意'false'本身是个非空字符串,转换为布尔值为true。
大部分情况下转布尔值都是返回true,除了以下几种情况:false本身、null、nudefined、空字符串''、数字0、数字NaN
思考题:!!undefined
(7)typeof运算符问题
console.log(typeof(0)); ==>number console.log(typeof(NaN)); ==>numberconsole.log(typeof('0')); ==>stringconsole.log(typeof('false')); ==>stringconsole.log(typeof(false)); ==>booleanconsole.log(typeof(undefined)); ==>undefinedconsole.log(typeof(null)); ==>objectconsole.log(typeof([1,2])); ==>objectconsole.log(typeof(function(){})); ==>function
这里罗列了typeof可以返回的所有可能值:number、string、boolean、undefined、object、function
可以总结的知识点是:
1. 凡是带上引号的typeof就是string,而不需要管里面内容是什么,比如不要误以为typeof('undefined')结果就是undefined
2. null、数组array、普通对象的typeof返回为object
3. 函数本身也是对象,但是它的typeof返回为特殊的function
4. 并不能依靠typeof全部分别出变量到底属于8种数据类型中的哪一种(比如数组和普通对象),但是可以直接区分基本数据类型中的四种类型:number、string、boolean、undefined,如果要全部区分类型可以结合其它方法共同判断(如instanceof、constructor),或者使用Object.prototype.toString.call(...)
(8)宽松等于严格等问题
console.log(null == undefined); ==>trueconsole.log(false == ''); ==>trueconsole.log(false === !true); ==>trueconsole.log([] === []); ==>false
关于等于运算符==:比较双方是否相等,这种比较是允许进行类型转换的,比如1 == '1'将返回true,至于它们是怎样进行类型转换可以参照《javascript权威指南》75页有非常详细的描述,这里不再阐述了。严格等运算符经常在对象的比较中被考察,我们要知道对象的比较是通过引用比较的,所以在这个测试案例中,它们虽然看上去很像,其实是两个不同的对象(这里也是数组),它们并没有引用同一个地址,所以并不严格等,对象严格等的情况如下:
var a = [];var b = a;console.log(a === b); ==>true
b变量被赋值为a,a本质是对象,对象赋值为引用赋值,此时a和b公用一个地址,所以严格等成立。
此外,关于==还有个比较经典的问题:
console.log([] == ![]); ==>true
因为!运算符优先级原因,将先执行![],所以这里等效于 [] == false,按照常识在boolean环境下空数组是转换为true的,但是实际上在==环境中,是通过转换为数字或字符串来比较的,当操作值有boolean类型时转换为数值比较,也就是[]转换为number类型为0,false转换为number类型为0,所以结果返回true。看上去这些繁琐的转换实在令人担忧,但实际也没有这么糟糕,毕竟这种看上去“非人性”的结果在实际开发场景中极少遇到,当你有一定的经验的时候,绝大部分宽松相等的结果你还是能够一眼识破的。
(9)或与问题
console.log(1&&2); ==>2console.log(1||2); ==>1console.log(1&&a=2); ==>errorconsole.log(1&&(a=2)); ==>2
关于&&和||两个运算符,也是笔试中的常客。这里有几点需要注意:
1. &&和||返回的是表达式的值,而并不是true或者false
2. 从左到右执行,&&左侧的表达式返回值如果转换布尔型为true时,则将执行右侧的表达式;||左侧的表达式返回值如果转换布尔型为true时,则直接返回该表达式返回值且不再执行右侧表达式
有了以上两点,前两个测试则比较容易理解了,而对于1&&a=2,实际等同于——(1&&a)=2——undefined=2,执行将报错;只要给a=2加上小括号优先执行则可以正常运行,其中a=2的返回值为2,所以最终表达式的值为2。
(10)浮点数字不精确问题
console.log(0.1*0.1); ==>0.010000000000000002
在js中采用的是一种二进制表示法,可以精确的表示分数,比如1/2、1/8、1/1024。但是其它浮点值实际上只是真实值的一个近视表示,比如0.1,二进制浮点数表示法并不能精确的表示0.1这样简单的数字。所以,当遇到小数点的四则运算的时候需要特殊处理,最典型的场景就是支付了,比如1.1元每个,用户购买3个,则不能直接使用相乘结果,可以使用toFixed控制结果,或者以乘法为例可使用以下方法保证精确性:
//乘法处理function FloatMul(arg1,arg2) { var m=0,s1=arg1.toString(),s2=arg2.toString(); try{m+=s1.split(".")[1].length}catch(e){} try{m+=s2.split(".")[1].length}catch(e){} return Number(s1.replace(".",""))*Number(s2.replace(".",""))/Math.pow(10,m); }