闭包与模仿块作用域
最近看《JavaScript语言精粹》真是一页的含量顶其它书的十页啊,受益匪浅,在这里整理一下闭包的一些东东吧。
首先我也忘了之前有没有整理过关于原型的一些用法,先给出一段代码:
1 | Function.prototype.method=function(name,func){ |
具体要是忘了的话,去看原型那节,作用是给Function对象的原型添加了一个method方法,用于往该原型里新增方法,这样继承自Function对象的子对象们都会动态更新它们的方法。
然后就是我们主要整理的东西了:1
2
3
4
5
6
7
8
9
10
11
12
13
14String.method("deentityify",function(){
var entity={
quot: '"',
lt: '<',
gt: '>'
};
return function(){
return this.replace(/&([^&;]+);$/g,function(a,b){
var r=entity[b];
return typeof r === "string" ? r : a;
});
};
}());
document.writeln("<body>"HAHAHA"<body>".deentityify())//<body>"HAHAHA"<body>
这段代码的主要功能是:给String对象添加一个deentityify方法。它的任务是寻找字符串中的HTML字符实体并把它们替换为对应的字符。
这里有四个需要注意的点:
1.模仿块作用域
1 | (function(){ |
JS中函数可以用来构造函数作用域,在函数里面可以看到外面的变量,但在函数外面则无法看到函数里面的变量,这是因为函数中搜索变量的过程是由内到外沿着作用域链逐层搜索,一直搜索到全局对象为止。
因为JS将function关键字当作一个函数声明的开始,而函数声明后面不能跟圆括号。然而,函数表达式的后面可以跟圆括号。所以最外层的把整个函数包起来的圆括号表示:将函数声明转换成函数表达式。
这段代码实际上定义并立即调用了一个匿名函数,其中最后的()圆括号是调用运算符。
要注意()调用运算符立刻调用了我们刚刚构造出来的函数,这个调用创建并返回的函数才是deentityify()方法,
即:
1 | deentityify=function(){ |
在匿名函数中定义的任何变量,都会在执行结束时被销毁,这样就可以减少闭包占用的内存问题。
2.闭包
一个函数可以访问它被创建时所处的上下文环境,这称为闭包。例子中第一个return的函数就是一个闭包:
1 | return function(){ |
上面的函数可以访问到deentityify方法上下文环境定义的entity对象,所以这个函数就可以被称作闭包。
在这里再讨论一下变量的生存周期:
全局变量的生存周期是永久,除非我们主动销毁(=null)这个全局变量。
在函数内用var关键字声明的局部变量,当退出函数时,会随着函数调用的结束而被销毁。
但是如果局部变量被封闭在闭包形成的环境中,那么这个局部变量就能一直生存下去。像上面的闭包,它可以访问到deentityify()被调用时产生的环境,所以局部变量entity因为能被外界访问,所以就不会被销毁,除非我们手动销毁。
所以说闭包的作用有:
可以延长局部变量的生存周期。
可以封装变量
3.模块模式
模块是一个提供接口却隐藏状态与实现的函数或对象。它利用函数作用域与闭包来创建被绑定对象与私有成员的关联,模块模式的一般形式是:一个定义了私有变量和函数的函数;利用闭包创建可以访问私有变量和函数的特权函数;最后返回这个特权函数,或者把它们保存到一个可访问到的地方。
4.String.prototype.replace()进阶用法
我这也是第一次知道它的第二个参数可以是一个函数,且非常有用,具体见:MDN文档
指定一个函数作为参数
你可以指定一个函数作为第二个参数。在这种情况下,当匹配执行后, 该函数就会执行。 函数的返回值作为替换字符串。 (注意:上面提到的特殊替换参数在这里不能被使用。) 另外要注意的是, 如果第一个参数是正则表达式, 并且其为全局匹配模式, 那么这个方法将被多次调用, 每次匹配都会被调用。
下面是该函数的参数:
变量名 代表的值
match 匹配的子串。(对应于上述的$&。)
p1,p2, … 假如replace()方法的第一个参数是一个RegExp 对象,则代表第n个括号匹配的字符串。(对应于上述的$1,$2等。)
offset 匹配到的子字符串在原字符串中的偏移量。(比如,如果原字符串是“abcd”,匹配到的子字符串是“bc”,那么这个参数将是1)
string 被匹配的原字符串。
代码中的正则表达式/&([^&;]+);$/g代表:从整个字符串中,从全局匹配出所有的以&开头,中间有1个或多个非&;的任意字符,然后以;结尾的字符串。