JavaScript: The Good Parts 读书笔记(二)
二.函数
JS 中函数亦是对象var add = function(a,b){return a + b;};?函数的声明:var myObject = {value : 0,increment : function(inc){// 方法this可以访问对象的属性。 this到对象的绑定发生在调用时。// 这种延迟绑定使得函数可以对this进行高度复用.// 通过this可以访问所属对象上下文的方法称为公共方法(!).this.value += typeof inc === "number" ? inc : 1;}};myObject.increment(); // 使用属性存取表达式调用,属于方法调用模式.document.writeln(myObject.value);myObject.increment(2);document.writeln(myObject.value);?函数调用模式var sum = add(3,4); // 7?? 当函数以此模式调用时,this被绑定到全局对象。这是JS语言设计上的一个错误。在正确设计时,当函数中的内部函数被调用时,this应该仍然绑定到外部函数的this变量.这个设计错误的后果是方法不能利用内部函数来帮助它工作,因为内部函数的this被绑定了错误的值, 所以不能共享该方法对原对象的访问权。幸运的是,有一个很容 易的解决方案。通过定义一个临时变量.
myObject.double = function(){var that = this; // 保存外部函数的上下文.var helper = function(){that.value = add(that.value,that.value);};helper(); // 函数方式调用helper. 此时helper内部的context 是 window. 所以需要定义 that.};myObject.double();document.writeln(myObject.value);?构造器调用模式// 按照约定,对于构造器变量. 它们需要以开头大写的格式来命名。var Quo = function(string){this.status = string;};// 为所有Quo 的实例提供一个get_status方法.Quo.prototype.get_status = function(){return this.status;};var myQuo = new Quo("confused");document.writeln(myQuo.get_status()); // 令人困惑的结果.?Apply/Call 调用模式var args = [3,4];var sum = add.apply(null,args); // context 为null 时表示将context 绑定到 window.document.writeln(sum);var statusObject = {status : "A-OK"};// 使用statusObject 作为函数上下文去调用 get_status方法.var status = Quo.prototype.get_status.apply(statusObject);document.writeln(status);? ?apply传入的是一个参数数组,也就是将多个参数组合成为一个数组传入,而call则作为call调用的参数传入(从第二个参数开始)。如 func.call(func1,var1,var2,var3)对应的apply写法为:func.apply(func1,[var1,var2,var3])使用apply的好处是可以直接将当前函数的arguments对象作为apply的第二个参数传入.var sum = function(){var i,sum = 0;for(i = 0 ; i < arguments.length; i++){sum+=arguments[i];}return sum;};document.writeln(sum(4,8,15,16,23,42));// 108?函数的返回值var checked_add = function(a,b){if(typeof a !== 'number' || typeof b !== 'number'){throw{name : "TypeError",message : "Add needs Numbbers!"};}return add(a,b);};// 通过使用 catch(exception) 可以捕获抛出的异常信息.// 这里与强类型语言不同,一个try块只能存在一个捕获所有异常的catch块(!).var try_it = function(){try{checked_add('not number');}catch(e){document.writeln(e.name+":"+e.message);}};try_it();?扩展函数对象Function.prototype.method = function(name,func){this.prototype[name] = func;return this;};// 此时该扩展的method 方法对所有继承自改原型链的对象生效.// 该方法可以简化使用 prototype.XX = function(){} 来增加功能.// 简单类型可以使用 new Number(),new String() 等构造实例,// 表明这些对象也继承自原型链 Function.prototype.Number.method("integer",function(){ // 为所有数字增加一个取整方法.return Math[this < 0 ? "ceiling" : "floor"](this);});document.writeln(( 10 / 3).integer()); // 3.3333 --> 3String.method("trim",function(){return this.replace(/^\s+|\s+$/g,'');});document.writeln(" Hello World! ".trim());?? // Function.prototype.method = function(name, func){// if(!this.prototype[name]){// this.prototype[name] = func;// }// }?函数的递归var walk_the_dom = function(node, callback){ // 自顶向下遍历DOM树callback(node);node = node.firstChild;while(node){walk_the_dom(node, callback);node = node.nextSibling;}};var getElementsByAttribute = function(attr,value){var results = [];walk_the_dom(document.body,function(node){var actual = node.nodeType === 1 && node.getAttribute(attr);if(typeof actual === 'string' && (actual === value || typeof value !== 'string')){results.push(node);}});return results;};?Javascript 的块作用域var foo = function(){var a = 3, b = 5;var bar = function(){var b = 7; c = 11; // b 覆盖了外面 b = 5 的定义;(其他语言中不可以这样声明)a+=b+c; // a += 7 + 11 --> 21. 因为闭包的关系,外面的 a 被赋值为 21.window.alert(b); // 在该块内,b = 7;};bar();window.alert(b); // 在块外, b = 5;window.alert(c); // 可以访问在块内声明的c};foo();?var myObject_protected = function(){var value = 0;// 这里通过一个函数的形式初始化了对象。由于函数作用域的关系,内部// 函数依然可以访问 value的值(闭包).return {increment : function(inc){value += typeof inc === 'number' ? inc : 1;},getValue : function(){return value;}};};var myObject_p = new myObject_protected();myObject_p.increment(10);document.writeln(myObject_p.getValue());几个使用闭包的例子:var fade = function(node){var level = 1;var step = function(){var hex = level.toString(16);node.style.backgroundColor = "#FFFF"+hex+hex;// 这里只是通过条件判断来避免死循环。if(level < 15){level += 1;window.setTimeout(step,100);}};window.setTimeout(step,100);};fade(document.body);?// var add_the_handlers = function(nodes){// var i;// for(i = 0 ; i < nodes.length ; i ++){// nodes[i].onclick = function(e){// //(!) 直接访问了外部变量i, 并不是复制一份i的实例。// //所以随着i一直自增,最后弹出来的均是nodes.length// alert(i); // }// }// }?矫正方法:// var add_the_handlers = function(nodes){// var i;// for(i = 0 ; i < nodes.length ; i ++){// nodes[i].onclick = function(i){//return function(e){ // 返回另一个匿名函数,但是该函数可以访问外面匿名函数构造时传入的 i 实例.// alert(i);// };// }(i); // 立即调用匿名函数 function(i);// }// }?window.setTimeout(function(){for(var i = 1 ; i <= 10 ; i++){window.setTimeout(function(i){return function(){var hex = i.toString(16);document.body.style.backgroundColor = "#FF"+hex+hex+"FF";};}(i), 100*i);}},2000);?模块模式String.method("deentityify",function(){// 字符实体表,它存放在闭包中,不会在每次deentityify调用时被实例化.var entity = {quot : '"',lt : '<',gt : '>'};// 返回真正的deentityify 方法return function(){return this.replace(/&([^&;]+);/g, function(a,b){var r = entity[b];return typeof r === 'string' ? r : a;});};}());// 注意最后一行使用()运算立即调用了外层的匿名函数,该函数返回了真正的实现方法。document.writeln("<">".deentityify());?var serial_marker = function(){var prefix = '';var seq = 0;return {set_prefix : function(p){prefix = p;},set_seq : function(q){seq = q;},gensym: function(){var result = prefix + seq;seq++;return result;}};};var seqer = serial_marker();seqer.set_prefix('Q');seqer.set_seq(1000);document.writeln(seqer.gensym());document.writeln(seqer.gensym());?