闭包
// 创建一个构造函数quo // 它带有get_status方法和status私有属性 var quo = function(status){ return { get_status: function(){ return status; } }; }; var myQuo = quo(‘amazing‘); alert(myQuo.get_status());
当我们调用quo时,它返回一个对象:
①该对象包含一个get_status方法,②该对象的一个引用保存在myQuo中。
即使quo已经返回了(return status;),但get_status方法仍然有访问status属性的特权。
get_status方法访问的并不是status参数的副本,而是status参数本身!
因为该函数可以访问它被创建时所处的上下文环境。也因此该函数被称为闭包。
再看一个更有用的栗子:
// 定义一个函数,它使一个DOM节点的背景色由黄变白 var fade = function(node){ var level = 1; var step = function(){ var hex = level.toString(16); node.style.backgroundColor = ‘#FFFF‘ + hex + hex; if(hex < 15){ level += 1; setTimeout(step, 100); } }; setTimeout(step, 100); }; fade(document.body);
只要fade的内部函数step需要level,level变量就会持续保留。
糟糕的栗子:
// 弹出节点的序号 var add_the_handlers = function(nodes){ var i; for(i=0; i<nodes.length; i++){ nodes[i].onclick = function(){ alert(i); //总是弹出节点的数目 }; } };
未能达到目的的原因,是因为onclick时间处理器函数绑定了变量 i 本身,而不是函数在构造时的变量 i 的值。
(个人理解:js解析的时候,把for循环解析了一遍, 运行的时候 i 已经等于节点数目了。绑定了 i 本身,故弹出的 i 等于节点数目。我们需要弹出的 i 是在它被构造时的值。)
解决糟糕的栗子:
var add_the_handlers = function(nodes){ var helper = function(i){ return function(){ alert(i); }; }; var i; for(i=0; i<nodes.length; i++){ nodes[i].onclick = helper(i); } };
避免在循环中创建函数,这会带来无谓的计算,还会引起混淆。
可以在循环之外先创建一个辅助函数helper,让辅助函数返回一个绑定了当前 i 值的函数,这样就不会混淆了。
模块
我们可以使用函数和闭包来构建模块,这几乎可以完全摒弃全局变量的使用。
模块是一个提供接口却隐藏状态和实现的函数或对象。
模块模式也可以用来产生安全的对象。
我们来构造一个产生序列号的对象:
var serial_maker = function(){ // 该对象包含一个设置前缀的方法 // 该对象包含一个设置序列号的方法 // 和一个生成字符串的方法 var prefix = ‘‘; //前缀 var seq = 0; //序列号 return { set_prefix: function(p){ prefix = String(p); }, set_seq: function(s){ seq = s; }, generate: function(){ var result = prefix + seq; seq += 1; return result; } }; }; // 调用 var seqer = serial_maker(); seqer.set_prefix(‘Q‘); seqer.set_seq(1000); var unique = seqer.generate(); // "Q1000"
如果我们把seqer.generate作为一个值传递给第三方函数,这个函数能产生唯一的字符串,但却不能通过它来改变prefix和seq的值。
时间: 2024-10-19 10:14:24