闭包与模仿块作用域

闭包与模仿块作用域

  最近看《JavaScript语言精粹》真是一页的含量顶其它书的十页啊,受益匪浅,在这里整理一下闭包的一些东东吧。

  首先我也忘了之前有没有整理过关于原型的一些用法,先给出一段代码:

1
2
3
4
Function.prototype.method=function(name,func){
this.prototype[name]=func;
return this;
}

  具体要是忘了的话,去看原型那节,作用是给Function对象的原型添加了一个method方法,用于往该原型里新增方法,这样继承自Function对象的子对象们都会动态更新它们的方法。

  然后就是我们主要整理的东西了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
String.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("&lt;body&gt;&quot;HAHAHA&quot;&lt;body&gt;".deentityify())//<body>"HAHAHA"<body>

  这段代码的主要功能是:给String对象添加一个deentityify方法。它的任务是寻找字符串中的HTML字符实体并把它们替换为对应的字符。
  这里有四个需要注意的点:

1.模仿块作用域

1
2
3
(function(){
var s="test";
}())

  JS中函数可以用来构造函数作用域,在函数里面可以看到外面的变量,但在函数外面则无法看到函数里面的变量,这是因为函数中搜索变量的过程是由内到外沿着作用域链逐层搜索,一直搜索到全局对象为止。

  因为JS将function关键字当作一个函数声明的开始,而函数声明后面不能跟圆括号。然而,函数表达式的后面可以跟圆括号。所以最外层的把整个函数包起来的圆括号表示:将函数声明转换成函数表达式。

  这段代码实际上定义并立即调用了一个匿名函数,其中最后的()圆括号是调用运算符

  要注意()调用运算符立刻调用了我们刚刚构造出来的函数,这个调用创建并返回的函数才是deentityify()方法,
  即:

1
2
3
deentityify=function(){
return this.replace(...);
}

  在匿名函数中定义的任何变量,都会在执行结束时被销毁,这样就可以减少闭包占用的内存问题。

2.闭包

  一个函数可以访问它被创建时所处的上下文环境,这称为闭包。例子中第一个return的函数就是一个闭包:

1
2
3
4
5
6
return function(){
return this.replace(/&([^&;]+);$/g,function(a,b){
var r=entity[b];
return typeof r === "string" ? r : a;
});
};

  上面的函数可以访问到deentityify方法上下文环境定义的entity对象,所以这个函数就可以被称作闭包

  在这里再讨论一下变量的生存周期:

  全局变量的生存周期是永久,除非我们主动销毁(=null)这个全局变量。

  在函数内用var关键字声明的局部变量,当退出函数时,会随着函数调用的结束而被销毁。

  但是如果局部变量被封闭在闭包形成的环境中,那么这个局部变量就能一直生存下去。像上面的闭包,它可以访问到deentityify()被调用时产生的环境,所以局部变量entity因为能被外界访问,所以就不会被销毁,除非我们手动销毁。

  所以说闭包的作用有:

  1. 可以延长局部变量的生存周期。

  2. 可以封装变量

3.模块模式

  模块是一个提供接口却隐藏状态与实现的函数或对象。它利用函数作用域与闭包来创建被绑定对象与私有成员的关联,模块模式的一般形式是:一个定义了私有变量和函数的函数;利用闭包创建可以访问私有变量和函数的特权函数;最后返回这个特权函数,或者把它们保存到一个可访问到的地方。

4.String.prototype.replace()进阶用法

  我这也是第一次知道它的第二个参数可以是一个函数,且非常有用,具体见:MDN文档

指定一个函数作为参数

  你可以指定一个函数作为第二个参数。在这种情况下,当匹配执行后, 该函数就会执行。 函数的返回值作为替换字符串。 (注意:上面提到的特殊替换参数在这里不能被使用。) 另外要注意的是, 如果第一个参数是正则表达式, 并且其为全局匹配模式, 那么这个方法将被多次调用, 每次匹配都会被调用。

  下面是该函数的参数:

  变量名   代表的值
  match   匹配的子串。(对应于上述的$&。)
  p1,p2, … 假如replace()方法的第一个参数是一个RegExp 对象,则代表第n个括号匹配的字符串。(对应于上述的$1,$2等。)
  offset  匹配到的子字符串在原字符串中的偏移量。(比如,如果原字符串是“abcd”,匹配到的子字符串是“bc”,那么这个参数将是1)
  string  被匹配的原字符串。

  代码中的正则表达式/&([^&;]+);$/g代表:从整个字符串中,从全局匹配出所有的以&开头,中间有1个或多个非&;的任意字符,然后以;结尾的字符串。

文章目录
  1. 1. 闭包与模仿块作用域
    1. 1.0.1. 1.模仿块作用域
    2. 1.0.2. 2.闭包
    3. 1.0.3. 3.模块模式
    4. 1.0.4. 4.String.prototype.replace()进阶用法
      1. 1.0.4.1. 指定一个函数作为参数
,