原文: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创建类,方法,属性了。 是不是有了面向对象的感觉。
怎么样,老外还可以吧。。。
说说我的理由:老外写的文章,例子多,浅显易懂,循序渐进,由浅及深,逻辑缜密。
通篇没有什么定义(不像国人写的东西,一开始就解释名词)。
好吧,最后欢迎您的拍砖!