JavaScript笔记:函数表达式

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

// 方式1
function sayHi(){
    alert("Hi!");
}
// 方式2
var 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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值