汪和康

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

 

原文:https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Closures

老外写的很详细,有兴趣的可以看原文。(不得不佩服老外的逻辑思维能力)

下面是老外说的一个稍微有趣的例子: 

//在这个例子中,定义了一个函数makeAdder(x),它接收一个参数x和返回一个新函数。
//返回新函数接受单个参数y,并返回x和y的总和。

//在本质上,是一个函数makeAdder工厂

//add5和add10都闭包。他们共享相同的函数体定义,但存储在不同的环境。在add5环境,x是5。至于add10而言,x是10。
function makeAdder(x) {
    return function (y) {
        return x + y;
    };
}
var add5 = makeAdder(5);
var add10 = makeAdder(10);
add5.toString(); //function (y) {  return x + y; };  makeAdder内部方法
alert(add5(2)); // 7
alert(add10(2)); // 12

 接着老外又给了一个稍微实用的的例子: 

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

var size12 = makeSizer(12);
size12.toString(); //function () { document.body.style.fontSize = size + 'px'; }
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;


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

 

老外开始慢慢引入重点了,还是看代码:

//闭包的模块模式 
//创建一个单一的环境,共享三功能:增量计数器。减量计数器。计数器值。
//这三个公共函数闭包,共享相同的环境。
//由于JavaScript的词法作用域,他们各自都能访问内部私有changeBy函数 privateCounter变量。
var Counter = (function () {
    var privateCounter = 0;    //私有变量
    function changeBy(val) {   //私有函数
        privateCounter += val;
    }
    return {
        increment: function () {   //返回方法
            changeBy(1);           //调用内部私有函数
        },
        decrement: function () {    
            changeBy(-1);
        },
        value: function () {       
            return privateCounter;
        }
    }
})();

 
alert(Counter.value()); /* Alerts 0 */
Counter.increment();
Counter.increment();
alert(Counter.value()); /* Alerts 2 */
Counter.decrement();
alert(Counter.value()); /* Alerts 1 */

 

怎么样,老外讲的例子还容易懂吧。

上面单一环境中,三个公共函数还是用的同一个变量。接着看:

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();
alert(Counter1.value()); /* Alerts 0 */
Counter1.increment();
Counter1.increment();
alert(Counter1.value()); /* Alerts 2 */
Counter1.decrement();
alert(Counter1.value()); /* Alerts 1 */
alert(Counter2.value()); /* Alerts 0 */

相信你已经看到   Counter1 Counter2 闭包变量包含一个不同的实例privateCounter。(即:两个实例的变量独立变化,互不影响)

老外又举了一个例子:创建闭包循环,一个常见的错误,看代码:
<p id="help">Helpful notes will appear here</p>
<p>E-mail: <input type="text" id="email" name="email"></p>
<p>Name: <input type="text" id="name" name="name"></p>
<p>Age: <input type="text" id="age" name="age"></p>
function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}
 
function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];
 
  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = function() {
      showHelp(item.help);
    }
  }
}
 
setupHelp(); 
 

上面是错误的写法(所有的事件都提示年龄),正确的如下:

function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}
 
function makeHelpCallback(help) {
  return function() {
    showHelp(help);
  };
}
 
function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];
 
  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = makeHelpCallback(item.help);
  }
}
 
setupHelp(); 
这是预期的。而不是回调都共享一个单一的环境,makeHelpCallback函数为每个调用它的对象创建一个新的环境, 。

写到这老外还没结束。

 

老外引出了:构造函数和原型 。
 
老外说:当闭包不是必要的时候,创建一个新对象/类、方法通常应该是关联到对象的原型,而不是定义成对象的构造函数。
          原因是,每当在调用构造函数的方法将得到重新分配(即为每一个对象创建)。
老外说的很明白,接下来看js 原型的写法(prototype)。
当然老外还是有例子的(看代码):
//构造函数写法
function MyObject(name, message) {
  this.name = name.toString();      //变量
  this.message = message.toString();
  this.getName = function() {       //方法
    return this.name;
  };
 
  this.getMessage = function() {
    return this.message;
  };
}

原型写法:

//原型的写法1
function MyObject(name, message) {
  this.name = name.toString();        //变量
  this.message = message.toString();  
}
MyObject.prototype = {
  getName: function() {      //方法
    return this.name;
  },
  getMessage: function() {
    return this.message;
  }
};
 
//原型的写法2
function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype.getName = function() {
  return this.name;
};
MyObject.prototype.getMessage = function() {
  return this.message;
};

调用方法:

var a = new MyObject('zhangsan', '我是张三');
a.getMessage();
a.getName();

 

怎么样看到这里你是不是明白了:js构造函数,js 的原型(prototype),以及js闭包了。

         你是不是应该明白了js创建类,方法,属性了。 是不是有了面向对象的感觉。 

 

怎么样,老外还可以吧。。。

说说我的理由:老外写的文章,例子多,浅显易懂,循序渐进,由浅及深,逻辑缜密。

                   通篇没有什么定义(不像国人写的东西,一开始就解释名词)。

 

好吧,最后欢迎您的拍砖!

 
 

 

 

 

          

              

posted on 2013-04-18 17:03  Supe  阅读(234)  评论(0编辑  收藏  举报