Fork me on GitHub

试试看 ? 离奇古怪的javascript题目

来源地址:

http://dmitrysoshnikov.com/ecmascript/the-quiz/#q1

 

另一篇帖子 看看国外的javascript题目,你能全部做对吗?

http://www.cnblogs.com/aaronjs/p/3172112.html

答案都是自己总结的,毕竟是人肉解释器,解释不一定完全正确,如有不对欢迎大家指出!

 

题目1. 结果是什么?

typeof typeof(null)
  • “undefined”
  • SyntaxError
  • “string”
  • “object”
  • TypeError
答案

 

题目2. 下面的检测是否等价?

typeof foo == 'undefined'
typeof foo === 'undefined'
  • Yes
  • No
答案

 

题目3. 结果是什么?

100['toString']['length']
  • 100
  • 3
  • 1
  • 8
  • 0
  • SyntaxError

这个估计好多人想不通了

JavaScript 中所有变量都是对象,除了两个例外 null 和 undefined

false.toString(); // 'false'
[1, 2, 3].toString(); // '1,2,3'
function Foo(){}
Foo.bar = 1;
Foo.bar; // 1

一个常见的误解是数字的字面值(literal)不是对象。这是因为 JavaScript 解析器的一个错误, 它试图将点操作符解析为浮点数字面值的一部分。

2.toString(); // 出错:SyntaxError

例如以下这种就OK

true.toString()
"true"

false.toString()
"false"

01.toString()
"1"

0x20.toString()
"32"

true.toString()、false.toString()、02.toString()、 0x20.toString()都是可以的

我仔细查了下,网上大多给的解释

2.toString()出错是因为解释器读到“2. ”不知道这个“点”究竟该作为小数点还是“.”操作符, 或者也就是说在这里产生了一个“shift-shift conflit”

有很多变通方法可以让数字的字面值看起来像对象

2..toString(); // 第二个点号可以正常解析
2 .toString(); // 注意点号前面的空格
(2).toString(); // 2先被计算

 

访问100['toString']的length返回toString方法形参个数;

换个

(function(a,b){
    //输出2
})['length']
fn = {
   f :  function(a,b,c){
    //3
        }
};

fn.f['length']

有长度属性的函数,这个长度就是用来计算多个参数的,

可以分解为

100['toString'].length

由于toString是个方法,所以它length属性返回的是toString的形参个数,而toString方法可以接收一个radix(基数)作为形参(比如:toString(2),返回该数值的二进制,16则代表16进制),所以最终返回结果是1。

function toString( [radix : Number] ) : String

radix 可选项。为将数字值转换为字符串指定一个基数。此值仅用于数字。

所以当数字调用toString的时候,会有一个参数radix,其它的比如:Function、Array、String这些调用的话,结果会是 0

刚才上面的100['toString']['length']为什么不分解成100.toString.length?

这是是由于100后面的.是小数点之后是小数部分,从而导致语法错误

解决的方法也说了

100..toString.length
1

参考资料

http://www.dengpeng.org/archives/615

 

题目4:结果是什么?

var a = (1,5 - 1) * 2
  • 0.999999999
  • 1
  • 0.5
  • 8
  • -0.5
  • 4

这里主要是理解这个 逗号操作符了

在for循环中,增量表达式

j = 10;
for (i = 0; i <5; i++, j++) {
    k = i + j;
  }
i j  k
0 10 10 
1 11 12
2 12 14 
3 13 16 
4 14 18

允许多个表达式被当作单个表达式,所以这两个变量可以递增。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator

 

当有一个包含一个或多个逗号表达式,表达式的值等于最后一个值

var a = (1,2,3,4)   //4

image

* 逗号操作符的最佳用途是定义多个变量和示例使用这里是绝对不推荐的

答案呼之欲出了就是8,

 

题目5 结果是什么?

var x = 10;
var foo = {
    x: 20,
    bar: function () {
        var x = 30;
        return this.x;
    }
};

console.log(
    foo.bar(),             // 1.
    (foo.bar)(),           // 2.
    (foo.bar = foo.bar)(), // 3.
    (foo.bar, foo.bar)()   // 4.
);
  • 20, 20, 20, 20
  • 20, 20, 10, 10
  • 20, 20, 20, 10
  • 20, 20, 10, 30
  • 20, 10, 10, 10
  • 20, 10, 30, SyntaxError

我们一句一句的分解

foo.bar()

这个很简单,直接调用bar 方法,此时的上下文就是foo对象,所以this.x = 20  return 的结果也就是20了

 

(foo.bar)()

我们看看括号的定义 ECMA语言规范

11.1.6 The Grouping Operator

The production PrimaryExpression : ( Expression ) is evaluated as follows:

    1. Evaluate Expression. This may be of type Reference.
    2. Return Result(1).

NOTE 

This algorithm does not apply GetValue to Result(1). The principal 
motivation for this is so that operators such as delete and typeof 
may be applied to parenthesised expressions.

引用类型的值与函数上下文中的this值如何相关?——从最重要的意义上来说。 一个函数上下文中确定this值的通用规则如下:

在一个函数上下文中,this由调用者提供,由调用函数的方式来决定。如果调用括号()的左边是引用类型的值,this将设为引用类型值的base对象(base object),在其他情况下(与引用类型不同的任何其它属性),这个值为null。不过,实际不存在this的值为null的情况,因为当this的值为null的时候,其值会被隐式转换为全局对象。注:第5版的ECMAScript中,已经不强迫转换成全局变量了,而是赋值为undefined。

这是对组操作符的描述,说明得到的结果是一个 Reference 引用类型

alert( foo.bar === (foo.bar) )   //true

关于引用类型的解释,参考

http://dmitrysoshnikov.com/

 

(foo.bar = foo.bar)(),

这一题我也做错了,呵呵

查了查资料,我们先看看

var foo = { 
     bar: function () {    
              alert(this);    
              alert(this === foo); 
     }
};
 

foo.bar(); // foo, true

var exampleFunc = foo.bar;

alert(exampleFunc === foo.bar); // true

// 再一次,同一个function的不同的调用表达式,this是不同的
exampleFunc(); // global, false

可见将foo.bar的引用赋给一个变量,在执行的时候,这个this的指向就被改变了

 

重点(=)赋值是如何处理的?

不懂就翻规范呗,又去找找ECMA语言规范

11.13.1 Simple Assignment (= )
The production AssignmentExpression : LeftHandSideExpression = AssignmentExpression is evaluated as follows:

1. Evaluate LeftHandSideExpression.

2. Evaluate AssignmentExpression.

3.Call GetValue(Result(2)).

4.Call PutValue(Result(1), Result(3)).

5.Return Result(3).
8.7.2 PutValue(V, W)

1. If Type(V) is not Reference, throw a 
   ReferenceError exception.
2. Call GetBase(V).
3. If Result(2) is null, go to step 6.
4. Call the [[Put]] method of Result(2), passing GetPropertyName(V) 
   for the property name and W for the value.
5. Return.
6. Call the [[Put]] method for the global object, passing 
   GetPropertyName(V) for the property name and W for the value.
7. Return.

我也没有看太明白, 还要慢慢消化,但是大体的定义

赋值运算符调用了GetValue方法。返回的结果是函数对象(但不是引用类型),这意味着this设为null,结果是global对象

语句返回的是 foo.bar 的函数值

因此,赋值操作符返回的是「值(Value)」而不是「引用(Reference)」。

因为函数体需要 this 值获取 x 属性的值,那么接下来我们考虑改函数时调用时的上下文作用域以及背后的具体流程。 尤其注意第七条规则

...
6. If Type(Result(1)) is Reference, Result(6) is GetBase( Result(1)). 
    Otherwise, Result(6) is null.
7. If Result(6) is an activation object, Result(7) is null. Otherwise,
    Result(7) is the same as Result(6).
8. Call the [[Call]] method on Result(3), providing Result(7) as 
    the this value and providing the list Result(2) as the 
    argument values.
…

那么在这种情况下,GetBase 操作实际上返回的是 null,因此此条语句函数执行的作用域为 global ,在浏览器中也就是 window 。

(foo.bar = foo.bar)()

那么,上面的语句中我们可以得知

  1. Grouping Operator 中的赋值语句返回的是 foot.bar 的函数值(「Value」)
  2. 该函数执行的上下文作用域为 window

找了几篇相关文章具体参考:

http://www.w3.org/html/ig/zh/wiki/ES5/%E8%A1%A8%E8%BE%BE%E5%BC%8F

http://www.cnblogs.com/TomXu/archive/2012/01/17/2310479.html

 

(foo.bar, foo.bar)()

逗号运算符和逻辑运算符(OR)调用了GetValue 方法,相应地,我们失去了引用而得到了函数。并再次设为global。

 


我们似乎可以总结一下:

JavaScript 是弱类型语言,这就意味着,等于操作符会为了比较两个值而进行强制类型转换

两个等号组成:==

""           ==   "0"           // false
0            ==   ""            // true
0            ==   "0"           // true
false        ==   "false"       // false
false        ==   "0"           // true
false        ==   undefined     // false
false        ==   null          // false
null         ==   undefined     // true
" \t\r\n"    ==   0             // true

严格等于操作符由个等号组成:===

""           ===   "0"           // false
0            ===   ""            // false
0            ===   "0"           // false
false        ===   "false"       // false
false        ===   "0"           // false
false        ===   undefined     // false
false        ===   null          // false
null         ===   undefined     // false
" \t\r\n"    ===   0             // false

== 被广泛认为是不好编程习惯的主要原因, 由于它的复杂转换规则,会导致难以跟踪的问题。

此外,强制类型转换也会带来性能消耗,比如一个字符串为了和一个数字进行比较,必须事先被强制转换为数字。

关于Object.prototype.toString方法的原理参考

https://developer.mozilla.org/zh-CN/docs/JavaScript/Reference/Global_Objects/Object/toString

http://www.cnblogs.com/ziyunfei/archive/2012/11/05/2754156.html

 

后面还有5道题,楼主要深思熟虑才行…

JS越学越谦卑….

后面还有5道题,楼主要深思熟虑才行…

JS越学越谦卑….

后面还有5道题,楼主要深思熟虑才行…

JS越学越谦卑….

还有5道题,需要深思熟虑了...

PS:JS越学越谦卑........

posted on 2013-09-26 10:17  【艾伦】  阅读(6046)  评论(8编辑  收藏  举报