当前位置: 首页 > 科技观察

能看懂这10个JavaScript难点的程序员,运气不会太差……

时间:2023-03-20 18:58:28 科技观察

1。ImmediatelyInvokedFunctionImmediatelyInvokedFunctionExpression(IIFE),顾名思义,就是创建一个函数并立即执行。它不绑定任何事件,也不需要等待任何异步操作:(function(){//code//...})();function(){...}是一个匿名函数,它周围的那对括号会变成一个表达式,后面跟着一对调用该函数的括号。立即执行函数也可以理解为立即调用匿名函数。立即执行函数最常见的应用场景是将var变量的作用域限制在你的函数中,以避免命名冲突。2.闭包对于闭包(closure),当外层函数返回时,内层函数仍然可以访问外层函数的变量。functionf1(){varN=0;//N是f1函数的局部变量functionf2()//f2是f1函数的内部函数,是一个闭包{N+=1;//外部函数f1是在函数f2内部使用变量Nconsole.log(N);}returnf2;}varresult=f1();result();//输出1result();//输出2result();//输出3代码,外部函数f1只执行一次,变量N置0,内部函数f2赋值给变量result。由于外部函数f1已经执行完毕,其内部变量N应该在内存中被清除,但事实并非如此:每次调用result时,我们发现变量N一直在内存中,并且在累积。为什么?这就是闭包的魔力!3.使用闭包定义私有变量通常,JavaScript开发人员使用下划线作为私有变量的前缀。但实际上这些变量还是可以被访问和修改的,并不是真正的私有变量。这时候使用闭包可以定义真正的私有变量:functionProduct(){varname;this.setName=function(value){name=value;};this.getName=function(){returnname;};}varp=newProduct();p.setName("Fundebug");console.log(p.name);//输出undefinedconsole.log(p.getName());//输出Fundebug代码,对象p的name属性是private的属性不能直接使用p.name访问。4.prototype每个JavaScript构造函数都有一个prototype属性,用来设置所有实例对象需要共享的属性和方法。无法枚举原型属性。JavaScript仅支持通过原型属性继承属性和方法。functionRectangle(x,y){this._length=x;this._breadth=y;}Rectangle.prototype.getDimensions=function(){return{length:this._length,breadth:this._breadth};};varx=newRectangle(3,4);变化=新矩形(4,3);console.log(x.getDimensions());//{length:3,breadth:4}console.log(y.getDimensions());//在{length:4,breadth:3}代码中,x和y是构造函数Rectangle创建的对象实例,它们通过原型继承了getDimensions方法。5.模块化JavaScript不是模块化编程语言,至少在ES6出现之前不是。然而,对于一个复杂的Web应用程序来说,模块化编程是一个基本要求。这时候可以使用立即执行函数来实现模块化,就像jQuery等很多JS库都是这样实现的。varmodule=(function(){varN=5;functionprint(x){console.log("Theresultis:"+x);}functionadd(a){varx=a+N;print(x);}return{description:"Thisisdescription",add:add};})();console.log(module.description);//输出"thisisdescription"module.add(5);//输出"Theresultis:10"所谓模块化就是基于需要控制模块中属性和方法的可访问性,即private或public。代码中,module是一个独立的模块,N是它的私有属性,print是它的私有方法,description是它的公有属性,add是它的公有方法。6.变量提升JavaScript会将所有的变量和函数声明移到其作用域的前面,这称为变量提升(Hoisting)。也就是说,无论你在哪里声明变量和函数,解释器都会将它们移到作用域的前面。所以我们可以先使用变量和函数,再声明。然而,只有变量声明被提升,而不是变量赋值。如果不理解这个,有时候会出错:console.log(y);//outputundefinedy=2;//初始化y上面的代码相当于下面的代码:vary;//stateyconsole.log(y);//Outputundefinedy=2;//初始化y为了避免bug,开发者应该在每个作用域的开头声明变量和函数。7.CurryingCurrying,即柯里化,可以让函数更加灵活。我们可以通过一次传入多个参数来调用它;我们也可以只传入一部分参数来调用它,让它返回一个函数来处理剩下的参数。varadd=function(x){returnfunction(y){returnx+y;};};console.log(add(1)(1));//输出2varadd1=add(1);console.log(add1(1)));//输出2varadd10=add(10);console.log(add10(1));//输出11的代码,我们可以一次性传入两个1作为参数add(1)(1),还有你传入一个参数就可以得到add1和add10函数,使用起来非常灵活。8.Apply、call和bind方法JavaScript开发人员必须了解apply、call和bind方法之间的区别。它们的共同点是第一个参数是this,也就是函数运行时所依赖的上下文。三者中,call方式最简单,相当于通过指定这个值来调用函数:varuser={name:"RahulMhatre",whatIsYourName:function(){console.log(this.name);}};user.whatIsYourName();//输出"RahulMhatre",varuser2={name:"NehaSampat"};user.whatIsYourName.call(user2);//输出“NehaSampat”apply方法和call方法类似。两者唯一的区别是apply方法使用数组来指定参数,而call方法需要分别指定每个参数:apply(thisArg,[argsArray])call(thisArg,arg1,arg2,…)varuser={greet:"你好!",greetUser:function(userName){console.log(this.greet+""+userName);}};vargreet1={greet:"Hola"};user.greetUser.call(greet1,"Rahul");//Output"HolaRahul"user.greetUser.apply(greet1,["Rahul"]);//Output"HolaRahul"使用bind方法,可以将这个值绑定到函数上,作为新函数返回:varuser={greet:"Hello!",greetUser:function(userName){console.log(this.greet+""+userName);}};vargreetHola=user.greetUser.bind({greet:"Hola"});vargreetBonjour=用户。greetUser.bind({greet:"Bonjour"});greetHola("Rahul")//输出"HolaRahul"greetBonjour("Rahul")//输出"BonjourRahul"9.MemoizationMemoization用于优化耗时计算,通过Cache计算结果到内存中,使得对于相同的输入值,下次只需要读取内存中的结果即可。functionmemoizeFunction(func){varcache={};returnfunction(){varkey=arguments[0];if(cache[key]){returncache[key];}else{varval=func.apply(this,arguments);cache[key]=val;returnval;}};}varfibonacci=memoizeFunction(function(n){return(n===0||n===1)?n:fibonacci(n-1)+fibonacci(n-2);});console.log(fibonacci(100));//输出35424848179262000000console.log(fibonacci(100));//输出354224848179262000000代码中,第二次计算fibonacci(100)只需要直接在内存读取结果。10、函数重载所谓函数重载(方法重载)就是函数名相同,但输入输出不同。换句话说,允许一个函数有不同的输入,并根据不同的输入返回不同的结果。直观上,函数重载可以通过if...else或者switch来实现,不用管它。jQuery之父JohnResig提出了一个非常聪明(bian)妙(tai)的方法,使用闭包。实际上,people对象的find方法允许3种不同的输入:参数为0时,返回所有名称;参数为0时,返回所有名称。当有1个参数时,根据firstName查找人名返回;有2个参数时,根据全名查找人名,返回。难点在于people.find只能绑定一个函数,为什么可以处理三个不同的输入呢?不可能同时绑定三个函数find0、find1、find2!这里的关键是旧属性。根据addMethod函数的调用顺序,people.find最终绑定到了find2函数。但是绑定find2时,old是find1;同样,绑定find1时,old是find0。因此,三个函数find0、find1和find2通过闭包链接起来。按照addMethod的逻辑,当f.length不匹配arguments.length时,会调用old直到匹配。functionaddMethod(object,name,f){  varold=object[name];  object[name]=function(){//f.length是函数定义时的参数个数//arguments。length是调用函数时的参数个数    if(f.length===arguments.length){  returnf.apply(this,arguments);    }elseif(typeofold==="function"){returnold.apply(this,arguments);    }  };}//不传参数时,返回所有名字functionfind0(){  returnthis.names;}//当传入一个参数时,返回firstName匹配名字函数find1(firstName){  varresult=[];  for(vari=0;i