http://davidshariff.com/js-quiz上看到一些比较有意思的输出结果题,决定总结一下。
js的各种小知识总是把人绕晕,慢慢来剖析一下。

问题一

1
2
3
4
var foo = function foo() {
console.log(foo === foo);
};
foo();

答案:true
分析:foo是分配给函数的变量,foo的类型是function,由于这是一个函数直接量,所以函数名本身是完全不相关的。
所以

1
2
3
4
5
6
7
8
9
var foo = function sb() {
console.log(foo === foo);
};
foo();

var foo = function() {
console.log(foo === foo);
};
foo();

都是一样的


问题二

1
2
3
4
5
6
7
function aaa() {
return
{
test: 1
};
}
alert(typeof aaa());

答案:undefined
分析:这理论上应该返回一个object,但其实js会解析成

1
2
3
4
5
6
function aaa() {
return;
{
test: 1
};
}

自动补全分号,所以说:一定要写分号


问题三

1
Number("1") - 1 == 0;

答案:true
分析:Number()函数的规则:

数据类型 转换规则
Boolean true->1,false->0
Number 简单的传入传出
null 0
undefined NaN
String 只含有数字(无论正负号,浮点,进制),都转换成十进制整数值,空转换为0,其他字符转为NaN

由于Number()函数在转换字符串时比较复杂而且不够合理,因此处理整数的时候更常用的是parseInt()函数,parseInt()函数在转换字符时,更多的是看其是否符合数值模式。它会忽略字符串前面的空格,一直解析所有字符或者遇到了一个非数字字符。


问题四

1
(true + false) > 2 + true;

答案:false
分析:要知道Boolean类型的转换规则

数据类型 转换为true的值 转换为false的值
Boolean true false
String 任何非空字符串 “”(空字符串)
Number 任何非零数字值(包括无穷大) 0和NaN
Obeject 任何对象 null
Undefined undefined

问题五

1
2
3
4
5
6
7
function bar() {
return foo;
foo = 10;
function foo() {}
var foo = '11';
}
alert(typeof bar());

答案:function
分析:首先最容易的部分:return立即返回。这意味着return语句下的一切永远不会执行正确的?
其实不是的,还考察函数声明方式的差异

函数定义的方式 function语句 Function构造函数 函数直接量
例子 function eg(a,b){return a+b} var eg = new Function(“a”,”b”,”return a+b”) var eg =function(a,b){return a+b}
兼容 完全 js1.1以上 js1.2以上版本
形式 句子 表达式 表达式
名称 有名 匿名 匿名
性质 静态 动态 静态
解析时机 优先解析 顺序解析 顺序解析
作用域 具有函数的作用域 顶级函数(顶级作用域) 具有函数作用域

function foo()优先解析 所以bar()返回foo,typeof foo答案为function
那接下来我们来改一下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
function bar() {
return foo;
foo = 10;
var foo = function foo() {}
var foo = '11';
}
alert(typeof bar());
//undefined 没有了函数声明
//而是被一个函数直接量foo在编译时创建的“未定义”的警告来代替。

//再改!
function bar() {
foo = 10;
return foo;
function foo() {}
var foo = '11';
}
alert(typeof bar());
//number
function bar() {
foo = 10;
function foo() {};
return foo;
var foo = '11';
}
alert(typeof bar());
//number
//很奇怪,为啥是number
//其实在这两种情况下,函数foo虽然是在编译时创建的
//但在执行过程中,我们写在它前面的全局变量foo已经把函数覆盖掉
function bar() {
foo = 10;
function foo() {};
var foo = '11';
return foo;
}
//string
//函数foo再次在编译时创建的,而全局变量foo覆盖它,最后本地变量foo字符串又覆盖了全局变量。
//这个时候再在函数外面alert(foo)会报错哦~


问题六

1
"1" - - "1";

答案:2
分析:一元加和减操作符在对非数值应用时,该操作符会像Number()转型函数一样对这个值执行转换。要注意“-”之间有空格,会被解释成1-(-1),若没有这个空格,会报错。


问题七

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var x = 3;

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

var go = foo.baz.bar;

alert(go());
alert(foo.baz.bar());

答案:3,1
分析:this是js的一个关键字,随着函数使用场合不同,this的值会发生变化。
•this对象是在运行时基于函数的执行环境绑定的。在全局函数中,this等于window,而当函数被作为某个对象的方法调用时,this等于那个对象。
•也就是说this关键字总是指代调用者。
因此go()的调用者是window,所以alert(go())返回的是3
第二个alert()由父对象访问它的实例化功能。由于bar()函数被foo.baz调用,所以关键字this指向foo.baz,而foo.baz.x值为1,最后输出1


问题八

1
new String("This is a string") instanceof String;

答案:true
分析:创建了个新的String对象,当然是String的实例了。

那么问题来了,new都干啥了?
1、创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。
2、属性和方法被加入到 this 引用的对象中。
3、新创建的对象由 this 所引用,并且最后隐式的返回 this 。

问题又来了 instanceof 和typeof有啥区别?
typeof返回一个表达式的数据类型的字符串,返回结果为js基本的数据类型,
包括number,boolean,string,object,undefined,function.语法为typeof(data) 或 typeof data
instanceof则为判断一个对象是否为某一数据类型,或一个变量是否为一个对象的实例;返回boolean类型
语法为 o instanceof A
那。。。
window instanceof Object?
arguments instanceof Array??
答案当然都为false啦,
原谅精神患者思维广-!-


问题九

1
[] + [] + 'foo'.split('');

答案:"f,o,o"
分析:+会把[]转换为””或0,[]+[]会转换成””
‘foo’.split(‘’)为[‘f’,’o’,’o’]
于是。。问题来啦~为什么 ++[[]][+[]]+[+[]] = 10?这里就不多解释了
可以看迷渡的http://justjavac.com/javascript/2012/05/24/can-you-explain-why-10.html


问题十

1
new Array(5).toString();

答案:",,,,"
分析:这貌似没啥说的吧,new Array(5),开辟了长度为5,每项都为空字符串的数组


问题十一

1
2
3
4
var myArr = ['foo', 'bar', 'baz'];
myArr.length = 0;
myArr.push('bin');
console.log(myArr);

答案:['bin']
分析:设定一个数组的长度小于它的“当前长度”,会截断它。数组的长度设置为0都清空了。


问题十二

1
String('Hello') === 'Hello';

答案:true
分析:转换为字符串有两种方式

  1. 几乎所有值都有的toString()方法
    数值、布尔值、对象和字符串都有一个toString()方法,null、undefined值没有这个方法。因此null.toString()和undefined.toString()会报错。

  2. 转型函数String()
    转换规则:
    1.如果值有toString()方法,调用该方法并返回相应结果
    2.如果值为null,则返回“null”
    3.如果值为undefined,则返回“undefined”
    那么问题来了。。

    1
    2
    new String('Hello') === 'Hello';//false,一个object 一个string嘛
    new String('Hello') == 'Hello';//true,啥???

这就是“===”和“==”的问题了,比如说object->valueOf()->toString()->etc.


问题十三

1
2
3
4
5
6
7
8
var x = 0;
function foo() {
x++;
this.x = x;
return foo;
}
var bar = new new foo;
console.log(bar.x);

答案:undefined
分析:这是啥???为啥两个new
我们可能会想因为两个new所以x会自增到2,其实不是这样。因为foo会返回自己的函数定义。
先看看这个

1
2
3
4
5
6
7
var x = 0;
function foo() {
x++;
this.x = x;
}
var bar = new foo();
console.log(bar.x);//1,没问题吧?有问题?!一个是全局一个是局部啊,少年郎

上面打印1是因为这里foo返回的不是一个函数定义。变量bar被赋值{x:1}。
回归到原来的问题,foo是一个函数声明返回本身,使得bar.x不确定,
因为它是内部的函数foo和返回对象上的不可用的属性。
如果bee = new bar;或者bee = new bar();这会报错。


问题十四

1
"This is a string" instanceof String;

答案:false
分析:
字符串”This is a string’并不包含在String原型的构造里,它不像之前出现的问题。
对比感受一下。

1
typeof "This is a string" ==="string"//true


问题十五

1
2
3
4
5
6
7
var bar = 1,
foo = {};
foo: {
bar: 2;
baz: ++bar;
};
foo.baz + foo.bar + bar;

答案:NaN
分析:传统意义上我们会这么写

1
2
3
4
5
6
7
var bar = 1,
foo = {};
foo={
bar: 2
baz: ++bar
};
foo.baz + foo.bar + bar;//6,这是毫无疑问的

我们想想什么时候会用到冒号,1.声明对象的成员2.switch语句分支3.三元表达式
如果单独运行

1
2
3
4
foo: {
bar: 2;
baz: ++bar;
};

会报错。而原题并没有报错,这是因为最开始的时候声明了foo为一个{},后来的代码块实际上并没有改变变量foo。
因此只是undefined + undefined +1 结果为NaN


问题十六

1
2
3
var myArr = ['foo', 'bar', 'baz'];
myArr[2];
console.log('2' in myArr);

答案:true
分析:数组本身也是Object,尽管本身的原型链不太相同,但是属性的组织结构大致一样。


问题十七

1
2
3
4
5
var arr = [];
arr[0] = 'a';
arr[1] = 'b';
arr.foo = 'c';
alert(arr.length);

答案:2
分析:当添加一个foo属性时,只是把属性添加到数组对象上,而不会占有数组对象的整数索引,并没有增加一个索引值。
数组仅仅根据他的索引值增加长度,所以长度还是为2


问题十八

1
10 > 9 > 8 === true;

答案:false
分析:作为一个人,可能会理解为(10>9)&&(9>8)答案当然为true;
但是作为Js,它会理解为(10>9)>8,显而易见 true>8,根据Number()转换规则,1>8。剩下就不用多说了。


问题十九

1
2
3
4
5
function foo(a, b) {
arguments[1] = 2;
alert(b);
}
foo(1);

答案:undefined
分析:考察函数的参数
•函数的参数:arguments对象
arguments是表示函数的实际参数(与形参无关),因此b仍然为undefined。
•callee函数(回调函数属性)
arguments对象的 callee属性:返回arguments对象所属的函数的引用,这相当于在自己的内部调用自己。
arguments.cellee.length == arguments.length;
注:与caller区分,caller:functionName.caller 返回调用者。
js的参数数组可以是任意大小,并且所有的参数捆绑到arguments对象,但这并不意味着可以单独定义为捆绑的参数。
同样的问题

1
2
3
4
5
function foo(a, b) {
arguments[1] = 2;
alert(b);
}
foo(1,3);

这个问题的答案就是2了。


问题二十

1
NaN === NaN;

答案:false
分析:这个就不用做过多解释了,NaN,即not a number,本身不等于本身
isNaN()函数:不能返回数值时返回true。在对object类型进行验证时,先调用对象的valueOf()方法,然后确定该方法返回的值是否可以转换为数值。如果不能,则基于这个返回值再调用toString()方法,再测试返回值。

最后的福利:
英语版本:http://www.ecma-international.org/ecma-262/5.1/

就知道英语不好↓ ↓ ↓ 颜海镜这里有中文版
http://yanhaijing.com/es5