[Effective JavaScript 笔记] 第11条:熟练掌握闭包

理解闭包三个基本的事实

第一个事实:js允许你引用在当前函数以外定义的变量。

function makeSandwich(){

var magicIngredient=”peanut butter”;

function make(filling){

return magicIngredient+’and ’+filling;

}

return make(‘jelly’);

}

makeSandwich();//”peanut butter and jelly”

图上直接指出如下

第二个事实:即使外部函数已经返回,当前函数仍然可以引用在外部函数所定义的变量。

function sandwichMaker(){

var magicIngredient=”peanut butter”;

function make(filling){

return magicIngredient+’and ’+filling;

}

return make;

}

var f=sandwichMaker();

f(“jelly”);//”peanut butter and jelly”

f(“bananas”);//”peanut butter and bananas”

f(“marshmallows”);//”peanut butter and marshmallows”

还是上图标识

原理:js的函数值包含了比调用它们时执行所需要的代码还要多的信息。而且js函数值还在内部存储它们可能会引用的定义在其封闭作用域的变量。那些在其所涵盖的作用域内跟踪变量的函数被称为闭包。make函数就是一个闭包。其代码引用了两个外部变量:magicIngredient和filling。每当make被调用时,其代码都能引用到这两个变量,因为该闭包存储了这两个变量。

函数可以引用在其作用域内的任何变量,包括参数和外部函数变量。

function sandwichMaker(magicIngredient){

function make(filling){

return magicIngredient+”and ”+filling;

}

return make;

}

var hamAnd=sandwichMaker(“ham”);

hamAnd(“cheese”);//”ham and cheese”

hamAnd(“mustard”);//”ham and mustard”

var turkeyAnd=sandwichMaker(“trukey”);

turkeyAnd(“Swiss”);//”trukey and Swiss”

turkeyAnd(“Provolone”);//”trukey and Provolone”

闭包是js最优雅、最有表现力的特性之一,也是许多惯用法的核心。js还提供了一种更为方便构建闭包的字面量语法--函数表达式。

function sandwichMaker(magicIngredient){

return function(filling){

return magicIngredient+”and ”+filling;

}

}

请注意,该函数表达式是匿名的。由于只需要产生一个新值,而不需要在局部使用,所以没必要给该函数命名。

第三个事实:闭包可以更新外部变量的值。

实际上,闭包存储的是外部变量的引用,而不是它们的值的副本。因此任何具在访问这些外部变量的闭包,都可以进行更新。

function box(){

var val=undefined;

return {

set:function(newVal){val=newVal},

get:function(){return val},

type:function(){return typeof val}

}

}

var b=box();

b.type();//”undefined”

b.set(98.6);

b.get();//98.6

b.type();//”number”

这个例子里产生了一个包含三个闭包的对象。这三个闭包是set,get和type属性。它们共享访问val变量。

提示

  • 函数可以引用定义在其外部作用域的变量
  • 闭包比创建它们的函数有更长的生命周期
  • 装饰在内部存储其外部变量的引用,并能读写这些变量

后记

这个部分,这觉得这里讲得已经很清楚,如果想再深入去了解,可以去看高3上面关于闭包的讲解。

里面对作用域链,执行环境,变量对象,都有详细说明。

时间: 2024-10-08 20:59:52

[Effective JavaScript 笔记] 第11条:熟练掌握闭包的相关文章

[Effective JavaScript 笔记]第27条:使用闭包而不是字符串来封装代码

函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式传递给eval函数以达到同样的功能.程序员面临一个选择:应该将代码表示为函数还是字符串?毫无疑问,应该将代码表示为函数.字符串表示代码不够灵活的一个重要原因是:它们不是闭包. 闭包回顾 看下面这个图 js的函数值包含了比调用它们时执行所需要的代码还要多的信息.而且js函数值还在内部存储它们可能会引用

[Effective JavaScript 笔记]第28条:不要信赖函数对象的toString方法

js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+1}" 反射获取函数源代码的功能很强大,使用函数对象的toString方法有严重的局限性.toString方法的局限性ECMAScript标准对函数对象的toString方法的返回结果(即该字符串)并没有任何要求.这意味着不同的js引擎将产生不同的字符串,甚至产生的字符串与该函数并不相关. 如果函数

[Effective JavaScript 笔记] 第4条:原始类型优于封闭对象

js有5种原始值类型:布尔值.数字.字符串.null和undefined. 用typeof检测一下: typeof true; //"boolean" typeof 2; //"number" typeof "s";//"string" typeof null;//"object":ECMAScript把null描述为独特的类型,但返回值却是对象类型,有点困惑. 可以使用Object.prototype.t

[Effective JavaScript 笔记]第50条:迭代方法优于循环

"懒"程序员才是好程序员.复制和粘贴样板代码,一但代码有错误,或代码功能修改,那么程序在修改的时候,程序员需要找到所有相同功能的代码一处处进行修改.这会使人重复发明轮子,而且在别人看代码的时候无法在更高层次都看待问题的解决方案.太容易陷入细节. for循环 js中的for循环在进行一些细微变化时,可以引入不同的行为.编程的时候对于边界条件的判断往往会导致一些简单的错误.下面的一些for循环的细微变化导致边界条件的变化. for(var i=0;i<=n;i++){...} //包

[Effective JavaScript 笔记]第68条:使用promise模式清洁异步逻辑

构建异步API的一种流行的替代方式是使用promise(有时也被称为deferred或future)模式.已经在本章讨论过的异步API使用回调函数作为参数. downloadAsync('file.txt',function(file){ console.log('file:'+file); }); 基于promise的API不接收回调函数作为参数.相反,它返回一个promise对象,该对象通过其自身的then方法接收回调函数. var p=downloadP('file.txt'); p.th

[Effective JavaScript 笔记]第46条:使用数组而不要使用字典来存储有序集合

对象属性无序性 js对象是一个无序属性集合. var obj={}; obj.a=10; obj.b=30; 属性a和属性b并没有谁前谁后之说.for...in循环,先输出哪个属性都有可能.获取和设置不同的属性与顺序无关,都会以大致相同的效率产生相同的结果.也就是说访问属性a和访问属性b,没有哪个访问更快之说.ES标准并未规定属性存储的任何特定顺序,甚至于枚举对象也未涉及.for...in循环会挑选一定的顺序来枚举对象的属性,标准允许js引擎自由选择一个顺序,它们的选择会微妙地改变程序行为.如要

[Effective JavaScript 笔记]第15条:当心局部块函数声明笨拙的作用域

嵌套函数声明.没有标准的方法在局部块里声明函数,但可以在另一个函数的顶部嵌套函数声明. function f(){return "global"} function test(x){ var result=[]; function f(){return "local";}//block-local if(x){ result.push(f()); } result.push(f()); return result; } test(true);//["loc

[Effective JavaScript 笔记]第45条:使用hasOwnProperty方法以避免原型污染

之前的43条,44条讨论了属性的枚举,但都没有彻底地解决属性查找中原型污染的问题.看下面关于字典的一些操作 'zhangsan' in dict; dict.zhangsan; dict.zhangsan=22; js的对象操作总是经继承的方式工作的.即使是一个空的对象字面量也是继承了Object.protoype属性. var dict={}; 'zhangsan' in dict;//false 'lisi' in dict;//false 'wangwu' in dict;//false'

[Effective JavaScript 笔记] 第6条:了解分号插入的局限

分号可以省略 js可以在语句结束不强制加分号.(建议还是添加,不添加分号往往会出现不易发现的BUG) function Point(x,y){ this.x=x||0; this.y=y||0; } Point.prototype.isOrigin=function(){ return this.x===0 && this.y===0 } 上面代码可以运行,是由于js可以自动插入分号,它是一种程序解析技术.能推断出某些上下文中省略的分号,然后有效地自动地将分号"插入"到