html商城网站源码分享,html的电商网站源代码

大家好,今天来为大家解答html商城网站源码分享这个问题的一些问题点,包括html的电商网站源代码也一样很多人还不知道,因此呢,今天就来为大家分析分析,现在让我们一起来看看吧!如果解决了您的问题,还望您关注下本站哦,谢谢~

前言

能够手撕各种JavaScript原生函数,可以说是进大厂必备!同时对JavaScript源码的学习和实现也能帮助我们快速扎实地提升自己的前端编程能力。

最近很多人和我一样在积极地准备前端面试笔试,所以就整理了一些前端面试笔试中非常容易被问到的原生函数实现和各种前端原理实现,其中部分源码戳这里。

实现一个new操作符

我们首先知道new做了什么:

创建一个空的简单JavaScript对象(即{});链接该对象(即设置该对象的构造函数)到另一个对象;将步骤(1)新创建的对象作为this的上下文;如果该函数没有返回对象,则返回this。

知道new做了什么,接下来我们就来实现它

functioncreate(Con,…args){\n//创建一个空的对象\nthis.obj={};\n//将空对象指向构造函数的原型链\nObject.setPrototypeOf(this.obj,Con.prototype);\n//obj绑定到构造函数上,便可以访问构造函数中的属性,即this.obj.Con(args)\nletresult=Con.apply(this.obj,args);\n//如果返回的result是一个对象则返回\n//new方法失效,否则返回obj\nreturnresultinstanceofObject?result:this.obj;\n}

实现一个Array.isArray

思路很简单,就是利用Object.prototype.toString

Array.myIsArray=function(o){\nreturnObject.prototype.toString.call(Object(o))===&39;;\n};

实现一个Object.create()方法

functioncreate=function(o){\nvarF=function(){};\nF.prototype=o;\nreturnnewF();\n};

实现一个EventEmitter

真实经历,最近在字节跳动的面试中就被面试官问到了,要求手写实现一个简单的Event类。

classEvent{\nconstructor(){\n//储存事件的数据结构\n//为查找迅速,使用对象(字典)\nthis._cache={}\n}\n\n//绑定\non(type,callback){\n//为了按类查找方便和节省空间\n//将同一类型事件放到一个数组中\n//这里的数组是队列,遵循先进先出\n//即新绑定的事件先触发\nletfns=(this._cache[type]=this._cache[type]||[])\nif(fns.indexOf(callback)===-1){\nfns.push(callback)\n}\nreturnthis\n}\n\n//解绑\noff(type,callback){\nletfns=this._cache[type]\nif(Array.isArray(fns)){\nif(callback){\nletindex=fns.indexOf(callback)\nif(index!==-1){\nfns.splice(index,1)\n}\n}else{\n//全部清空\nfns.length=0\n}\n}\nreturnthis\n}\n//触发emit\ntrigger(type,data){\nletfns=this._cache[type]\nif(Array.isArray(fns)){\nfns.forEach((fn)=>{\nfn(data)\n})\n}\nreturnthis\n}\n\n//一次性绑定\nonce(type,callback){\nletwrapFun=()=>{\ncallback.call(this);\nthis.off(type,wrapFun);//执行完以后立即解绑\n};\nthis.on(type,wrapFun);//绑定\nreturnthis;\n}\n}\n\nlete=newEvent()\n\ne.on(&39;,function(){\nconsole.log(&39;)\n})\n//e.trigger(&39;,&39;)\nconsole.log(e)\n

实现一个Array.prototype.reduce

先回忆一下Array.prototype.reduce语法:

Array.prototype.reduce(callback(accumulator,currentValue[,index[,array]])[,initialValue])

然后就可以动手实现了:

Array.prototype.myReduce=function(callback,initialValue){\nletaccumulator=initialValue?initialValue:this[0];\nfor(leti=initialValue?0:1;i<this.length;i++){\nlet_this=this;\naccumulator=callback(accumulator,this[i],i,_this);\n}\nreturnaccumulator;\n};\n\n//使用\nletarr=[1,2,3,4];\nletsum=arr.myReduce((acc,val)=>{\nacc+=val;\nreturnacc;\n},5);\n\nconsole.log(sum);//15

实现一个call或apply

先来看一个call实例,看看call到底做了什么:

letfoo={\nvalue:1\n};\nfunctionbar(){\nconsole.log(this.value);\n}\nbar.call(foo);//1

从代码的执行结果,我们可以看到,call首先改变了this的指向,使函数的this指向了foo,然后使bar函数执行了。总结一下:

call改变函数this指向调用函数

思考一下:我们如何实现上面的效果呢?代码改造如下:

Function.prototype.myCall=function(context){\ncontext=context||window;\n//将函数挂载到对象的fn属性上\ncontext.fn=this;\n//处理传入的参数\nconstargs=[…arguments].slice(1);\n//通过对象的属性调用该方法\nconstresult=context.fn(…args);\n//删除该属性\ndeletecontext.fn;\nreturnresult\n};

我们看一下上面的代码:

首先我们对参数context做了兼容处理,不传值,context默认值为window;然后我们将函数挂载到context上面,context.fn=this;处理参数,将传入myCall的参数截取,去除第一位,然后转为数组;调用context.fn,此时fn的this指向context;删除对象上的属性deletecontext.fn;将结果返回。

以此类推,我们顺便实现一下apply,唯一不同的是参数的处理,代码如下:

Function.prototype.myApply=function(context){\ncontext=context||window\ncontext.fn=this\nletresult\n//myApply的参数形式为(obj,[arg1,arg2,arg3]);\n//所以myApply的第二个参数为[arg1,arg2,arg3]\n//这里我们用扩展运算符来处理一下参数的传入方式\nif(arguments[1]){\nresult=context.fn(…arguments[1])\n}else{\nresult=context.fn()\n}\ndeletecontext.fn;\nreturnresult\n};

以上便是call和apply的模拟实现,唯一不同的是对参数的处理方式。

实现一个Function.prototype.bind

functionPerson(){\nthis.name=&34;;\nthis.age=18;\nthis.gender=&34;\n}\nletobj={\nhobby:&34;\n}\n//将构造函数的this绑定为obj\nletchangePerson=Person.bind(obj);\n//直接调用构造函数,函数会操作obj对象,给其添加三个属性;\nchangePerson();\n//1、输出obj\nconsole.log(obj);\n//用改变了this指向的构造函数,new一个实例出来\nletp=newchangePerson();\n//2、输出obj\nconsole.log(p);

仔细观察上面的代码,再看输出结果。

我们对Person类使用了bind将其this指向obj,得到了changeperson函数,此处如果我们直接调用changeperson会改变obj,若用new调用changeperson会得到实例p,并且其__proto__指向Person,我们发现bind失效了。

我们得到结论:用bind改变了this指向的函数,如果用new操作符来调用,bind将会失效。

这个对象就是这个构造函数的实例,那么只要在函数内部执行thisinstanceof构造函数来判断其结果是否为true,就能判断函数是否是通过new操作符来调用了,若结果为true则是用new操作符调用的,代码修正如下:

//bind实现\nFunction.prototype.mybind=function(){\n//1、保存函数\nlet_this=this;\n//2、保存目标对象\nletcontext=arguments[0]||window;\n//3、保存目标对象之外的参数,将其转化为数组;\nletrest=Array.prototype.slice.call(arguments,1);\n//4、返回一个待执行的函数\nreturnfunctionF(){\n//5、将二次传递的参数转化为数组;\nletrest2=Array.prototype.slice.call(arguments)\nif(thisinstanceofF){\n//6、若是用new操作符调用,则直接用new调用原函数,并用扩展运算符传递参数\nreturnnew_this(…rest2)\n}else{\n//7、用apply调用第一步保存的函数,并绑定this,传递合并的参数数组,即context._this(rest.concat(rest2))\n_this.apply(context,rest.concat(rest2));\n}\n}\n};

实现一个JS函数柯里化

Currying的概念其实并不复杂,用通俗易懂的话说:只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。

functionprogressCurrying(fn,args){\n\nlet_this=this\nletlen=fn.length;\nletargs=args||[];\n\nreturnfunction(){\nlet_args=Array.prototype.slice.call(arguments);\nArray.prototype.push.apply(args,_args);\n\n//如果参数个数小于最初的fn.length,则递归调用,继续收集参数\nif(_args.length<len){\nreturnprogressCurrying.call(_this,fn,_args);\n}\n\n//参数收集完毕,则执行fn\nreturnfn.apply(this,_args);\n}\n}\n

手写防抖(Debouncing)和节流(Throttling)

节流

防抖函数onscroll结束时触发一次,延迟执行

functiondebounce(func,wait){\nlettimeout;\nreturnfunction(){\nletcontext=this;//指向全局\nletargs=arguments;\nif(timeout){\nclearTimeout(timeout);\n}\ntimeout=setTimeout(()=>{\nfunc.apply(context,args);//context.func(args)\n},wait);\n};\n}\n//使用\nwindow.onscroll=debounce(function(){\nconsole.log(&39;);\n},1000);\n

节流

节流函数onscroll时,每隔一段时间触发一次,像水滴一样

functionthrottle(fn,delay){\nletprevTime=Date.now();\nreturnfunction(){\nletcurTime=Date.now();\nif(curTime-prevTime>delay){\nfn.apply(this,arguments);\nprevTime=curTime;\n}\n};\n}\n//使用\nvarthrotteScroll=throttle(function(){\nconsole.log(&39;);\n},1000);\nwindow.onscroll=throtteScroll;

手写一个JS深拷贝

乞丐版

JSON.parse(JSON.stringfy));

非常简单,但缺陷也很明显,比如拷贝其他引用类型、拷贝函数、循环引用等情况。

基础版

functionclone(target){\nif(typeoftarget===&39;){\nletcloneTarget={};\nfor(constkeyintarget){\ncloneTarget[key]=clone(target[key])\n}\nreturncloneTarget;\n}else{\nreturntarget\n}\n}

写到这里已经可以帮助你应付一些面试官考察你的递归解决问题的能力。但是显然,这个深拷贝函数还是有一些问题。

一个比较完整的深拷贝函数,需要同时考虑对象和数组,考虑循环引用:

functionclone(target,map=newWeakMap()){\nif(typeoftarget===&39;){\nletcloneTarget=Array.isArray(target)?[]:{};\nif(map.get(target)){\nreturntarget;\n}\nmap.set(target,cloneTarget);\nfor(constkeyintarget){\ncloneTarget[key]=clone(target[key],map)\n}\nreturncloneTarget;\n}else{\nreturntarget;\n}\n}

实现一个instanceOf

原理:L的proto是不是等于R.prototype,不等于再找L.__proto__.__proto__直到proto为null

//L表示左表达式,R表示右表达式\nfunctioninstance_of(L,R){\nvarO=R.prototype;\nL=L.__proto__;\nwhile(true){\nif(L===null){\nreturnfalse;\n}\n//这里重点:当O严格等于L时,返回true\nif(O===L){\nreturntrue;\n}\nL=L.__proto__;\n}\n}

实现一个原型链继承

functionmyExtend(C,P){\nvarF=function(){};\nF.prototype=P.prototype;\nC.prototype=newF();\nC.prototype.constructor=C;\nC.super=P.prototype;\n}

实现一个async/await

原理

就是利用generator(生成器)分割代码片段。然后我们使用一个函数让其自迭代,每一个yield用promise包裹起来。执行下一步的时机由promise来控制

实现

function_asyncToGenerator(fn){\nreturnfunction(){\nvarself=this,\nargs=arguments;\n//将返回值promise化\nreturnnewPromise(function(resolve,reject){\n//获取迭代器实例\nvargen=fn.apply(self,args);\n//执行下一步\nfunction_next(value){\nasyncGeneratorStep(gen,resolve,reject,_next,_throw,&39;,value);\n}\n//抛出异常\nfunction_throw(err){\nasyncGeneratorStep(gen,resolve,reject,_next,_throw,&39;,err);\n}\n//第一次触发\n_next(undefined);\n});\n};\n}

实现一个Array.prototype.flat()函数

最近字节跳动的前端面试中也被面试官问到,要求手写实现。

Array.prototype.myFlat=function(num=1){\nif(Array.isArray(this)){\nletarr=[];\nif(!Number(num)||Number(num)<0){\nreturnthis;\n}\nthis.forEach(item=>{\nif(Array.isArray(item)){\nletcount=num\narr=arr.concat(item.myFlat(–count))\n}else{\narr.push(item)\n}\n});\nreturnarr;\n}else{\nthrowtihs+&34;;\n}\n};

实现一个事件代理

这个问题一般还会让你讲一讲事件冒泡和事件捕获机制

<ulid=&34;>\n<li>red</li>\n<li>yellow</li>\n<li>blue</li>\n<li>green</li>\n<li>black</li>\n<li>white</li>\n</ul>\n<script>\n(function(){\nvarcolor_list=document.getElementById(&39;);\ncolor_list.addEventListener(&39;,showColor,true);\nfunctionshowColor(e){\nvarx=e.target;\nif(x.nodeName.toLowerCase()===&39;){\nalert(x.innerHTML);\n}\n}\n})();\n</script>

实现一个Vue的双向绑定

Vue2.x的Object.defineProperty版本

//数据\nconstdata={\ntext:&39;\n};\nconstinput=document.getElementById(&39;);\nconstspan=document.getElementById(&39;);\n//数据劫持\nObject.defineProperty(data,&39;,{\n//数据变化—>修改视图\nset(newVal){\ninput.value=newVal;\nspan.innerHTML=newVal;\n}\n});\n//视图更改–>数据变化\ninput.addEventListener(&39;,function(e){\ndata.text=e.target.value;\n});

Vue3.x的proxy版本

//数据\nconstdata={\ntext:&39;\n};\nconstinput=document.getElementById(&39;);\nconstspan=document.getElementById(&39;);\n//数据劫持\nconsthandler={\nset(target,key,value){\ntarget[key]=value;\n//数据变化—>修改视图\ninput.value=value;\nspan.innerHTML=value;\nreturnvalue;\n}\n};\nconstproxy=newProxy(data,handler);\n\n//视图更改–>数据变化\ninput.addEventListener(&39;,function(e){\nproxy.text=e.target.value;\n});\n

思考:Vue双向绑定的实现,使用ES6的Proxy相比Object.defineProperty有什么优势?

实现一个Array.prototype.map()

先看看reduce和map的使用方法

letnew_array=arr.map(functioncallback(currentValue[,index[,array){}[,thisArg])\n\nletresult=arr.reduce(callback(accumulator,currentValue[,index[,array]])[,initialValue])

最常见的方式我们可以用一个for循环来实现:

Array.prototype.myMap=function(callback,thisArg){\nletarr=[];\nfor(leti=0;i<this.length;i++){\narr.push(callback.call(thisArg,this[i],i,this));\n}\nreturnarr;\n};

同样的我们也可以用数组的reduce方法实现

Array.prototype.myMap2=function(callback,thisArg){\nletresult=this.reduce((accumulator,currentValue,index,array)=>{\naccumulator.push(callback.call(thisArg,currentValue,index,array));\nreturnaccumulator;\n},[]);\nreturnresult;\n};

结语

看完如果觉得对你有帮助,劳烦点个赞哈,你的鼓励就是我更新最大的动力!

学习使我快乐!

关于html商城网站源码分享,html的电商网站源代码的介绍到此结束,希望对大家有所帮助。

Published by

风君子

独自遨游何稽首 揭天掀地慰生平