JavaScript笔记:函数表达式

来源:互联网 发布:windows 包管理工具 编辑:程序博客网 时间:2024/05/22 11:51

函数表达式是JavaScript中一个很强大又容易令人困惑的特性。
创建函数有两种方式:

// 方式1function sayHi(){    alert("Hi!");}// 方式2var sayHi = function(){    alert("Hi!");};

函数有个很重要的特性就是函数声明提升:意思就是说,在执行代码之前会先读取函数的声明。

闭包

闭包指的是有权访问另一个函数作用域的变量的函数。创建闭包的另一个方式,就是在一个函数中创建另一个函数。

function createComparisonFunction(propertyName) {    return function(object1, object2){        var value1 = object1[propertyName];        var value2 = object2[propertyName];        if (value1 < value2){            return -1;        } else if (value1 > value2){            return 1;        } else {            return 0;        }     };}// 请注意下面这两行代码://var value1 = object1[propertyName];//var value2 = object2[propertyName];

这段代码中,需要注意的两行代码如上所示,这两行代码访问了外部的函数属性propertyName。即使这个内部的函数被返回了,并且在其他地方被调用了,它仍然可以访问变量propertyName。

1、闭包与变量

注意,闭包只能取得包含函数变量中任何变量的最后一个值。也就是说,闭包保存的是整个变量对象,而不是某个特殊的变量。
下面这段代码很能说明问题:

function createFunctions(){    var result = new Array();    for (var i=0; i < 10; i++){        result[i] = function(){return i; };}    return result;}

createFunctions函数返回后,result中的所有返回值都是10;如果我们想要返回值是期望的0-10,应该如下这样写:

function createFunctions(){    var result = new Array();    for (var i=0; i < 10; i++){        result[i] = function(num){            return function(){                return num;            };        }(i);    return result;}

上面这段代码通过创建匿名函数达到了目的。

2、关于this对象

我们知道,this对象是在运行的时候根据函数的执行环境绑定的。在全局函数中,this等于window。当函数被某个对象调用时,this则等于那个对象。但是,匿名函数的执行环境具有全局性,因此其this对象通常指向window。
例如:

var name = "The Window";var object = {    name : "My Object",    getNameFunc : function(){        return function(){            return this.name;        };} };alert(object.getNameFunc()()); //"The Window"

如果在getNameFunc想返回object的name,该如何做呢?只需要在闭包中,将this变量保存下来即可:

var name = "The Window";var object = {    name : "My Object",    getNameFunc : function(){        var that = this;        return function(){            return that.name;        };    }};alert(object.getNameFunc()());  //"My Object"

3、内存泄漏

由于IE9之前的版本对JS对象和DOM对象使用不同的垃圾收集例程,因此闭包在这些IE浏览器下就会有一些特殊的问题。具体来说就是,如果闭包的作用域链中保存了HTML元素,那么就意味着,该元素不会被销毁。
看下面的例子:

function assignHandler(){    var element = document.getElementById("someElement");    element.onclick = function(){        alert(element.id);    };}

上述代码创建了一个作为element元素事件处理的闭包。由于匿名函数保存了一个对assignHandler活动对象的引用,因此就会导致无法减少element元素的引用数,那么它所占用的内存就永远不会被回收。因此,这段代码应该修改为:

function assignHandler(){    var element = document.getElementById("someElement"); var id = element.id;    element.onclick = function(){        alert(id);    };    element = null;}

模仿块级作用域

JavaScript没有块级作用域的概念,这意味着,在块语句中定义的变量,实际上是包含在函数中,而非语句中的。例如:

function outputNumbers(count){    for (var i=0; i < count; i++){        alert(i);     }    alert(i); }

这个函数中定义了一个for循环,在java语言中,一旦for循环结束,变量i也会被销毁。但是在js语言中,i变量会在函数中一直存在,不会被销毁。

匿名函数可以模仿块级作用域来避免这个问题。

(function(){     // 这里是块级作用域})();

以上代码创建了一个匿名函数并立即调用了这个函数。

利用匿名函数,上面的一段代码可以修改为:

function outputNumbers(count){    (function () {        for (var i=0; i < count; i++){            alert(i);        }    })();    alert(i); // 错误}

这种技术经常用来限制向全局作用域中添加过多的变量和函数。一般来说,我们应该少向全局作用域中添加变量和函数,因为在多人参加的大型项目中,这样很容易导致命名冲突的错误。通过创建私有域,大家不必担心搞乱全局作用域,又可以愉快地使用自己的变量。

私有变量

任何在函数内部定义的变量,可以成为私有变量,因为在函数外部不能访问这些变量。
另外,我们把有权访问函数内部私有变量和私有方法的方法称为特权方法。

function myObject(){    // 私有变量和函数    var privateVariable = 10;    function privateFunction(){        return false;    }    // 特权方法    this.publicMethod = function (){        privateVariable++;        return privateFunction();    };}

利用私有和特权成员,可以隐藏那些不可以被直接修改的数据。

1、静态私有变量。

通过在私有作用域中定义私有变量和函数,同样可以创建特权方法:

(function(){    // 私有变量和函数    var privateVariable = 10;    function privateFunction(){        return false;    }    // 构造函数    MyObject = function(){ };    // 公有/特权方法    MyObject.prototype.publicMethod = function(){        privateVariable++;        return privateFunction();    };})();

需要注意的是,在定义构造函数的时候,我们没有使用函数声明,而是使用了函数表达式。函数声明只能创建局部函数,但那并不是我们想要的。

上面这种方式定义和在构造函数中定义特权方法的区别在于,私有变量和函数是所有对象共享的:

(function(){        var name = "";        Person = function(value){            name = value;        };        Person.prototype.getName = function(){            return name;        };        Person.prototype.setName = function (value){            name = value;        };})();var person1 = new Person("Nicholas"); alert(person1.getName()); //"Nicholas" person1.setName("Greg"); alert(person1.getName()); //"Greg"var person2 = new Person("Michael");alert(person1.getName()); //"Michael"alert(person2.getName()); //"Michael"

我们可以发现了:当对象2修改了name变量,对象1也跟着受到影响。

2、模块模式

为单例创建私有变量和方法:(所谓单例,就是只有一个实例的对象)

var singleton = function(){    // 私有变量和私有函数    var privateVariable = 10;    function privateFunction(){            return false;    }    //特权/公有    return {        publicProperty: true,        publicMethod : function(){            privateVariable++;            return privateFunction();        }     }; }();

从本质上讲,这个对象字面量定义的是单例的公共接口。

3、增强的模块模式

单例必须是某种类型,并且还需要添加某些方法和属性的情况。

var singleton = function(){    //私有变量和私有函数    var privateVariable = 10;    function privateFunction(){            return false;    }    //创建对象    var object = new CustomType();    //添加特权/公有方法    object.publicProperty = true;    object.publicMethod = function(){            privateVariable++;            return privateFunction();    };    //          return object;}();
0 0