面试题手写系列
手写防抖
手写节流
手写科里化函数
简单版本的bind方法的实现
new方法的实现
实现instaceof方法
实现一个深拷贝方法
排平数组
饮料换购问题
封装判断两个对象相等的方法
手写防抖debounce
在事件被触发一定时间后再执行,如果在这段时间内再次触发,重新计时。如果一直触发,一直都不执行。
function debounce(fn,delay){
let timer
return function(){
let context = this
let args = arguments
//如果已存在计时器 清空 重新计时
timer && clearTimeout(timer)
timer = setTimeout(function(){
fn.apply(context,args)
},delay)
}
}
手写节流throttle
规定一段时间内,多次触发事件只能有一次触发执行。只有当此次触发执行完成后,下一次触发才能执行。会稀释执行次数。
function throttle(fn,delay){
let timer
return function(){
let context = this
let args = arguments
//计时器不存在的情况下 才会执行 执行完会将timer清空
if(!timer){
timer = setTimeout(function(){
timer = null
fn.apply(context,args)
},delay)
}
}
}
测试:
<div class="container" id="box"></div>
.container{
width:100px;
height:100px;
background-color: blue;
}
var box = document.getElementById("box")
box.addEventListener("click",debounce(function(){
console.log("点击事件发生")
},2000))
box.addEventListener("click",throttle(function(){
console.log("点击事件发生")
},2000))
手写科里化函数
function curry(fn,args){
let length = fn.length
args = args || []
return function(){
let finalArgs = args.concat(Array.from(arguments))
//如果参数个数小于fn所需参数个数 返回函数
if(finalArgs.length < length){
return curry.call(this,fn,finalArgs)
}else{
return fn.apply(this,finalArgs)
}
}
}
测试:
function multi(a,b,c){
return a+b+c
}
var fn = curry(multi,[1,2,3])
console.log(fn()) //6
var fn1 = curry(multi)
console.log(fn1(1)(2)(3)) //6
console.log(fn1(1,2,3)) //6
var fn2 = curry(multi,[1,2])
cosnole.log(fn2(3)) //6
console.log(fn2(4)) //7 参数复用
简单版本的bind方法的实现
/*bind方法的实现*/
Function.prototype.bindFn = function(thisArg){
if(typeof this !== "function"){
throw new TypeError(this+"must be a function")
}
//存储函数本身
var self = this
//存储除去thisArg之外的参数转成数组
var args = Array.prototype.slice.call(arguments,1)
var bound = function(){
var finalArgs = args.concat(Array.prototype.slice.call(arguments))
return self.apply(thisArg,finalArgs)
}
return bound
}
测试:
var obj = {
name:"Audrey"
}
function original(a,b){
console.log(this.name)
console.log([a,b])
}
var bound = original.bindFn(obj,1)
bound(2) //Audrey [1,2]
new方法的实现
/*new方法实现*/
function myNew(){
//获取构造函数
var constr = Array.prototype.shift.call(arguments)
//创建一个新对象 新对象的原型指向构造函数的原型
var obj = Object.create(constr.prototype)
//执行构造函数
var result = constr.apply(obj,arguments)
//判断构造函数的执行结果 如果是对象 返回 否则返回新对象
return result instanceof Object ? result : obj
}
测试:
function Person( name , age ){
this.name = name
this.age = age
this.sayHi = function(){
console.log(`hello,my name is ${this.name},I am ${this.age} years old`)
}
}
var p1 = myNew(Person,'ls',18)
p1.sayHi() // hello,my name is ls,I am 18 years old
实现instaceof方法
function myInstance(left,right){
while(true){
if(left.__proto__ === right.prototype) return true
if(left.__proto__ === null) return false
left = left.__proto__
}
}
测试:
function Person(name,age){
this.name = name
this.age = age
this.sayHi = function(){
console.log(`hello,my name is ${this.name}`)
}
}
var p1 = new Person("zs",28)
console.log(myInstance(p1,Person)) //true
console.log(myInstance(p1,Object)) //true
实现一个深拷贝方法
function deepClone(source){
//先判断要拷贝的是数组还是对象
var target = Array.isArray(source) ? []:{}
for(let key in source){
if(source[key] instanceof Object){
//递归调用
target[key] = deepClone(source[key])
}else{
target[key] = source[key]
}
}
return target
}
测试:
var testO = {
a:"111",
b:[1,2,"3"],
c:{
d:1,
e:2
}
}
var deepTestO = deepClone(testO)
deepTestO.b[2] = "666"
deepTestO.c.e=3
console.log(deepTestO)
console.log(testO)
排平数组
// 拍平数组
function flat(){
let flattenArray = []
return function faltten(array){
for(let i=0;i<array.length;i++){
Array.isArray(array[i])?faltten(array[i]):flattenArray.push(array[i])
}
return flattenArray
}
}
测试:
var a = [1,[2],[[3,4]]]
var a1 = flat()
console.log(a1(a)) // [1,2,3,4]
饮料换购问题
一瓶饮料n元钱,k个瓶盖可以换一瓶饮料,问m元钱可以喝到多少瓶饮料?
function getBottles( m, n, k){
//第一次可以买到的瓶数
let initBottles = Math.floor( m / n)
//总瓶数
let sumCount = initBottles
//第一次的剩余瓶盖数是0
let initRestCaps = 0
//每一轮 瓶子数 和 剩余瓶盖数
function cycle( bottles , restCaps ){
if( ( bottles + restCaps ) >= k ) {
let currentBottles = Math.floor((bottles + restCaps) / k)
let currentCaps = (bottles + restCaps) % k
sumCount += currentBottles
cycle( currentBottles,currentCaps )
}
}
cycle(initBottles,initRestCaps)
return sumCount
}
测试:
getBottles(10,3,2) // 5 10元 3元/瓶 2个瓶盖换一瓶
getBottles(20,1,2) //39 20元 1元/瓶 2个瓶盖换一瓶
封装判断两个对象相等的方法
function isDeepEqual(o1, o2) {
let o1keys = Object.keys(o1);
let o2keys = Object.keys(o2);
let flag = true; //默认二者是相等的
//如果二者的key数组长度不等 一定不相同
if (o1keys.length !== o2keys.length) return flag = false;
//如果二者的key数组长度相同
for (let key in o1) {
//判断o2是否有这个key
if (o2[key]) {
//如果是对象类型
if (o1[key] instanceof Object && o2[key] instanceof Object) {
flag = isDeepEqual(o1[key], o2[key]);
if (!flag) {
break;
}
} else {
if (o1[key] !== o2[key]) {
flag = false;
break;
}
}
} else {
//如果o2中没有这个key 一定不相同 结束循环
flag = false;
break;
}
}
return flag;
}
测试:
let a1 = {
a: "11",
b: {
c: "1",
},
};
let a2 = {
b: "22",
d: {
e: "1",
},
};
let c1 = { name: "audrey", age: 25, todo: [1, 2, 3] };
let c2 = { todo: [1, 2, 3], name: "audrey", age: 25 };
console.log(isDeepEqual(a1, a2)); //false
console.log(isDeepEqual(c1, c2)); //true