IT技术博客大学习 共学习 共进步

从另外两道题说起

Miller 2010-08-17 23:05:33 浏览 2,762 次

最近貌似流行做Javascript题,Dmitry A. Soshnikov这又出了几道题-The quiz,还可以自我评分,有兴趣的可以去试试看自己可以得几分。本文主要挑了2道比较绕的题目来说几个知识点:

1. Javascript中的”,”(逗号)操作符

2. Javascript中”;”(分号)的重要性

3. Javascript中with块中的作用域

下面请看题目。

题目1

以下是代码片段:
var
  b = 10,
  c = (
    20,
    function (x) { return x + 100},
    function () { return arguments[0]}
  );

a = b + c
({x: 10}).x;

要说这道题先来说说Javascript中的逗号,Javascript中的逗号大概在以下几处会用到:

以下是代码片段:
//1. 变量申明
var v1, v2;

//2. 数组定义
var arr = [1,2,3];

//3. 函数参数
function func(arg1,arg2){};
func(1,2);

//4. 操作符(for语句中的循环执行表达式)
for( var i = 0, j = 0; i < len; i++,j++){}

其中用法1-3是比较常见的,而用法4中很多人并没有注意,for语句最后的”i++,j++”正式一个包含逗号操作符的表达式,在MDC中对逗号操作符做了如下描述:

You can use the comma operator when you want to include multiple expressions in a location that requires a single expression.

实际上,逗号操作符会从左往右依次计算每个表达式的值并返回最后一个表达式的计算结果。会到题中,变量c的赋值正是一个逗号操作符运算,因此c的值为function(){return arguments[0]}。

之后看a的赋值”b+c”,表面看貌似一个整数加一个函数,实际上因为c之后没有分号,因此c会与第二行的”({x:10})”结合,相当于”c({x:10})”,因此最后a的值为”10+10″。关于分号问题,下面有一段更常见的代码:

以下是代码片段:
var a = function(){ return arguments[0];}
(function(x){
    return x;
})(100);

最后a的值为100,因此在给变量申明时务必记得加分号。

题目2

以下是代码片段:
({
  x: 10,
  foo: function () {
    function bar() {
      console.log(x);
      console.log(y);
      console.log(this.x);
    }
    with (this) {
      var x = 20;
      var y = 30;
      bar.call(this);
    }
  }
}).foo();

这里主要是要搞清楚with块的作用,with(obj)使用时只是将obj放置在作用域链的最前端,而并不会像函数那样形成一个完整的活动对象(包括局部变量、arguments、函数等),因此在with块中使用var声明变量时,而实际上还是在with块之外创建了局部变量,并不是with块的局部变量。同样,在with块中定义的函数实际上也是属于with块外层。另外,在with块中给变量赋值时则会先从obj对象中查找变量,如果找不到则依次按照作用域链查找。 因此,以上的with块的代码可以这样分解:

以下是代码片段:
({
  x: 10,
  foo: function () {
    function bar() {
      console.log(x);
      console.log(y);
      console.log(this.x);
    }

    var x,y;
    with (this) {
      x = 20;
      y = 30;
      bar.call(this);
    }
  }
}).foo();

在代码解析的时候,会在foo函数中创建两个局部变量x,y(初始值都为undefined)。而在运行时的with块中,给x赋值时因为首先找到的变量是this(也就是最外层的那个对象)中的x属性,因此this.x=20;这也造成了作用域链中位于this上一级的foo的上下文(context)中的局部变量x没有被赋值。同理,with块中给y赋值时,按作用域链查找到得是foo中的局部变量,因此局部变量y=30。最后bar在执行的时候,x/y均是访问foo中的局部变量,因此一个为undefined,一个为30,而this.x为20。

呃…要说清楚还真绕……!

建议继续学习

  1. javascript的词法作用域 (阅读 4,545)
  2. 有趣的变量作用域-PHP中global和Javascript中的var关键字 (阅读 3,843)
  3. 闭包与作用域 (阅读 3,722)
  4. javascript作用域和作用域链 (阅读 3,383)
  5. 认识javascript中的作用域和上下文 (阅读 3,385)
  6. JavaScript 函数、作用域和继承 (阅读 3,285)
  7. JavaScript 中的 相等检测 (阅读 3,282)
  8. Javascript作用域原理 (阅读 3,083)
  9. 样式的作用域──页面重构中的模块化设计(一) (阅读 2,842)
  10. JavaScript的作用域和提升机制 (阅读 2,681)