JavaScript函数表达式

函数定义

在JS中定义函数有函数声明和函数表达式的两种方式,函数声明会被提到代码执行之前,所以函数声明可以放在函数的调用之后;函数表达式看起来像变量赋值语句,通常是创建一个匿名的函数对象并把它赋值给某个变量,如果在赋值之前调用函数表达式会导致错误。

println("Hello World"); // OK
function println(str) {
    alert(str);
}

println("Hello Wolrd") // error
var println = function(str) {
    alert(str)
}

闭包

闭包是指有权访问另外一个函数作用域中的变量的函数,创建闭包的常见方式就是在一个函数内部创建另外一个函数。通常来说当函数执行完毕局部上下文对象就会被销毁,内存中仅包含全局作用域的变量对象,但是闭包会将外部函数活动对象添加到它的作用域里,外部函数即使执行完毕也不会销毁它的活动对象;直到返回的匿名闭包函数被销毁,外部函数的活动对象才会被销毁。

function createPrintln() {
   var x = 10;
   return function() {
       x++;
       alert(x);
   }
}

var println = createPrintln()

println() // 11
println() // 12

上面的例子中println指向的匿名函数引用了createPrintln函数的活动对象,因而x变量在createPrintln执行完成之后依然保存在内存中。通过前面的分析我们知道局部变量x是保存在内存中,这样闭包取得的包含函数中的变量的值总是最后一个。

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

var array = createPrintln()
for (var i = 0; i < array.length; i++) {
     alert(array[i]()); // 全部都是10
}

这时因为在createPrintln函数的最后x的最新值就是10,每个闭包函数查询x值的时候都会引用到这个最新值,如果想要返回不同的值需要闭包函数引用不同的外部函数才可以。

function createPrintln() {
   var result = new Array()
   for (var x = 0; x < 10; x++) {
       result[x] = function(num) {
           // 闭包函数引用的外部函数是这个function(num)匿名函数,
           // 不再是createPrintln
           return function() { 
               return num
           }
       }(x);
   }
   return result;
}

var array = createPrintln()
for (var i = 0; i < array.length; i++) {
     alert(array[i]());
}

闭包最后需要注意的一条是this对象的引用,在对象函数里this代表的就是当前对象,如果对象方法返回的是一个闭包,这个闭包的this就是全局对象,因为查询变量通常在查到第一个VO变量对象里的变量就不再向其他VO变量对象查询,this在自己的变量对象总一定能查得到,就会出现问题。

var name = "World"

var object = {
    name: "Hello",
    getName: function() {
        return function() {
             return this.name
        }
    }
}

alert(object.getName()()) // World

为了能够正确地访问对象里的this,可以在访问之前用that属性将对象this保存下来,之后再访问对象this就通过that变量访问。

var name = "World"

var object = {
    name: "Hello",
    getName: function() {
        var that = this;
        return function() {
             return that.name
        }
    }
}

alert(object.getName()()) // Hello

模仿块级作用域

JS中没有块级作用域的概念,也就是说语句块中定义的变量其实是在包含函数中创建,如果在函数内部多次声明同一个变量,JS会对后续的声明视而不见,不过会执行初始化方法,匿名函数可以用来模拟块级作用域。

var sum = 0;
// i的作用域在全局作用域,在if块外也可以访问
for (var i = 0; i < 5; i++) {
   sum += i;
}
alert(i); // 5

var i = 3; // 声明会被忽略但是初始化还是被执行
alert(i);  // 3

// 块级作用域语法
(function() {
   var num = 100
   alert(num)
})(); // num的作用域仅在匿名函数块内

alert(num); // error

私有变量

JS中没有私有成员的概念,所遇的对象属性都是公开的,不过变量却可以是私有的。任何函数中定义的变量都可以认为是私有变量,因为无法在函数外部访问这些变量。如果函数返回闭包对象,那么闭包对象就可以访问函数内部定义的变量,闭包对象就作为访问私有变量的公共方法,也称为特权方法。

可以使用构造函数内部方法访问构造函数的变量,除了内部定义的函数可以访问变量,外部无法访问这个变量。不过用构造函数定义的方法在每个对象内部都会创建一组新的方法,使用静态私有变量就可以解决这个问题。

function Person(name) {
    this.getName = function() {
        return name;
    }

    this.setName = function(value) {
        name = value;
    }
}

var tom = new Person('Tom');
alert(tom.getName()) // Tom
var jerry = new Person('Jerry');
alert(jerry.getName()) // Jerry

为了保存私有数据可以使用前面的模拟块级作用域定义变量,同时外部访问函数定义在构造函数的prototype对象上,这样每个新生成的对象都共享同一份变量和方法。

(function() {
   var name;

   // 注意前面没有var,这个Person构造函数是全局的
   Person = function(value) {
      name = value;
   }

   Person.prototype.getName = function() {
       return name;
   }

   Person.prototype.setName = function(value) {
       name = value
   }
})();

var tom = new Person('Tom')
alert(tom.getName())  // Tom 
var jerry = new Person('Jerry')
alert(tom.getName()) // Jerry
alert(jerry.getName()) // Jerry

前面实现都可以创建多个对象,有些对象在系统中可能只有一个,也就是常说的单例对象,对于这种对象的创建可以将它的数值私有化,将操作数值的方法暴露给用户。

var singleton = (function() {
    var name;
    var object = new Object();
    object.getName = function() {
       return name;
    }

    object.setName = function(value) {
       name = value;
    }

    return object;
})();

singleton.setName('Tom');
alert(singleton.getName());
singleton.setName('Jerry');
alert(singleton.getName());
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值