所有的过失在未犯以前,都已定下应处的惩罚。 莎士比亚
所有的选择在未抉择以前,都已定下他未来的筹码。 该佚名
js设计最出色的就是函数设计。
函数用于指定对象的行为。
一般来说,所谓编程。。。就是将一组需求 分解为 ① 一组函数 ② 数据结构
- 函数对象(function objects)(对象------属性名/属性值得集合,且拥有连接到原型对象的隐藏链接。)
· 对象字面量 连接到 Object.prototype
· 函数字面量 连接到 Function.prototype (每个函数在创建的时候,都会附加两个隐藏属性:函数的上下文和实现函数行为的代码)(因为函数是对象,所以可以有方法,所以可以被保存在变量里,对象数组中,可以被当做参数传递给其他函数,可以被当做返回值被return)(与对象唯一不同的是,函数可以被调用)
- 函数字面量(function literral)
函数的字面量一共包含四个部分 1 保留字 function 2 函数名 ,但是也可以被省略。 额。。。就成了匿名函数麽 (anonymous) 可以通过函数名来调用自己。 额。。。就成了递归函数麽 函数名特能被调试器和开发工具用来识别函数。 3 ()形参,区切,不会被初始化成undefined,是调用该函数的时候传进来的实参的值。 4 {}函数体。 函数字面量可以出现在任何表达式可以出现的地方。 可以被定义在其他函数中。 一个内部的函数除了可以访问自己的参数和变量,还可以自由的访问嵌套他的父级函数的参数和变量。 创建函数对象的时候,会包含着一个链接上下文的链接,这被称之为闭包。(closure)
- 调用(invocation)
主函数 =》 子函数 ① 暂停主函数的执行 ② 主函数把控制权和参数传递给子函数 (关于参数,除了传递过去的形参,还有两个附加参数:this和arguments) 【重头戏啊】 函数中的,其实this才重要。 果不其然,无论到哪里,想清楚自己才是最最最重要。 js一共有四种调用方式 · 方法调用模式 · 函数调用模式 · 构造函数调用模式 · apply 调用模式 以上四种方式,在如何初始化this方面存在差异。 (反正挺老师说过,箭头函数和普通函数中的this是不一样的。) 调用运算符 () (逗号分隔) 调用时候的实体参数,可以和函数定义的形式参数的个数不匹配。 实体参数 》 形参 多出来的参数将会被忽略 反之 没有被赋值得到形参,讲会是undefined。 另 不会对数据类型进行检查。。。。。。 以下 4 到 7 其实是调用的子集~~~
- 方法调用模式(the method invocation pattern)
// 创建 myObject 对象。 // 该对象有一个 value 属性 和 一个increment 方法 // increment 方法接受一个可选参数 // 如果调用函数的时候,实体参数不是数字的场合 // 那么会有一个默认值 数字1 // 对象体定义 var myObject = { value : 0 , increment : function (inc) { this.value += typeof inc === 'number' ? inc : 1; } } // 方法调用模式 // 当实体参数的个数少于形参的时候 // 形参将会为undefined myObject.increment(); document.writeln(myObject.value); // 不是数字的场合,返回1 // 实体参数是数字的场合 myObject.increment(2); document.writeln(myObject.value); // 是数字的场合, // 返回 之前的值(突然发现上面返回1以后value就是1 了, // 然后+=2 ,于是现在返回的是3)
- 函数调用模式(the function invocation pattern)
// 对象体定义 var myObject = { value : 0 , increment : function (inc) { this.value += typeof inc === 'number' ? inc : 1; } } // 当实体参数的个数少于形参的时候 // 形参将会为undefined myObject.increment(); // 实体参数是数字的场合 myObject.increment(2); ///////////////////////////////上集回顾,为了整体可以运行哈///////////////////// // var sum = add(); // 创建一个名为add的函数变量 var add = function(a,b){ return a + b; } // 当给一个对象赋值一个他没有的属性的时候 // 该对象会被扩展 // eg 给myObject 增加一个double 方法 myObject.double = function(){ var that = this; var helper = function (){ // 以函数调用模式调用了helper that.value = add(that.value , that.value); } helper(); } // 方法调用模式 调用了double myObject.double(); document.writeln(myObject.value); // 终于对了 上文提要 一句都不能少 !!! 汗-_-||
- 构造函数调用模式(the constructor invocation pattern)
// 创建一个 Quo 的构造器函数 它构造一个带有 status 属性的对象 // 一个函数如果创建的目的,就是为了结合new关键字来调用,那么他就是构造函数 // 但作者不推荐使用 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调用模式(the apply invocation pattern)
// simple case // 创建一个名为add的函数变量 var add = function(a,b){ return a + b; } var array = [3,4]; var sum = add.apply(null,array); console.log(sum);
var Quo = function (string){ this.status = string; } Quo.prototype.get_status = function(){ return this.status; } // statusObject并没有继承自Quo.prototype // 但我们可以在statusObject上, // 通过apply 这种方式 来调用get_status方法 // 尽管statusObject其实并没有一个名为get_status的方法 var statusObject = { status : "A-OK" } var status = Quo.prototype.get_status.apply(statusObject); console.log(status);
- 参数(arguments)
// 构造一个好多数相加的函数 // 注意: 该函数内部定义的sum不会影响到外部定义的sum // 该函数只能看到内部定义的那个sum var sum = function(){ var i,sum = 0; for(i=0;i<arguments.length;i++){ sum += arguments[i]; } return sum; }; console.log(sum(1,2,3,4,5,6,7,8,9,10));// (1+10)*10/2=55
- 返回(return)
子函数 () { return ; // 使子函数提前啊结束,不在执行return以下的代码程序。 } // 结束函数体,讲控制权交还给主函数 主函数 () { 调用 子函数(); } // 一个函数总会有一个返回值的,如果都没有的时候,就会返回一个undefined // 而当是构造函数,且返回对象不是一个对象的时候,则返回this(该新对象) // 另需要注意的是 // 当返回值 比如是个json数据类型的时候 rerun { status:true; }; // 如果写成了下面这样,就不会返回json对象,只会返回undefined // 原因就是 js 会在return 后面自动补上 ; // 这是js的一个坑 需要注意 rerun { status:true; };
- 异常(exceptions)
var add = function (a,b){ if(typeof a !== 'number' || typeof b !== 'number' ){ throw { name : 'TypeError', message : 'add needs numbers' }; } return a + b; } var try_it = function(){ try{ add("seven"); }catch(e){ document.writeln(e.name + ": " + e.message); } } try_it();
- 扩充类型的功能(augmenting types)
// TODO 说是以前是 Object.prototype 需要在这个原型链上面 才可以给对象 追加方法 // 这个还是没太明白 还需要想想 // 通过给 Function.prototype 增加方法来使得该方法对所有函数都管用 // 通过给 Function.prototype 增加一个method的方法,下次给对象增加方法的时候就不用必须键入 // prototype这几个字符 Function.prototype.method = function (name,func){ // 因为是通过基本条件增加的方法,所以加一个条件判断 // 在确定没有该方法的时候,才去追加该方法 if(!this.prototype[name]){ this.prototype[name] = func; } return this; } // js没有专门的整数类型 取整??? // js本身的取整 有些丑陋 // 一般会通过给 Number.prototype增加一个 integer 方法来改善它 // 根据数字的正负 来判定是使用 ceiling 还是 使用 floor // 如下 Number.method('integer',function(){ return Math[this < 0 ? 'cell' : 'floor'](this); }); document.writeln((-10/3).integer()); document.writeln((10/3).integer()); // js缺少一个trim的方法 String.method('trim',function(){ return this.replace(/^\s+|\s$/g,''); }); document.writeln('"' + " neat ".trim() + '"');
- 递归(recursion)
// 1.汉诺塔 var hanoi = function (disc, one , two , three){ if(disc > 0){ hanoi(disc - 1,one , three , two); document.writeln('Move disc ' + disc + 'from ' + one + ' to ' + three + '<br>'); hanoi(disc - 1,two , one , three ); } }; hanoi( 3 , '①' , '②', '③' ); // 对 非常 nice // TODO 我还要做一个 图形界面的 回头单独做一个 link 文章 // 因为觉得 可能需要写一些 css js目测 应该并不会太多
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script type="text/javascript"> /// 另外一个问题 我在想另外一个问题 递归是不是 和分而治之 的 那个动态规划 // 就是那个什么把大问题 切成 小问题 分别解决的那个事情 是一起的 // 【划重点啊】递归函数 // 可以非常高效的去遍历树形结构的数据类型 // 比如浏览器短的文档对象模型DOM // 思路就是每次递归调用的时候,都处理指定的树的一小段 // 定义 wait_the_DOM 函数,它从某个指定的节点开始,按HTML源码中的顺序 // 访问每一个节点 // 她会调用一个函数 // 并且依次传递每个节点给他 // wait_the_DOM 调用自身去处理每一个节点 var wait_the_DOM = function walk(node,func){ // console.log(node); // console.log(node.firstChild); func(node); node = node.firstChild; while(node){ walk(node,func); node = node.nextSilbling; } } // 定义 getElementsByAttribute 函数。 // 它以一个属性名称字符串和一个可选的匹配值作为参数。 // 她调用 wait_the_DOM ,传递一个用来查找节点属性名的函数作为参数。 // 匹配的节点会累加到一个结果数组中。 var getElementsByAttribute = function (att , value) { var results = []; wait_the_DOM(document.body, function (node) { // console.log(node); var actual = node.nodeType === 1 && node.getAttribute(att); if(typeof actual === 'string' && (actual === value || typeof value !== 'string')){ results.push(node); } }); return results; }; </script> </head> <body class='testdiv'> <div class='testdiv'> 1<div>11<div class='testdiv'>111</div></div> </div> <div>2</div> <div class='testdiv'>3</div> <script type="text/javascript"> // for(var ele in getElementsByAttribute('class','testdiv')){ // console.log(ele); // } console.log(getElementsByAttribute('class','testdiv')); </script> </body> </html>
// 尾递归优化 // 如果一个函数返回自身递归调用的结果,那么调用过程会被替代为一个循环,可以显著提高速度。 // js当前没有提供尾递归优化。 // 深度递归的函数可能会因为 堆栈溢出 而运行失败 // 构建一个带尾递归的函数 // 因为它会返回自身调用结果,所以是尾递归 // 阶乘 n!=1×2×3×...×(n-1)×n var factorial = function factorial (i,a){ a = a || 1; if(i<2){ return a; } return factorial (i-1,a*i); } console.log(factorial(5));
- 作用域(scope)
// js 迷之作用域 // 。。。。。。 var foo = function (){ var a=3,b=5; var bar = function () { var b = 7,c=11; // 此时 a = 3 , b = 7 ,c = 11 // 而且 我理解 bar 中的 b 也不是 foo 中的b // b被盖掉了 console.log("1====="," a= " + a," b= " + b," c= " + c); a += b+c; // 此时 a = 21 , b = 7 ,c = 11 console.log("2====="," a= " + a," b= " + b," c= " + c); }; // 此时 a = 3,b=5 ,c 没有定义 console.log("3====="," a= " + a," b= " + b); bar(); // 此时 a = 21,b=5 console.log("4====="," a= " + a," b= " + b); }; foo();
- 闭包(closure)
// 作用域的好处,便是内部函数可以访问 外部函数的参数和变量 // 除了 this 和 arguments // TODO 一个更有趣的 内部函数 拥有比外部函数 更长的生命周期 ??? var myObject = (function(){ var value = 0; return { increment:function(inc){ value += typeof inc === 'number' ? inc:1; }, getValue:function(){ return value} }; }()); console.log(myObject.getValue()); myObject.increment(5); console.log(myObject.getValue()); // 结果是 0 5 // 效果是 函数外面 看不到 value 不能直接访问value // 当时 通过 return 出来的函数 // 可以修改 value的 value !!! // 感觉 有点像 java 里面 封装了那个私有的成员变量 // 当时可以 set get等等 公开化的成员函数 // 去对数据进行 修改操作的调调 ~~~ // 我要找回自己,找回我希冀中的自己 // 如果曾经错过,我希望未来不要继续迷失 // 我要努力遇见 // 遇见那个未来的 // 喜欢的自己 // 加油~~~
// 创建一个quo构造函数 // 她构造出带有 get_status 方法 和status 私有属性的一个对象。 var quo = function (status){ return { get_status : function (){ return status; } }; }; // 构造一个 quo实例 var myQuo = quo("chinsei"); console.log(myQuo.get_status());
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script type="text/javascript"> // 定义一个函数 DOM节点 渐渐变色 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; setTimeout(step,100); // console.log(level); }else if(level > 0){ // level -=1; level = 1; // console.log('AA'+level); setTimeout(step,100); } }; setTimeout(step,100); } fade(document.body); </script> </body> </html>
// 这个 李游 老师讲过 // bug 就是 只能alert 出来 3 // 打印出来的不对 // 构造一个函数 ,用错误的方式给一个数组中的节点 设置事件处理程序 // 当点击一个节点时,按照预期,应该 弹出来 123 // 但是实际上弹出来的 只是 333 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> .testdiv{ width: 100px; height: 100px; background-color: orange; margin: 10px; } </style> </head> <body> <div class='testdiv'>1</div> <div class='testdiv'>2</div> <div class='testdiv'>3</div> <script type="text/javascript"> var add_the_handlers = function(nodes){ var i; for(i=0;i<nodes.length;i++){ nodes[i].onclick = function(e){ alert(i); } } } add_the_handlers(document.getElementsByClassName('testdiv')); </script> </body> </html>
// 改良以后 // 用正确的方式 绑定点击事件 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> .testdiv{ width: 100px; height: 100px; background-color: orange; margin: 10px; } </style> </head> <body> <div class='testdiv'>1</div> <div class='testdiv'>2</div> <div class='testdiv'>3</div> <script type="text/javascript"> var add_the_handlers = function(nodes){ var helper = function(i){ return function(e){ alert((i+1)); }; } var i; for(i=0;i<nodes.length;i++){ // nodes[i].onclick = function(e){ // alert(i); // } nodes[i].onclick = helper(i); } } add_the_handlers(document.getElementsByClassName('testdiv')); </script> </body> </html>
- 回调(callbacks)
// 修改前 request = prepare_the_request(); response = send_request_synchronously(request); display(response); ==========>>>>>>>>>>>>>>>>> // 修改后 request = prepare_the_request(); send_request_synchronously(request,response,function(){ display(response); }); // 且 并不是一个线程 数据返回以前就死等 的效果 // 异步等待数据的获得 // 达到 一定 收到数据以后才绑定 返回 响应的效果
- 模块(module)
// 用来产生 序列号 的对象 // 返回一个用来产生唯一字符串的对象 // 唯一字符串的组成:前缀 + 序列号 // 该对象包含一个设置前缀的方法,一个设置序列号的方法 // 和一个产生唯一字符串的 gensym 方法 var serial_maker = function(){ var prefix = ''; var seq = 0; return { set_prefix : function(p){ prefix = String(p); }, set_seq : function(s){ seq = s; }, gensym : function(){ var rs = prefix + seq; seq++; return rs; } }; }; var seqer = serial_maker(); console.log(seqer); seqer.set_prefix('Q'); seqer.set_seq(1000); var unique = seqer.gensym(); console.log(unique);
Function.prototype.method = function (name,func){ if(!this.prototype[name]){ this.prototype[name] = func; } return this; } String.method('deentityify',function(){ // 字符实体表。她映射字符实体的名字到对应的字符。 var eentity = { quot:'"', lt:'<', gt:'>' }; // 返回deentityify方法 return function(){ // 这是deentityify方法,调用replace // 查找 ^'&' $';' 字符串 // 当这样的字符串被找到 就被 替换成 映射表中的值 return this.replace(/&([^&;]+);/g,function(a,b){ var r = eentity[b]; return typeof r === 'string' ? r : a; }); }; }());// 最后的这个()运算法 立刻调用我们刚刚构造出来的函数。 // 这个调用所创建并返回的函数才是deentityify方法。 console.log("<">".deentityify());
- 级联(cascade)
有一些方法没有 返回值 不想返回 undefined 想返回 this的时候 ,需要用到级联!!!???
- 柯里化(curry)
Function.prototype.method = function (name,func){ if(!this.prototype[name]){ this.prototype[name] = func; } return this; } var add = function(a,b){ return a+b; } Function.method('curry',function(){ var slice = Array.prototype.slice, args = slice.apply(arguments), that = this; return function(){ return that.apply(null,args.concat(slice.apply(arguments))); }; }); var add1 = add.curry(1); document.writeln(add1(6));
- 记忆(memoization)
// 函数会记忆先前操作的结果 记录在某个对象里 // 从而避免重复 运算 // js 的对象和数组 实现这种优化 非常方便 // 【※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※】 var memoizer = function (memo,formula){ var recur = function(n){ var result = memo[n]; if(typeof result !== 'number'){ result = formula(recur,n); memo[n] = result; } return result; }; return recur; } var fibonacci = memoizer([0,1],function(recur,n){ return recur(n-1) + recur(n-2); }); console.log(fibonacci(10)); var factorial = memoizer([1,1],function(recur,n){ return n * recur(n-1); }); console.log(factorial(6)); // 好么 这么好用 ~~~ 服了 !! 还是有点不太懂 ,我还得想想 ~~~