8-JS闭包、回调实例

1.回调

javascipt中,函数回调一般用于以下几种场景:

1.异步执行(例如读取文件,进行HTTP请求)
2.同步(阻塞)
3.事件监听和处理
4.设置超时和时间间隔的方法

异步例子(使用AJAX加载XML文件的示例,并且使用了call()函数,在请求对象(requested object)上下文中调用回调函数。):

function fn(url, callback){
  var httpRequest;    //创建XHR
  httpRequest = window.XMLHttpRequest ? new XMLHttpRequest() :   //针对IE进行功能性检测
    window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : undefined;
  
  httpRequest.onreadystatechange = function(){
   if(httpRequest.readystate === 4 && httpRequest.status === 200){  //状态判断4和200表示执行该语句
     callback.call(httpRequest.responseXML); 
   }
  };
  httpRequest.open("GET", url);
  httpRequest.send();
}
 
fn("text.xml", function(){    //调用函数
 console.log(this);   //此语句后输出
});
 
console.log("this will run before the above callback.");  //此语句先输出

异步处理,意味着我们开始请求时,就告诉它们完成之时调用我们的函数。在实际情况中,onreadystatechange事件处理程序还得考虑请求失败的情况,这里我们是假设xml文件存在并且能被浏览器成功加载。这个例子中,异步函数分配给了onreadystatechange事件,因此不会立刻执行。

同步例子(func1代码执行完成后才执行func2):

var func1=function(callback){
  //do something.
  (callback && typeof(callback) === "function") && callback();
}
 
func1(func2);
  var func2=function(){
}

提醒:回调函数是个闭包

2.闭包

官方释义:闭包是指那些能够访问独立(自由)变量的函数 (变量在本地使用,但定义在一个封闭的作用域中)。换句话说,这些函数可以“记忆”它被创建时候的环境。

闭包允许将函数与其所操作的某些数据(环境)关连起来。这显然类似于面向对象编程。在面对象编程中,对象允许我们将某些数据(对象的属性)与一个或者多个方法相关联。

官方实例:假设我们想在页面上添加一些可以调整字号的按钮。一种方法是以像素为单位指定 body 元素的 font-size,然后通过相对的 em 单位设置页面中其它元素(例如页眉)的字号:

body {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 12px;
}

h1 {
  font-size: 1.5em;
}

h2 {
  font-size: 1.2em;
}

我们的交互式的文本尺寸按钮可以修改 body 元素的 font-size 属性,而由于我们使用相对的单位,页面中的其它元素也会相应地调整。

以下是 JavaScript:

function makeSizer(size) {
     return function() {
        document.body.style.fontSize = size + 'px';
    };
}
    
var size12 = makeSizer(12);
    
var size14 = makeSizer(14);
    
var size16 = makeSizer(16);

size12,size14 和 size16 为将 body 文本相应地调整为 12,14,16 像素的函数。我们可以将它们分别添加到按钮上(这里是链接)。如下所示:

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;

<a href="#" id="size-12">12</a>
<a href="#" id="size-14">14</a>
<a href="#" id="size-16">16</a>

function makeSizer(size) {
  return function() {
    document.body.style.fontSize = size + 'px';
  };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;

附:闭包的模块模式(使用闭包模拟私有方法)

var makeCounter = function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }  
};

var Counter1 = makeCounter();
var Counter2 = makeCounter();
console.log(Counter1.value()); /* logs 0 */
Counter1.increment();
Counter1.increment();
console.log(Counter1.value()); /* logs 2 */
Counter1.decrement();
console.log(Counter1.value()); /* logs 1 */
console.log(Counter2.value()); /* logs 0 */

此种私有方法不仅仅有利于限制对代码的访问:还提供了管理全局命名空间的强大能力,避免非核心的方法弄乱了代码的公共接口部分。

注意:不要在循环中创建闭包

posted @ 2017-01-04 22:50  shelfy  阅读(155)  评论(0编辑  收藏  举报