标签:
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.有新元素加入数组时,自动更新length 112 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与caller 134 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.length 156 ) 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 1 208 Html001.html (第 23 行) 209 2 210 Html001.html (第 23 行) 211 3 212 Html001.html (第 23 行) 213 4 214 Html001.html (第 23 行) 215 5 216 Html001.html (第 23 行) 217 6 218 Html001.html (第 23 行) 219 7 220 Html001.html (第 23 行) 221 8 222 Html001.html (第 23 行) 223 9 224 Html001.html (第 23 行) 225 10 226 Html001.html (第 23 行) 227 11 228 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()也是在其作用域链内寻找scope 270 //这个作用域链在函数定义之初就开始存在 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(),任何函数或语句都访问不到其内部的count 285 //而又因为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());//=>arr1 364 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]();//=》返回值为5 374 375 上述代码循环创建了很多闭包 376 377 总结性语言:多个嵌套函数并不会将作用域链上的私有成员复制一份,他们是共享的。 378 379 例子8: 380 嵌套函数内,也就是闭包,无法访问外部函数的arguments和this 381 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,或者undefined 391 //闭包 end 392 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);
标签:
原文地址:http://www.cnblogs.com/windSamW/p/4937855.html