摘要:最近准备初级前端面试,发现有很多手写实现什么的,例如什么手写实现,。后面以这道题为引线面试官可能会追问什么是执行上下文的判断,的区别手写一个函数实现斐波那契数列首先拷一个阮神在他教程里的一个写法。
最近准备初级前端面试,发现有很多手写实现什么的,例如什么手写实现bind,promise。手写ajax,手写一些算法。
翻阅了很多书籍和博客。
这里做一个总结改进,算是对我后面大概为期一个月找工作的准备。
手写实现bind()Function.prototype._bind = function (context) { var self = this; var args = Array.prototype.slice.call(arguments, 1); var fBind = function () { var bindArgs = Array.prototype.slice.call(arguments); return self.apply(this instanceof fBind ? this : context, args.concat(bindArgs)); } fBind.prototype = self.prototype&&Object.create(self.prototype) return fBind; }
简单的说明:
这里之所以传参的时候需要两个数组,是因为考虑到用new以构造函数的形式调用硬绑定函数的情况:this这时是应该指向实例对象的。
这样子就需要继承之前函数的方法, fBind.prototype = self.prototype&&Object.create(self.prototype)
,同时也可以用 Object.setPrototypeOf(fBind.prototype,self.prototype)。
考虑到存在undefined的情况,前面加一个判断self.prototype&&.....
关于apply的第一个参数,如果考虑到之前的情况,是不能传入context的,这需要做一个判断。
像是下面的情况
function Foo(price){ this.price =price this.fn = ()=>{ console.log("hi fn") } console.log(this.name) } Foo.prototype.sayMyName = function(){ console.log(this.name) } var Obj1 = { name:"obj1" } var b =Foo._bind(Obj1) b() //"obj1" var c = new b(1000)//"i am c" c.name = "i am c" c.sayMyName()
这里的this的指向就是c,它指向实例对象本身。
后面以这道题为引线面试官可能会追问:
什么是执行上下文
this的判断
call,bind的区别
手写一个函数实现斐波那契数列首先拷一个阮神在他es6教程里的一个写法。
function* fibonacci() { let [prev, curr] = [0, 1]; for (;;) { yield curr; [prev, curr] = [curr, prev + curr]; } } for (let n of fibonacci()) { if (n > 1000) break; console.log(n); }
更精简的
const feibo= max=>{ let [a,b,i]= [0,1,1] while(i++<=max) { [a,b] = [b,a + b ] console.log(b) } return a }
相对是非常简单的,感觉也不会多问啥,就不多说了。
手写一个简单的ajaxlet xhr = new XMLHttpRequest() xhr.open("get", url, true) xhr.onreadystatechange = function(){ if(xhr.readyState === 4){ console.log("请求完成") if(this.status >= 200 &&this.status<300){ conso.log("成功") }else{ consol.log("失败") } } } xhr.onerror = function(e) { console.log("连接失败") } xhr.send()
大概是这么个意思就差不多了,顺势可能会问一些状态码和状态值的问题,或者直接问到关于http上面的问题。
原型继承function inherit(supertype,subtype){ Object.setPrototypeOf(subtype.prototype,supertype.prototype) subtype.prototype.constructor = subtype } function Car(name,power){ this.name=name this.power = power } Car.prototype.showPower = function(){ console.log(this.power) } function Changan(price,name,power){ this.price = price Car.call(this,name,power) } inherit(Car,Changan) Changan.prototype.showName = function(){ console.log(this.name) } var star = new Changan(100000,"star",500) star.showPower()防抖与节流
function debounce(fn,duration){ var timer window.clearTimeout(timer) timer = setTimeout(()=>{ fn.call(this) },duration) } function throttle(fn,duration){ let canIRun if(!canIRun)return fn.call(this) canIRun = false setTimeout(()=>{ canIRun = true },duration) }数组去重
我一般就用这两种,大部分情况都能应付了。
[...new Set(array)]
//hash function unique(array) { const object = {} array.map(number=>{ object[number] = true }) return Object.keys(object).map(//.......) }//大概是这样的意思,写法根据数组的不同可能会有所改变深拷贝
应该是面试里面手写xxx出现频率最高的题了,无论是笔试还是面试。
总是让你手写实现深拷贝函数。
事实上,js并不能做到真正完全的标准的深拷贝
所以不管你写什么样的深拷贝函数都会有不适用的地方,这取决于使用的场景和拷贝的对象,如果面试官在这上面钻研比较深的话,是很难做到完美的。
既然这样就写个将就一点的深拷贝吧,面向面试的那种。
function deepClone(item) { return result; }
首先在类型判断上做一个选择,一般情况来说,用new创建的实例对象用typeof判断会出问题的,相比之下instanceof也不靠谱。这里面相对比较靠谱的Object.prototype.toString.call(item)。(这个其实也不兼容到全部情况和性能要求,但是面向面试代码可读性会高一点)。
type = Object.prototype.toString.call(item).slice(8,this.length-1), //[object String],[object Array],[object Object]
函数的拷贝,这里不能使用bind,会改变this指向或影响后续使用call调用该拷贝的函数,大部分情况是不必要的,这里就直接赋值吧。
于是这里可以把基本数据类型和Function放一起。
fk= ["String","Number","Boolean","Function"].indexOf(type)>-1
dom对象的拷贝: result = item.cloneNode(true);
忽略正则
Date,[object Object], [object Array]放到后面的判断
let other = { //需要递归或者其他的操作 Array() { result = [] item.forEach((child, index)=>{ hash.set(item, result); result[index] = deepClone(child,hash) }) }, Date(){ result = new Date(item) }, Object(){ result = {} Reflect.ownKeys(item).forEach(child=>{ hash.set(item, result); result[child] = deepClone(item[child],hash) }) } } other[type]()
这样子是不是相对清晰一些了,应付一般的情况应该差不多了,但是没考虑循环引用。
这里给个思路是使用ES6的WeakMap,不知道的兄弟可以看看阮神的ES6博客,为防止爆栈,我把循环引用直接扔给它,完美拷贝。
就相当于
var wm = new WeakMap() var obj = { name:null } obj.name = obj wm.set(obj,wm.get(obj)) console.log(wm)
现在就需要在开头检查一下循环引用,然后直接返回WeakMap对象键名为item参数对象的值
所以最终代码就是
function deepClone(item,hash = new WeakMap()) { if (!item) return item if (hash.has(item))return hash.get(item); //检查循环引用 var result, type = Object.prototype.toString.call(item).slice(8,this.length-1), fk= ["String","Number","Boolean","Function"].indexOf(type)>-1 if(fk){ result = item;//直接赋值 }else if(item.nodeType && typeof item.cloneNode === "function"){ result = item.cloneNode(true); //是否是dom对象 }else{ let other = { //需要递归或者其他的操作 Array() { result = [] item.forEach((child, index)=>{ hash.set(item, result); result[index] = deepClone(child,hash) }) }, Date(){ result = new Date(item) }, Object(){ result = {} Reflect.ownKeys(item).forEach(child=>{ hash.set(item, result); result[child] = deepClone(item[child],hash) }) } } other[type]() } return result; }
意思就大概是这个意思,当然深拷贝的方法有很多,甚至不一定用到递归。面试官总会有找茬的地方的。
我觉得我写的这个还是满足我现在找工作的级别要求的。
然后是我用来测试的对象
var obj1 = { name:"obj1", one : { a:new Date(), b:new String("1-2"), c:new Array(["this","is",1,{a:23}]), d: function () { if(true){ return "d" } }, e:new Number(15), f:new Boolean(true) }, two(x){ console.log(x+" "+this.name) }, three : [ { a:"this is a", b:document.body, c:{ a:[1,2,3,4,5,[13,[3],true],10], b:{ a:{ a:[1,2,3] }, b:2 } } }, ], four:[1,2] } obj1.name=obj1 obj1.four[3] = obj1 var copy = deepClone(obj1) console.log(copy) copy.two.call(window,"hi")
## new
简单说下大概是这么一个过程
创建一个空对象
执行传入的构造函数,执行过程中对 this 操作就是对 这个空对象 进行操作。
返回这个空对象
模拟需要考虑的问题
是一个空对象,这里面的写法obj原型链是没有上一级的,即不存在与其他任何对象之间的联系,虽然在这里面没多少区别:var obj = Object.create(null),
this指向这个空对象:let rt = Constructor.apply(obj, arguments);
可以访问构造函数的原型链, Object.setPrototypeOf(obj, Constructor.prototype);
如果构造函数有返回值,并且是一个对象,那么实例对象拿到的就是这个对象(应该只是值,并不是引用)。所以这里要做个判断return typeof rt === "object" ? rt : obj;
最终的代码
function _new(){ var obj = Object.create(null), Constructor = [].shift.call(arguments); Object.setPrototypeOf(obj, Constructor.prototype); let rt = Constructor.apply(obj, arguments); return rt instanceof Object ? rt : obj; }
快排
:代码精简了一点
function quickSort(arr){ if(arr.length<=1)return arr var index = Math.floor(arr.length/2), number = arr.splice(index,1)[0], left = [], right = []; arr.forEach(item=>{ item<=number?left.push(item):right.push(item) }) return quickSort(left).concat([number],quickSort(right)) }
这期间会不断更新并修改,这里面的手写实现您如果有更好的写法或者新的思路,也希望可以说明交流。最后谢谢大佬些的观看。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/103597.html
摘要:第一天写文章,心里难免有些小激动,希望能坚持下去,有输出才有更好的输入。用户选择完之后,我会得到一个时间戳的数组这里呢我们先需要看一下的语法。 第一天写文章,心里难免有些小激动,希望能坚持下去,有输出才有更好的输入。 reduce这个方法最初我是在面试题里看见的有一个长度为100的数组,请以优雅的方式求出该数组的前10个元素之和?答案如下 var a = [1, 2, 3, 4, 5...
摘要:先说下我面试情况,我一共面试了家公司。篇在我面试的众多公司里,只有同城的面问到相关问题,其他公司压根没问。我自己回答的是自己开发组件面临的问题。完全不用担心对方到时候打电话核对的问题。 2019的5月9号,离发工资还有1天的时候,我的领导亲切把我叫到办公室跟我说:阿郭,我们公司要倒闭了,钱是没有的啦,为了不耽误你,你赶紧出去找工作吧。听到这话,我虎躯一震,这已经是第2个月没工资了。 公...
摘要:那既然频繁出,肯定不能是手撕红黑树我觉得面试官也多半撕不出来,不撕红黑树,那这道题还有点救,慢慢往下看。简单说来说,哈希表由两个要素构成桶数组和散列函数。所谓的哈希冲突,就是不同的经过哈希函数计算,落到了同一个下标。快手面试官真的吗我不信。手写HashMap?这么狠,面试都卷到这种程度了?第一次见到这个面试题,是在某个不方便透露姓名的Offer收割机大佬的文章:这……我当时就麻了,我们都知道...
摘要:前言得益于金三银四,在最近一段时间,面试了一些人,但是符合的寥寥无几。看到我的面试题自己写的面试题,自己想的答案。听人说过一个面试套路面试官问的问题,可能面试官自己都不懂,目的只是为了压工资,挫士气。不过我是为了测试面试者是不是真的精通。 技术在不断的创新,随着框架,库,构建工具,打包工具,版本控制工具等操作越来越方便,使用越来越简单。面对这样的情况,除了兴奋,也要警惕。这些工具使得开...
阅读 3016·2023-04-26 00:32
阅读 498·2019-08-30 15:52
阅读 2104·2019-08-30 15:52
阅读 3346·2019-08-30 15:44
阅读 3279·2019-08-30 14:09
阅读 1415·2019-08-29 15:15
阅读 3389·2019-08-28 18:12
阅读 1073·2019-08-26 13:55