你的位置:首页 > Java教程

[Java教程]犀牛笔记


 1 1.函数作为参数传给其他函数: 2   data.sort(function(a,b){return a-b}) 3   //关于数组的sort函数,其回调函数返回负值,a在b之前 4   //正值,b在a之前 5  6 2.函数定义好后立即调用 7   var temp=(function(x){return x*x})(10); 8  9   function foo(){};//函数声明,可以提前 10   var foo=function(){};//表达式定义函数,不能提前 11  12 3.JAVASCRIPT的函数可以嵌套在其他函数里面 13   function hy(a,b){ 14     function square(x){return x*x;} 15     return Math.sqrt(square(a)+square(b)); 16   } 17  18 4.函数声明语句并非真正的语句,他们可以出现在全局代码里,或者内嵌在其他函数内, 19 但是它们并不能出现在循环,条件判断或者try/catch语句中。 20 但函数定义表达式可以出现在javascript的任何地方 21  22 5.jquery的方法链式调用原理 23   $(":header").map(function(){return this.id;}).get().set(); 24   当方法不需要返回值时,建议将其返回值定为return this; 25   这样就可以实现方法的链式调用。 26  27 6.嵌套函数中this的指向问题 28 var o={ 29      m:function(){ 30        var This=this; 31         f(); 32  33        function f(){ 34          console.log(this); 35           console.log(This); 36         } 37       } 38     }; 39   o.m(); 40 以上示例中,对象o内定义了函数m(),在函数m()内定义函数f()并调用 41 在f内的this并不指向调用外层函数的上下文,而是指向全局对象 42 其返回值: 43  44       Window Html001.html//严格模式下返回undefined 45       Object { m=function()} 46  47 7.构造函数的调用 48 如果在函数或者方法调用前带有关键字new,就形成了构造函数调用 49 var o=new Object(); 50 var o=new Object;//没有形参时可以省略掉Object后面的括号 51 一旦使用了new关键字调用构造函数,构造函数内的this就会指向new出来的新对象 52     var o={ 53      m:function(){ 54        var This=this; 55         f(); 56  57        function f(){ 58          //console.log(this); 59          console.log(This===o); 60         } 61       } 62     }; 63     new o.m();//函数内部this不指向上下文,也就是不指向o 64  65 8.间接调用,call()与aplly() 66 允许函数显示的指定函数运行的域,也就是规定this的指向 67 foo.call(obj1,args1...); 68 foo.apply(obj2,arg[...]); 69  70 9.函数调用的形参与实参 71 function foo(arg1,arg2,arg3....){} 72  73 foo(arg1);  74 如果函数调用时传入的实参比函数定义时的形参数要少,则多的形参被定义为undefined 75 这个特性可用来实现类似函数重载的功能 76  77 function getPropertyName(o,/*可选参数,数组a*/arr) { 78   // body... 79   var arr=arr||[];//undefined在逻辑运算中返回false,若调用时a不存在,则将arr赋予新的空数组[] 80   for(var p in o) arr.push(p); 81     return arr; 82 } 83 getPropertyName(o); 84 getPropertyName(o,myArray); 85  86 设计原则: 87   当使用这种可选参数来创建函数时,满足原则1.可选参数放在参数列表最后。2./**/用注释说明可选参数 88     3.调用函数时,可选参数位置最好显式的传入null作为占位符。 89  90 function foo(arg1,arg2,arg3....){} 91  92 foo(arg1);  93 再回到这个函数调用例子,当实参列表超过形参列表时,函数内部无法显式获得超过部分的引用 94 此时可用arguments类数组来调用,事实上arguments[]数组内存储着传进来的所有实参 95 arguments也包含着length属性; 96  97 实参对象的应用实例: 98     (function (x,y,z){ 99       if(arguments.length!=3){100         throw new Error(101             "该函数必须传入3个参数"102         );103       }104       console.log(x+y+z);105     }(1,2));106     运行结果:Error: 该函数必须传入3个参数107   用于验证实参列表个数,只有在实参数目为3时,才执行函数。108 /**109 复习:类数组对象110   js的数组有四大特性:111     1.有新元素加入数组时,自动更新length112     2.设置length为一个较小值时将截断数组113     3.从Array.prototype继承一些方法114     4.其类属性为“Array”115 116   这四个特性让数组与类数组对象区分开来117   而像arguments这样的类数组对象,可以理解为其属性名刚好为从0开始的非负整数序列118   且有个length属性记录它的属性长度119 120   而区别在于length没有数组的特性1,2.121   且没有从Array.prototype继承方法122 123   但是可以利用call临时继承调用124   var a={"0":"a","1":"b","2":"c"};125 126   Array.prototype.join.call(a,"+") //"a+b+c"127   128   Array.map(function(x){return x*x}) //ecma5,不修改原数组,返回一个数组副本,其中的每一项都是129                     参数内函数的返回值。130 131 */132 细说实参对象arguments://该对象的可枚举属性仅有实参133   属性callee与caller134       callee:指代当前正在执行的函数135       caller:调用当前正在执行的函数的函数//!--暂时无法理解这句话--!136 137       callee可用在匿名函数中实现递归138       function (x) {139         // body...140         if(x<1)return 1;141         return x*arguments.callee(x-1);//此处的arguments.callee()就是在调用函数本身142       }143 144 10.将对象属性用作实参,减少记忆参数列表顺序,在参数列表巨大的情况下145   function arrayCopy(a,a_start,b,b_start,length) {//该函数将a数组从a_start处复制length个元素至b数组中起始位置b_start位置146           // 要记忆这些形参的顺序实在不容易147         }148 149   function easyCopy(argument) {150           // body...151           arrayCopy(arguments.from,152               arguments.from_start||0,153               arguments.to,154               arguments.to_start||0,155               arguments.length156             )157         }158   //在调用easyCopy时,只需传入一个匿名对象就可以了159   a=[1,2,3,4];160   b=[];161   easyCopy(162     {from:a,to:b,length:4}//传入匿名对象163     );164 165 11.作为值的函数166   在js中,函数也可以作为--实参,传入别的函数参数列表;167   看如下一个函数的定义168     function square(x) {return x*x}169   这个定义创建一个新的函数对象,并将其值赋给square;170   var s=square;//现在s和square指代同一个函数171 172   类似数组的sort()方法,接受一个函数173   写法Array.sort(function (a,b) {return a-b;174     // body...175   })176   或者Array.sort(bj);//bj指代一个函数177 178 12.自定义函数属性179   js中函数是对象,所以可以定义函数的属性,用于计数之类的工作180   比如要计算一个函数的调用次数,又不想定义一个全局变量181   则可以:182   foo.times=0;183   function foo(argument) {184     // body...185     foo.times++;186   }//还有更多用途,187 188   //定义缓存189   foo.times=0;//判断if语句执行次数的标记190     function foo(x){191       if(x<1&&x!=Math.round(x)) throw new Error("foo()函数参数为大于1的正整数");192       if( !(x in foo)){193         foo.times++;194         console.log(foo.times);195         foo[x]=x*foo(x-1);//将已经计算出来的值存储进foo[]类数组之中196       }197       return foo[x];198     }199     foo[1]=1;200 201     foo(11);202     foo(10);//从返回结果可以看到,此处并没进入if判断中,减少了很多次运算203     foo(12);204     foo(0.6);//验证数值输入的正确性205 206       代码运行返回结果:207           1208           Html001.html (第 23 行)209           2210           Html001.html (第 23 行)211           3212           Html001.html (第 23 行)213           4214           Html001.html (第 23 行)215           5216           Html001.html (第 23 行)217           6218           Html001.html (第 23 行)219           7220           Html001.html (第 23 行)221           8222           Html001.html (第 23 行)223           9224           Html001.html (第 23 行)225           10226           Html001.html (第 23 行)227           11228           Html001.html (第 23 行)229           Error: foo()函数参数为大于1的正整数230 231 13.作为命名空间的函数232   类似jquery这样的库的做法233   a.(function (argument) {234     // body...235   }())236   b.(function (argument) {237     // body...238   })();239   定义一个匿名函数并立即调用,在匿名函数内的变量就不会污染全局命名空间240   对于包裹function的圆括号的解释:241     function前面没有圆括号时,js会将function解释为函数声明语句,这样就无法立即执行242     而加上圆括号,或是其他运算符,则会被解释为函数定义表达式。243 244 14.闭包245   js中,函数的执行依赖于变量作用域,这个作用域是在函数定义时创建的,而不是在函数执行时。246   例子1:247     var scope="global scope";248     function checkscope() {249       // body...250       var scope="local scope";251       function f() {252         // body...253         return scope;254       }255       return f();//f在checkscope内部执行时很容易理解,它寻找f的作用域,在checkscope函数内找到scope就返回了256     }257     checkscope();258   例子2:259     var scope="global scope";260     function checkscope() {261       // body...262       var scope="local scope";263       function f() {264         // body...265         return scope;266       }267       return f;268     }269     checkscope()();  //在全局执行chechscope内部的函数f;因为闭包的存在,f()也是在其作用域链内寻找scope270             //这个作用域链在函数定义之初就开始存在271 272   例子3:          273   foo.times=0;274   function foo(argument) {275     // body...276     foo.times++;277   }//这是12小结的一个例子,foo.times来记录函数foo的调用次数278   下面用闭包来改写它。279     var times=(function () {280         var count=0;281         return function(){282           return count++;283         }284     }())//当外层作为命名空间的函数返回之后,除了timer(),任何函数或语句都访问不到其内部的count285       //而又因为times在外部使用其作用域链上的count,所以count会作为一个变量一直存在内存286       //times()每次调用都访问的是内存中已经存在的count;故可以充当计数器287     function foo(argument) {288     // body...289       times();290     }291 292 293   例子4:294   function counter(){295     var count=0;296     return {297       count:function(){return ++count;},298       reset:function(){return count=0;}299     }//返回一个包含两个方法的匿名对象300   }301 302   var obj1=counter();303   var obj2=counter();//obj1和obj2接受到的是不同的Object。所以他们的count互不干涉304    console.log(obj1.count());305    console.log(obj1.count());306    console.log(obj1.count());307 308   例子5://ECMA5中的变体309   function counter(n){310     return {311        get count(){312         return ++n;313        },314        set count(m){315         if (m<=n) {throw Error("您设置的n太小了")}316         return n=m;317        }318      }319    }320   var obj=counter(100);321    console.log(obj.count);322    console.log(obj.count);323    console.log(obj.count);324   console.log(obj.count=200);325    console.log(obj.count);326    console.log(obj.count);327      利用setter和getter来定义计数器count,给予计数器设置初值和临时更改的方法328      而count则是obj的私有属性,且通过赋值修改必须遵循get方法设置的条件329 330   例子6://闭包实现私有属性的经典做法331      function addPrivateProperty(o,name,predicate) {332       // body...333       var value;334       o["get"+name]=function () {335         return value;336       };337       o["set"+name]=function(v){338         if (predicate&&!predicate(v)) {throw new Error("数据未通过predicate函数合法性验证")}339         value=v;340       }341     }342 343     var o={};344     addPrivateProperty(o,"Name",function(v){return typeof v=="string";});345     addPrivateProperty(o,"Age");//未进行合法性验证346 347     o.setName("wangjue");348     o.setAge(18);349     console.log(o.getName());350     console.log(o.getAge());351     为何能够不断添加属性,是因为每个属性的set和get方法组--存在于不同的作用域链,352     可以利用如下例子进行例证:353     function constfunc(v){354       var value;355       return function(){356         value=v;357         return value;};358 359     }360     var fun1=constfunc("arr1");361     var fun2=constfunc();//发现只要传递的参数不一样,就会创建新的闭包362 363     console.log(fun1());//=>arr1364     console.log(fun2());//=>undefine  value并未共享365 366 367    例子7:368     function constfunc(v) {369       // body...370       return function(){return v;};371     }//一个总是返回v的函数372     for(var i=0;i<10;i++) fun[i]=constfunc(i);373     fun[5]();//=》返回值为5374 375      上述代码循环创建了很多闭包376 377    总结性语言:多个嵌套函数并不会将作用域链上的私有成员复制一份,他们是共享的。378 379    例子8:380      嵌套函数内,也就是闭包,无法访问外部函数的arguments和this381     function foo(arg1,arg2) {382       // body...383       var self=this;384       var outArguments=arguments;385 386       return function () {387         return self+outArguments.toString(" ");388        }389      }390      前文已经提过,嵌套函数内部使用this,指代的是全局环境window,或者undefined391     //闭包 end392 393 15.函数的属性,方法和构造方法394   在js中,函数是特殊的对象,故其也有对象的一些性质395     所以其也拥有属性和方法,能够用Function()构造函数创建。396 397   (1)length:函数对象也拥有一个length属性,其值为函数声明时的形参列表的表长。398         //一个检测实参与形参是否数量相等的函数399         function check(argument) {400           // body...401           if(argument.length!=argument.callee.length) throw new Error("传入参数数量不对")402         }403 404         function test(arg1,arg2) {405           // body...406           check(arguments);407           return arg1+arg2;408         }409 410         test(1,2);411 412   (2)prototype每一个函数都有一个原型属性413   (3)call(),与aplly()方法414 415   (4)bind(); //ECMA5中新增的方法,f.bind(obj);将函数f绑定至obj上416     兼容性解决:417       function bind(f,o) {418         // body...419         if(f.bind) return f.bind(o);420         else{421           return f.apply(o,arguments);422         }423       }//这里的自定义bind()函数就是将f绑定至o下,且立即执行424 425     function f(argument) {426       // body...427     }428     f.bind(o,agr1,arg2);//bind除第一个参数外,后面的几个参数都将传入f,参与函数调用429 430   (5)toString()//返回函数的完整源码431 432   (6)Function()构造函数;433 434 435 16.函数式编程436   类似数组的reduce(),map()之类的方法就是函数式编程的例子437 438 439 17.高阶函数,所谓高阶函数就是操作函数的函数,其传入函数作为参数,返回值也是函数440 441 18.记忆函数442   //闭包的实例443     function memorize(foo){444       var cache={};445       return function () {446         // body...447         var key=arguments.length+Array.prototype.join.call(arguments,",");//作为缓存的键448         if(key in cache) return cache[key];//若在cache中找到了key,则直接返回内存中的值449         else {450           cache[key]=foo.apply(this,arguments);//内存中没有,则赋值。451           return counter[key];//452         }453       }454     }455 456     //当我们使用一个递归函数时,往往要使用记忆功能457     var factorial=memorize(function (n) {458       // body...459       return n<=1?1:n*factorial(n-1);460     })461     factorial(5);