功夫不负有心人,只要基础打的牢,一切梦想都可以成真。面试高峰期已经度过,可是我还在被窝刷着面试题,虽然说是为了生活,但是我更多的还是在学习中寻找快乐。最近写了一下常见的面试手写题目,这里就列举5个常见的JavaScript手写功能,有的是在学习中自己写的,也算是学习的笔记吧,如果有不对的地方,欢迎评论区指正。
1、防抖和节流
1.1、防抖
函数防抖,指的是在触发函数之后,函数在指定时间之内只会执行一次,如果在规定时间之内再次触发函数,就会重新计算函数的执行时间。简单来说,就是如果多次触发函数,每次间隔的时间不超过规定时间,他只会执行最后一次。
代码实现
函数逻辑:设置一个定时器,当重复调用函数的时候,每一次都会清除定时器,然后重新定时,在规定时间内没有重复调用,就会执行剩下的逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function debounce(fnc,delay){ let timer = null; return function(){ let args = arguments; clearTimeout(timer); timer = setTimeout(()=>{ fnc.apply(this,args); },delay); } }
|
1.2、节流
节流就是限制函数在单位时间之内如果触发多次,而在只会执行一次,当过了这个时间段,在下一个时间段内,才会再次执行第二次,以此类推。
代码实现
代码:
函数逻辑:先开启一个定时器,定时器任务完成后清空,如果在规定时间之内重复触发函数,将不会做任何操作,直到定时器完成之后。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| function throttle(fnc,delay){ let timer = null; return function(){ let args = arguments; if(!timer){ timer = setTimeout(()=>{ fnc.apply(this,args); timer = null; },delay) } } }
|
2、深浅拷贝
2.1浅拷贝
只是复制指向某个对象的指针,没有复制对象本身,当原对象的值发生改变,浅拷贝的对象对应的值也会发生改变,他们都是在共用一块内存。
代码实现
函数逻辑:遍历对象,然后把属性和属性值都放在一个新的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13
| var shallowCopy = function(obj) { if (typeof obj !== 'object') return; var newObj = obj instanceof Array ? [] : {}; for (var key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = obj[key]; } } return newObj; }
|
2.2、深拷贝
创造一个一模一样的对象,两个对象不在同一块内存,修改原始的对象,并不会影响新的对象。
代码实现
函数逻辑:拷贝的时候判断一下属性值的类型,如果是对象,就递归调用深拷贝函数
1 2 3 4 5 6 7 8 9 10
| var deepCopy = function(obj) { if (typeof obj !== 'object') return; var newObj = obj instanceof Array ? [] : {}; for (var key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key]; } } return newObj; }
|
3、数组去重
在实际的开发当中,数组去重是我们经常要用到的一个方法,虽然不是很复杂,但是能将这些知识融会贯通还是非常难得的。这里简单写一下几种数组去重的方法,并不是最好的,只是简单记录一下。
方法1:双层 for 循环
函数逻辑:先定义一个包含原始数组第一个元素的数组,然后遍历原始数组,将原始数组中的每个元素与新数组中的每个元素进行比对,如果不重复则添加到新数组中,最后返回新数组。
1 2 3 4 5 6 7 8 9 10 11 12
| function distinct(arr) { for (let i=0, len=arr.length; i<len; i++) { for (let j=i+1; j<len; j++) { if (arr[i] == arr[j]) { arr.splice(j, 1); len--; j--; } } } return arr; }
|
方法2:Array.filter() 加 indexOf
函数逻辑:利用indexOf检测元素在数组中第一次出现的位置是否和元素现在的位置相等,如果不等则说明该元素是重复元素
1 2 3 4 5 6
| function distinct(a, b) { let arr = a.concat(b); return arr.filter((item, index)=> { return arr.indexOf(item) === index }) }
|
方法3:ES6 中的 Set 去重
函数逻辑:ES6 提供了新的数据结构 Set,Set 结构的一个特性就是成员值都是唯一的,没有重复的值。
1
| let unique = (a) => [...new Set(a)]
|
还有更多的方法,这里就不写了。(主要是我懒!)
4、Promise
Promise是一个管理异步编程的方案,他是一个构造函数,可以用new关键字创建实例。他有3种状态:pending、fulfilled、rejected,他这3种状态不会受外界影响。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| class MyPromise { constructor(executor) { this.status = 'pending' this.value = null this.reason = null this.onFulfilledCallbacks = [] this.onRejectedCallbacks = [] let resolve = value => { if (this.status === 'pending') { this.status = 'fulfilled' this.value = value; this.onFulfilledCallbacks.forEach(fn => fn()) } } let reject = reason => { if (this.status === 'pending') { this.status = 'rejected' this.reason = reason this.onRejectedCallbacks.forEach(fn => fn()) } }; try { executor(resolve, reject) } catch (err) { reject(err) } } then(onFulfilled, onRejected) { return new MyPromise((resolve, reject) => { if (this.status === 'fulfilled') { setTimeout(() => { const x = onFulfilled(this.value); x instanceof MyPromise ? x.then(resolve, reject) : resolve(x) }) } if (this.status === 'rejected') { setTimeout(() => { const x = onRejected(this.reason) x instanceof MyPromise ? x.then(resolve, reject) : resolve(x) }) } if (this.status === 'pending') { this.onFulfilledCallbacks.push(() => { setTimeout(() => { const x = onFulfilled(this.value) x instanceof MyPromise ? x.then(resolve, reject) : resolve(x) }) }) this.onRejectedCallbacks.push(() => { setTimeout(() => { const x = onRejected(this.reason) x instanceof MyPromise ? x.then(resolve, reject) : resolve(x) }) }) } }) } }
|
简单的promise就实现了,但这也只是最基础的,想要实现promise其他功能你可以看看这一篇文章。
可能是目前最易理解的手写promise - 掘金 (juejin.cn)
5、继承
ES5 继承(寄生组合继承)
1 2 3 4 5 6 7 8 9 10 11 12 13
| function Parent(name) { this.name = name } Parent.prototype.eat = function () { console.log(this.name + ' is eating') }
function Child(name, age) { Parent.call(this, name) this.age = age } Child.prototype = Object.create(Parent.prototype) Child.prototype.contructor = Child
|
ES6 继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class Parent { constructor(name) { this.name = name } eat() { console.log(this.name + ' is eating') } }
class Child extends Parent { constructor(name, age) { super(name) this.age = age } }
|
这些都是在面试当中经常遇到的面试题目,当然面试题目不是背出来的,而是去理解他的含义,当了解这一切之后,你就会发现其实比背下来更容易。更多的知识还在准备当中。