闭包函数的概念

概念

匿名函数经常被人误认为是闭包(closure)。闭包指的是那些引用了另一个函数作用域中变量的函数,通常是嵌套函数中使用的。

function createComparisonFunction(propertyName) { 
 return function(object1, object2) { 
 let value1 = object1[propertyName]; 
 let value2 = object2[propertyName]; 
 if (value1 < value2) { 
 return -1; 
 } else if (value1 > value2) { 
 return 1; 
 } else { 
 return 0; 
 } 
 }; 
}
在这个内部函数被返回并在其他地方被使用后,它仍然引用着那个变量。这是因为内部函数的作用域链包含createComparisonFunction()函数的作用域。

this对象

在闭包中使用 this 会让代码变复杂。如果内部函数没有使用箭头函数定义,则 this 对象会在运
行时绑定到执行函数的上下文。如果在全局函数中调用,则 this 在非严格模式下等于 window ,在
格模式下等于 undefined 。如果作为某个对象的方法调用,则 this 等于这个对象。匿名函数在这种
况下不会绑定到某个对象,这就意味着 this 会指向 window ,除非在严格模式下 this undefined
不过,由于闭包的写法所致,这个事实有时候没有那么容易看出来。来看下面的例子:
window.identity = 'The Window'; 
let object = { 
 identity: 'My Object', 
 getIdentityFunc() { 
 return function() { 
 return this.identity; 
 }; 
 } 
}; 
console.log(object.getIdentityFunc()()); // 'The Window'
这里先创建了一个全局变量 identity ,之后又创建一个包含 identity 属性的对象。这个对象还
包含一个 getIdentityFunc() 方法,返回一个匿名函数。这个匿名函数返回 this.identity 。因为
getIdentityFunc() 返回函数,所以 object.getIdentityFunc()() 会立即调用这个返回的函数,从而得到一个字符串。可是,此时返回的字符串是"The Winodw" ,即全局变量 identity 的值。为什
么匿名函数没有使用其包含作用域( getIdentityFunc() )的 this 对象呢前面介绍过,每个函数在被调用时都会自动创建两个特殊变量:this arguments 。内部函数永远不可能直接访问外部函数这两个变量。但是,如果把 this 保存到闭包可以访问的另一个变量中,则是行得通的。比如:
window.identity = 'The Window'; 
let object = { 
 identity: 'My Object', 
 getIdentityFunc() { 
 let that = this; 
 return function() { 
 return that.identity; 
 }; 
 } 
}; 
console.log(object.getIdentityFunc()()); // 'My Object'
在定义匿名函数之前,先把外部函数的 this 保存 到变量 that 中。然后在定义闭包时,就可以让它访问 that ,因为这是包含函数中名称没有任何冲突的 一个变量。即使在外部函数返回之后,that 仍然指向 object ,所以调 object.getIdentityFunc()
就会返回 "My Object"

注意:

this arguments 都是不能直接在内部函数中访问的。如果想访问包含作用域中的 arguments 对象,则同样需要将其引用先保存到闭包能访问的另一个变量中。

在一些特殊情况下, this 值可能并不是我们所期待的值。比如下面这个修改后的例子:
window.identity = 'The Window'; 
let object = { 
 identity: 'My Object', 
 getIdentity () { 
 return this.identity; 
 } 
};
getIdentity() 方法就是返回 this.identity 的值。以下是几种调用 object.getIdentity()
的方式及返回值:
object.getIdentity(); // 'My Object' 
(object.getIdentity)(); // 'My Object' 
(object.getIdentity = object.getIdentity)(); // 'The Window'
第一行调用 object.getIdentity() 是正常调用,会返回 "My Object" ,因为 this.identity 就是 object.identity 。第二行在调用时把 object.getIdentity 放在了括号里。虽然加了括号之
后看起来是对一个函数的引用,但 this 值并没有变。这是因为按照规范, object.getIdentity
(object.getIdentity) 是相等的。第三行执行了一次赋值,然后再调用赋值后的结果。因为赋值表
达式的值是函数本身, this 值不再与任何对象绑定,所以返回的是 "The Window"。一般情况下,不大可能像第二行和第三行这样调用对象上的方法。但通过这个例子,我们可以知道
即使语法稍有不同,也可能影响 this 的值。

静态私有变量

特权方法也可以通过使用私有作用域定义私有变量和函数来实现。这个模式如下所示:
(function() { 
 // 私有变量和私有函数
 let privateVariable = 10; 
 function privateFunction() { 
 return false; 
 } 
 // 构造函数
 MyObject = function() {}; 
 // 公有和特权方法
 MyObject.prototype.publicMethod = function() { 
 privateVariable++; 
 return privateFunction(); 
 }; 
})();
在这个模式中,匿名函数表达式创建了一个包含构造函数及其方法的私有作用域。首先定义的是私
有变量和私有函数,然后又定义了构造函数和公有方法。公有方法定义在构造函数的原型上,与典型的原型模式一样。注意,这个模式定义的构造函数没有使用函数声明,使用的是函数表达式。函数声明会创建内部函数,在这里并不是必需的。基于同样的原因(但操作相反),这里声明MyObject 并没有使用任何关键字。因为不使用关键字声明的变量会创建在全局作用域中,所以MyObject 变成了全局变量,可以在这个私有作用域外部被访问。注意在严格模式下给未声明的变量赋值会导致错误。
这个模式与前一个模式的主要区别就是,私有变量和私有函数是由实例共享的。因为特权方法定义
在原型上,所以同样是由实例共享的。特权方法作为一个闭包,始终引用着包含它的作用域。来看下面的例子:
(function() { 
 let name = ''; 
 Person = function(value) { 
 name = value; 
 }; 
 Person.prototype.getName = function() { 
 return name; 
 }; 
 Person.prototype.setName = function(value) { 
 name = value; 
 }; 
})(); 
let person1 = new Person('Nicholas'); 
console.log(person1.getName()); // 'Nicholas' 
person1.setName('Matt'); 
console.log(person1.getName()); // 'Matt' 
let person2 = new Person('Michael'); 
console.log(person1.getName()); // 'Michael' 
console.log(person2.getName()); // 'Michael'
这里的 Person 构造函数可以访问私有变量 name ,跟 getName() setName() 方法一样。使用这
种模式, name 变成了静态变量,可供所有实例使用。这意味着在任何实例上调用 setName() 修改这个 变量都会影响其他实例。调用 setName() 或创建新的 Person 实例都要把 name 变量设置为一个新值而所有实例都会返回相同的值。像这样创建静态私有变量可以利用原型更好地重用代码,只是每个实例没有了自己的私有变量最终,到底是把私有变量放在实例中,还是作为静态私有变量,都需要根据自己的需求来确定。

注意:

使用闭包和私有变量会导致作用域链变长,作用域链越长,则查找变量所需的时间

也越多。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值