当前位置:起点学习辅导网培训频道电脑知识学习网页制作Javascript教程JavaScript技巧与高级特性

JavaScript技巧与高级特性

08-08 00:39:50浏览次数:727栏目:Javascript教程
标签:javascript视频教程,javascript教程下载, JavaScript技巧与高级特性,
function 对象的 length 属性可以用来获取声明 function 时候指定的形式参数的个数。如前所述,function 对象被调用时的实际参数是通过 arguments 来获取的。

with

with 语句的语法是with ( Expression ) Statement。 with 会把由 Expression 计算出来的对象添加到当前执行上下文的作用域链的前面,然后使用这个扩大的作用域链来执行语句 Statement,最后恢复作用域链。不管里面的语句是否正常退出,作用域链都会被恢复。

由于 with 语言会把额外的对象添加到作用域链的前面,使用 with 可能会影响性能并造成难以发现的错误。由于额外的对象在作用域链的前面,当执行到 with 里面的语句,需要对一个标识符求值的时候,会首先沿着该对象的 prototype 链查找。如果找不到,才会依次查找作用域链中原来的对象。因此,如果在 with 里面的语句中频繁引用不在额外对象的 prototype 链中的变量的话,查找的速度会比不使用 with 慢。具体见代码清单 9。


清单 9. with 的用法示例

function A() {
    this.a = "A";
 }

 function B() {
    this.b = "B";
 }

 B.prototype = new A();

 function C() {
    this.c = "C";
 }

 C.prototype = new B();

 (function () {
    var myVar = "Hello World";
    alert(typeof a);    // 结果是 "undefined"
    var a = 1;
    var obj = new C();
    with (obj) {
        alert(typeof a); // 结果是 "string"
        alert(myVar);    // 查找速度比较慢
    }
    alert(typeof a);     // 结果是 "number"
 })();
在代码中,首先通过 prototype 的方式实现了继承。在 with 中,执行alert(typeof a)需要查找变量 a,由于 obj 在作用域链的前面,而 obj 中也存在名为 a 的属性,因此 obj 中的 a 被找到。执行alert(myVar)需要查找变量 myVal,而 obj 中不存在名为 myVal 的属性,会继续查找作用域链中后面的对象,因此比不使用 with 的速度慢。需要注意的是最后一条语句alert(typeof a),它不在 with 里面,因此查找到的 a 是之前声明的 number 型的变量。


闭包

闭包(closure)是 JavaScript 中一个非常强大的功能。如果使用得当的话,可以使得代码更简洁,并实现在其它语言中很难实现的功能;而如果使用不当的话,则会导致难以调试的错误,也可能造成内存泄露。只有在充分理解闭包的基础上,才能正确的使用它。理解闭包需要首先理解 JavaScript 中的prototype 链、执行上下文和作用域链等概念。

闭包指的是一个表达式(通常是一个 function),该表达式可以有自由的变量,并且运行环境能够正确的获取这些变量的值。 JavaScript 中闭包的产生是由于 JavaScript 中允许内部 function,也就是在一个 function 内部声明的 function 。内部 function 可以访问外部 function 中的局部变量、传入的参数和其它内部 function 。当内部 function 可以在包含它的外部 function 之外被引用时,就形成了一个闭包。这个时候,即便外部 function 已经执行完成,该内部 function 仍然可以被执行,并且其中所用到的外部 function 的局部变量、传入的参数等仍然保留外部 function 执行结束时的值。

下面通过一个例子来说明闭包的形成,见代码清单 10。


清单 10. JavaScript 闭包示例代码

function addBy(first) {
  function add(second) {
    return first + second;
  }
  return add;
 }

 var func = addBy(10);
 func(20);  // 结果为 30
 var newFunc = addBy(30);
 newFunc(20); // 结果为 50
在代码清单 10中,外部 functionaddBy的内部 functionadd的引用被返回给addBy的调用者,同时add在方法体中使用了addBy的参数first。这样就形成了一个闭包。通过调用addBy(10)得到的 functionfunc,在其之后的执行过程中,都会保留创建的时候使用的first参数的值10。

下面分析代码清单 10中执行的细节。首先addBy(10)被调用。由于addBy是在全局代码中声明的,因此被调用时候的执行上下文对应的作用域链只包含全局对象。在addBy的方法体中,声明了一个内部 functionadd。add的[[scope]]属性会在作用域链之前加上 functionaddBy的激活对象。该对象中包含了经过初始化的参数first,其值为10。至此,functionfunc的[[scope]]属性的值是包含两个对象。当func被调用的时候,会进入一个新的执行上下文,而此时的作用域链的前面加上了 functionadd调用时的激活对象。该对象中包含了经过初始化的参数second,其值为20。在func的执行过程中,需要对两个标识符first和second求值的时候,会使用之前提到的包含三个对象的作用域链。从而可以正确的求值。

在 JavaScript 中,正确的使用闭包可以简化代码。下面举几个例子来说明。


避免名称空间冲突

在多人协作开发应用,或是使用第三方开发的 JavaScript 库的时候,一个通常会遇到的问题是名称空间冲突。比如第三方的 JavaScript 库在全局对象中声明了一个属性叫test,如果在自己的代码中也会声明同样名称的属性的话,当两者一同使用的时候,后加载的属性值会替换之前的值,从而造成错误。

这个时候典型的做法是只在全局对象中保存一个对象,所有的功能都通过引用此对象来完成。完成功能所需要的内部状态都封装在一个闭包中。如代码清单 11所示。


清单 11. 使用闭包避免名称空间冲突

(function() {
  if (typeof MyCode === "undefined") {
    var defaultName = "Alex";
    MyCode = {
      "sayHello" : function(name) {
        alert("Hello, " + (name || defaultName));
      }
    };
  }
 })();

 MyCode.sayHello();  // 输出为 Hello, Alex
 MyCode.sayHello("Bob"); // 输出为 Hello, Bob
代码中通过创建一个匿名 function 并立即执行来生成一个闭包。在闭包中,通过修改全局对象MyCode来添加所需的功能。内部状态之一的属性defaultName被封装在闭包中,不能被闭包之外的代码所引用,也不会引发命名冲突。


保存状态

在 JavaScript 代码运行过程中,不可避免的需要保存一些内部状态。通过使用闭包,可以将内部状态封装在一个 function 内部,使得代码更加简洁。如代码清单 12所示。

var getNextId = (function() {
  var id = 1;
  return function() {
    return id++;
  }
 })();

 getNextId();  // 输出 1
 getNextId();  // 输出 2
 getNextId();  // 输出 3


代码中的getNextId的功能是生成惟一的 ID,因此它需要维护当前的 ID 这样一个状态。通过使用闭包,不需要在全局对象中添加一个新的属性,该属性由闭包来维护。闭包之外的代码也不能访问或修改getNextId的内部状态。

折叠调用参数

在 JavaScript 中,有些 function,如setTimeout和setInterval,只接受一个 function 作为参数。在有些情况下,这些 function 的执行是需要额外的参数的。这个时候可以通过使用闭包,将原始 function 的参数进行折叠,得到一个没有参数的新 function 。如代码清单 13所示。


function doSomething(a, b, c) {
  alert(a + b + c);

上一页  [1] [2] [3] [4] [5]  下一页

,JavaScript技巧与高级特性
给资讯打分:
网友评论: