Web培训
400-996-5531
Web前端开发工程师需要掌握的20个JavaScript技巧(详解)作为Web前端开发人员避免不了js技术,今天小编汇总20个JS技术并详细给大家介绍,值得收藏!
1、确定对象的数据类型
functionmyType(type){
returnObject.prototype.toString.call(type).slice(8,-1);
使用Object.prototype.toString,通过传入不同类型的判断返回不同的判断函数,一行代码,简洁优雅灵活。
2、循环遍历数组map方法
constmyMap=function(fn,context){
letarr=Array.prototype.slice.call(this);
letresultArr=Array();
for(leti=0;i<arr.length;i++){
if(!arr.hasOwnProperty(i))
continue;resultArr[i]=fn.call(context,arr[i],i,this);
}
returnresultArr;
};
Array.prototype.myMap=myMap;
letarr=[1,2,3];
console.log(arr.myMap((item)=>item+1));//2,3,4
值得注意的是,map第二个参数在第一个参数回调中指向this。如果第一个参数是箭头函数,则第二个this的设置无效,因为箭头函数的词法绑定。
3、循环遍历数组过滤方法
constmyFilter=function(fn,context){
letarr=Array.prototype.slice.call(this)letresultArr=[]for(leti=0;i<arr.length;i++){
if(!arr.hasOwnProperty(i))continue;
fn.call(context,arr[i],i,this)&&resultArr.push(arr[i])
}
returnresultArr
}
Array.prototype.myFilter=myFilterletarr=[1,2,3]
console.log(arr.myFilter(item=>item===2))//[2]
4、使用reduce实现数组过滤方法
constmyFilter2=function(fn,context){
returnthis.reduce((total,current,index)=>{
returnfn.call(context,current,index,this)?[...total,current]:[...total]},[])
}
5、遍历数组的一些方法
constmySome=function(fn,context){
letarr=Array.prototype.slice.call(this);
//Theemptyarrayreturnsfalsedirectly,andtheeverymethodofthearrayreturnstrueconverselyif(!arr.length)
returnfalse;
for(leti=0;i<arr.length;i++){
if(!arr.hasOwnProperty(i))
continue;letres=fn.call(context,arr[i],i,this);
if(res)returntrue;
}
returnfalse;
};
Array.prototype.mySome=mySome;letarr=[1,2,3];
console.log(arr.mySome((item)=>item===2));
执行some的数组如果是空数组总是返回false,而另一个数组的every方法中的数组如果是空数组总是返回true。
6、通过循环实现数组的reduce方法
Array.prototype.myReduce=function(fn,initialValue){
letarr=Array.prototype.slice.call(this)
letstartItemletstartIndexif(initialValue===undefined){
//Findstheelementandsubscriptofthefirstnon-empty(real)
unitfor(leti=0;i<arr.length;i++){
if(!arr.hasOwnProperty(i))continuestartIndex=istartItem=arr[i]
break
}
}
else{
startItem=initialValue
}
//Thestartingpointfortraversalistherealelementaftertherealelementfoundinthepreviousstep
//Eachiterationskipstheelementsoftheemptycellfor(leti=++startIndex||0;i<arr.length;i++){
if(!arr.hasOwnProperty(i))continuestartItem=fn.call(null,startItem,arr[i],i,this)
}
returnstartItem
}
Array.prototype.myReduce=myReduceletarr=[1,2,3]console.log(arr.myReduce((acc,cur)=>acc+cur))
//6console.log(arr.reduce((acc,cur)=>acc+cur))//6
7、使用reduce实现array的flat方法
//reduceimplementsarray.prototype.flat,ArrayflatconstmyFlat=function(depth=1){
letarr=Array.prototype.slice.call(this)
if(depth===0)
returnarrreturnarr.reduce((total,current)=>{
if(Array.isArray(current)){
//Youneedtobindthiswithcall,otherwiseitpointstothewindowreturn
[...total,...myFlat.call(current,depth-1)]
}
else{
return[...total,current]}},[])}
Array.prototype.myFlat=myFlatletarr=[1,2,[3,4,[5,6,['a','b','c',['d']],7,8],9],10,11,12,[13,14]]
console.log(arr.myFlat())
因为myFlat依赖这个指向,所以需要在reduce遍历的时候指定myFlat的这个指向;否则默认指向window,会报错。
当数组的元素还是数组时,使用ES6的扩展运算符对其进行降维(ES5中可以使用concat方法)。但是数组元素内部可能有嵌套数组,所以,需要递归调用selfFlat。
同时,原生的Flat方法支持一个深度参数来表示降维的深度。默认值为1,表示数组减少一维。
传递Infinity将传递的数组变成一维数组:
8、实现ES6类语法
functionAnimal(name){
this.name=name
}
Animal.staticFunc=function(){
console.log('staticFunc')
}
Animal.prototype.sleep=function(){
console.log('animalissleeping')
}//Parasiticcombinatorialinheritance+inheritancebetweenconstructorsfunctionDog(name,color){
Animal.call(this,name)this.color=color
}
functioninherit(subType,superType){
//DuetothenatureofJavaScriptreferencetypesandfunctionspassingbyvalue,youcannotchangethereferenceaddressofsubTypesubType.prototype=Object.create(superType.prototype,{
constructor:{
enumerable:false,configurable:true,writable:true,
//Pointstosubclasses,consistentwiththedefaultinheritancebehaviorvalue:subType
}})
//Thechildconstructorinheritstheparentconstructor(thechildinheritsthestaticmethodsandstaticpropertiesoftheparentclass)
Object.setPrototypeOf(subType,superType)}inherit(Dog,Animal)
//YouneedtoaddtheprototypemethodtoDogafterinheritance,otherwiseitwillbeoverwrittenDog.prototype.barking=function(){console.log('wang!')
}
letbrownTeddy=newDog('teddy','brown')
Dog.staticFunc()console.log(brownTeddy)
brownTeddy.sleep()brownTeddy.barking()
Create方法创建一个空Object,并从Object.create方法的参数中继承这个空Object。
然后,让子类的原型(subType)等于空对象,就可以实现子类的原型等于空对象,空对象等于父类的继承原型。
Object.create支持第二个参数,它为生成的空对象定义属性和属性/访问器描述符。我们可以给这个空对象一个更符合默认继承行为的构造函数属性。
它也是一个不能枚举的内部属性(Enumerable:False)。
ES6类允许子类从父类继承静态方法和静态属性,而普通的寄生组合继承只能在实例之间实现。对于类到类的继承,需要定义额外的方法。
这里我们使用Object.setProtoTypeof将superType设置为subType的原型,从而能够从父类继承静态方法和静态属性。
9、函数的焦化
constdisplay=(a,b,c,d,e,f)=>[a,b,c,d,e,f];
/***
@descriptionCurrizationofafunction(Howmanytimesacurrizationfunctionneedstobeexecutedaccordingtothenumberofparametersofthefunctionbeforecurrization)
*@param{function
}
fn-TheCurrifiedfunction
*/functioncurry(fn){
if(fn.length<=1)
returnfn;constgenerator=(...args)=>{
if(fn.length===args.length){
//Executesfnandreturnstheexecutionresultreturnfn(...args)
}
else{return(...args2)=>{
//Returngeneratorfunctionreturngenerator(...args,...args2)
}
}
}
returngenerator
}
constcurriedDisplay=curry(display);
console.log("curriedDisplay",curriedDisplay(1)(2)(3)(4)(5)(6));
Currization是函数式编程中的一项重要技术,该技术将一个接受多个参数的函数转换为一系列接受一个参数的函数。
函数式编程compose另一个重要的功能,要能够进行函数组合,函数的组合只接受一个参数,所以如果你必须接受多个函数的需求并且需要使用compose函数组合,就需要使用compose的部分curry准备复合函数,让它总是只接受一个参数。
10、函数修正(占位符支持)
constcurry3=(fn,placeholder="_")=>{
curry3.placeholder=placeholderif(fn.length<=1)
returnfn;letargsList=[]constgenerator=(...args)=>{
letcurrentPlaceholderIndex=-1args.forEach(arg=>{
letplaceholderIndex=argsList.findIndex(item=>item===curry3.placeholder)if(placeholderIndex<0){
currentPlaceholderIndex=argsList.push(arg)-1//(1,'_')('_',2)
}
elseif(placeholderIndex!==currentPlaceholderIndex){argsList[placeholderIndex]=arg}else{argsList.push(arg)
}
}
)
letrealArgsList=argsList.filter(arg=>arg!==curry3.placeholder)
if(realArgsList.length>=fn.length){
returnfn(...argsList)
}
else{
returngenerator
}
}
returngenerator
}
constcurriedDisplay3=curry3(display);
console.log("curriedDisplay3",curriedDisplay3('_',2)(1,'_',4)(3,'_',)('_',5)(6)(7,8))
如果当前轮参数包含占位符,则将其放置在内部保存数组的末尾。当前轮的元素不填充当前轮参数的占位符,而只填充之前传入的占位符
11、斐波那契数列及其优化
constspeed=function(fn,num){
console.time('time')letvalue=fn(num)console.timeEnd('time')console.log(`result:${value}`)
}
/***@descriptionFibonaccinumbers
*@param{
number}n-Numberofpositions
*@return{
number
}
Theargumentcorrespondstoanumberinasequence
**/letfibonacci=function(n){
if(n<1)thrownewError('Parameteriswrong')if(n===1||n===2)
return1returnfibonacci(n-1)+fibonacci(n-2)
}
speed(fibonacci,40)
//Memoryfunctionconstmemory=function(fn){
letobj={
}
returnfunction(n){
if(obj[n]===undefined)obj[n]=fn(n)returnobj[n]
}
}
fibonacci=memory(fibonacci)speed(fibonacci,40)
/***@descriptionFibonaccidynamicprogrammingversion(Optimal)
**/functionfibonacci_DP(n){
letres=1if(n===1&&n===2)
returnresn=n-2letcur=1letpre=1while(n){
res=cur+prepre=curcur=resn--
}
returnres
}
speed(fibonacci_DP,40)
使用函数内存,您可以为经常依赖先前结果的计算节省大量时间,例如斐波那契数列。缺点是闭包中的obj对象占用了额外的内存。
另外,动态规划的空间复杂度比前者低,也是比较推荐的方案。
12、实现绑定方法
constisComplexDataType=obj=>(typeofobj==='object'
||typeofobj==='function')&&obj!==null
//ImplementasimplebindconstmyBind=function(bindTarget,...args1){
if(typeofthis!=='function')thrownewTypeError('Bindmustbecalledonafunction')
constoriginFunc=thisconstboundFunc=function(...args2){
//Callsusingthenewkeywordreturnanewobjectif(new.target){
letres=originFunc.call(this,...args1,...args2)
//Iftheconstructorreturnsanobject,thatobjectisreturnedif(isComplexDataType(res))returnres
//Otherwise,thenewlycreatedobjectisreturnedreturnthis
}
else{
returnoriginFunc.call(bindTarget,...args1,...args2)
}}
if(originFunc.prototype){
boundFunc.prototype=originFunc.prototype}constdesc=Object.getOwnPropertyDescriptors(originFunc)
Object.defineProperties(boundFunc,{
length:desc.length,name:Object.assign(desc.name,{value:`bound${
desc.name.value}`
})})
returnboundFunc
}
实现函数的bind方法的核心使用调用绑定指向this,同时考虑到其他情况如:
当bind返回的函数作为构造函数被new调用时,绑定值失效,变为new指定的对象。
定义绑定函数的长度和名称属性(不可枚举的属性)。
绑定函数的原型必须指向原函数的原型。
13、实现调用方法
constmyCall=function(context,...args){
letfunc=thiscontext
||(context=window)
if(typeoffunc!=='function')
thrownewTypeError('thisisnotfunction')
letcaller=Symbol('caller')
context[caller]=funcletres=context[caller](...args)
deletecontext[caller]returnres}
原理是将函数作为传入的上下文参数的属性执行。ES6Symbol类型用于防止属性冲突。
14、简单的CO模块
//Self-executinggeneratorfunctionsconstdata="{a:1,b:2}";
constdata2="{c:3,d:4}";
constdata3="{e:5,f:6}";
constapi=function(data){
returnnewPromise((resolve)=>{
setTimeout(()=>{
resolve(data);
},
1000);
});
};
function*func(){
letres=yieldapi(data);
console.log(res);
letres2=yieldapi(data2);console.log(res2);
letres3=yieldapi(data3);
console.log(res3);console.log(res,res2,res3);
}
functionmakePromisify(source){
if(source.then&&typeofsource.then==="function")
returnsource;returnPromise.resolve(source);
}
functionrun(generatorFunc){
letit=generatorFunc();
letresult=it.next();
returnnewPromise((resolve,reject)=>{
constnext=function(result){
if(result.done){
returnresolve(result.value);
}
result.value=makePromisify(result.value);
result.value.then((res)=>{letresult=it.next(res);
//Recursivelyexecutethenextfunctionnext(result);
}
).catch((err)=>{reject(err);
});
};
next(result);});
}run(func);
run函数接受一个生成器函数,每次run函数包裹的生成器函数遇到yield关键字时停止,当yield后的promise解析成功时,自动调用next方法执行到下一个yield关键字。
最后,每次成功解析一个promise,都会解析下一个promise。
当所有的结果都解析成功后,所有解析的结果都会被打印出来,演变成今天最常用的async/await语法。
15、功能防抖
/***@descriptiondebounce
*@param{
Function
}
func-Functionsthatneedfunctionstabilization
*@param{
Number
}
time-Delaytime
*@param{
Options
}
options-Configurationitems
*@return{Function
}
-Afunctionthathasbeenshakenout**//
***@typedef{
Object
}
Options-Configurationitems
*@property{
Boolean
}
leading-Whetheranextratriggerisrequiredtostart
*@property{
Boolean
}
trailing-Whetheranadditionaltriggerisrequiredaftertheend
*@property{this}context-this
**/constdebounce=(func,time=20,options={leading:true,context:null}
)=>{lettimer;const_debounce=function(...args){
if(timer){
clearTimeout(timer)}
if(options.leading&&!timer){
timer=setTimeout(null,time)
func.apply(options.context,args)}
else{
timer=setTimeout(()=>{
func.apply(options.context,args)timer=null},time)}};
_debounce.cancel=function(){
clearTimeout(timer)timer=null
};
return_debounce};
16、函数节流
/***@descriptionthrottle
*@param{
Function
}
func-Functionsthatrequirefunctionthrottling
*@param{Number}time-Delaytime
*@param{
Options
}
options-Configurationitems
*@return{
Function
}
-经过节流处理的函数**
//***@typedef{
Object
}
Options-Configurationitems
*@property{
Boolean
}
leading-Whetheranextratriggerisrequiredtostart
*@property{
Boolean
}
trailing-Whetheranadditionaltriggerisrequiredaftertheend
*@property{this}context-this**/
constthrottle=(func,time=17,options={
//leading和trailing无法同时为falseleading:true,trailing:false,context:null})=>{letprevious=newDate(0).getTime()lettimer;const_throttle=function(...args){
letnow=newDate().getTime();
if(!options.leading){
if(timer)returntimer=setTimeout(()=>{timer=nullfunc.apply(options.context,args)},time)}
elseif(now-previous>time){
func.apply(options.context,args)previous=now}
elseif(options.trailing){
clearTimeout(timer)timer=setTimeout(()=>{
func.apply(options.context,args)},time)}};_throttle.cancel=()=>{
previous=0;clearTimeout(timer);timer=null};return_throttle};
添加尾随选项以指示是否在序列结束时触发附加事件。
17、图片的延迟加载
//getBoundingClientRectlazyLoadletimgList1=[...document.querySelectorAll(".get_bounding_rect")]letnum=imgList1.lengthletlazyLoad1=(function(){letcount=0returnfunction(){letdeleteIndexList=[]imgList1.forEach((img,index)=>{letrect=img.getBoundingClientRect()if(rect.top<window.innerHeight){img.src=img.dataset.src//AddtheimagetotheremovelistafterloadingsuccessfullydeleteIndexList.push(index)count++if(count===num){//UnbindtheScrolleventwhenallimagesareloadeddocument.removeEventListener('scroll',lazyLoad1)}}})//DeleteimagesthathavebeenloadedimgList1=imgList1.filter((_,index)=>!deleteIndexList.includes(index))}})()//Thethrottlingfunctionofthrottle.jsisreferencedherelazyLoad1=proxy(lazyLoad1,100)document.addEventListener('scroll',lazyLoad1)//Manuallyloadtheimageonce.Otherwise,theimageonthefirstscreencannotbeloadedwithouttriggeringscrollinglazyLoad1()//intersectionObserverlazyLoadletimgList2=[...document.querySelectorAll(".intersection_observer")]letlazyLoad2=function(){//instantiationobserverletobserver=newIntersectionObserver(entries=>{entries.forEach(entry=>{if(entry.intersectionRatio>0){entry.target.src=entry.target.dataset.srcobserver.unobserve(entry.target)}})})imgList2.forEach(img=>{observer.observe(img)})}lazyLoad2()
getBoundClientRect的实现监听滚动事件(建议为监听事件添加节流)。图片加载完成后,会从img标签组成的DOM列表中删除。
最后,加载监听器事件后,所有图像都需要解除绑定。
IntersectionObserver是通过实例化一个intersectionObserver并使其观察所有IMG标签来实现的。
当img标签进入查看区域时,实例化时执行回调。
同时,传入一个回调,保存实例来观察所有元素的某种状态,比如每个元素的边界,当前元素对应的DOM节点,当前元素进入查看区域的比例。
每当一个元素进入查看区域时,将真实图像分配给当前IMG标签,同时不观察它。
18、新关键字
constisComplexDataType=obj=>(typeofobj==='object'||typeofobj==='function')&&obj!==nullconstmyNew=function(fn,...rest){letinstance=Object.create(fn.prototype)letres=fn.call(instance,...rest)returnisComplexDataType(res)?res:instance}functionPerson(name,sex){this.name=namethis.sex=sex}letnewPerson=newPerson('tony','woman')letmyNewPerson=myNew(Person,'tony1','man')console.log(newPerson)console.log(myNewPerson)
19、实现对象分配
"usestrict"constisComplexDataType=obj=>(typeofobj==='object'||typeofobj==='function')&&obj!==nullconstmyAssign=function(target,...source){if(target==null)thrownewTypeError('Cannotconvertundefinedornulltoobject')returnsource.reduce((acc,cur)=>{isComplexDataType(acc)||(acc=newObject(acc));if(cur==null)returnacc;[...Object.keys(cur),...Object.getOwnPropertySymbols(cur)].forEach(key=>{acc[key]=cur[key]})returnacc},target)}Object.myAssign=myAssignlettarget={a:1,b:1}letobj1={a:2,b:2,c:undefined}letobj2={a:3,b:3,[Symbol("a")]:3,d:null}console.log(Object.myAssign(target,obj1,obj2))console.log(Object.myAssign("abd",null,undefined))
20、实例化
constmyInstanceof=function(left,right){letproto=Object.getPrototypeOf(left)while(true){if(proto==null)returnfalseif(proto===right.prototype){returntrue}proto=Object.getPrototypeOf(proto)}}console.log(myInstanceof({},Array))
填写下面表单即可预约申请免费试听! 怕学不会?助教全程陪读,随时解惑!担心就业?一地学习,可全国推荐就业!
Copyright © 京ICP备08000853号-56 京公网安备 11010802029508号 达内时代科技集团有限公司 版权所有
Tedu.cn All Rights Reserved