常见的手撕代码题

1,节流防抖

节流防抖题

防抖专题

代码实现:

function throttle(func,wait){
 var previous = 0
 return function(){
 var now = +new Date()
 if(now - previous > wait){
 func.apply(this,arguments)
 previous = now;
 }
 }
}
// 节流函数常用在高频点击提交,表单重复提交

function debounce(func,wait){
 var timer
 return function(){
 if(timer) clearTimeout(timer)
 timer = setTimeout(function(){
 func.apply(this,arguments)
 },wait)
 }
}
// 防抖函数常用在:1,input框search减少资源请求次数(输入后200秒内没有操作才会触发请求接口)2,window的resize事件,防止重复渲染。

// 区别
// 防抖是将多次执行变为只执行一次,类似于王者荣耀的回城特效,只认最后一次。动作发生后,在规定时间内重新触发,则重新计算时间。节流是将多次执行变为每隔一段时间执行,动作发生后,在规定时间重新触发会被无视。

2,Set, Map、weakset、weakMap

Set, Map、weakset、weakMap

使用场景:

3,数组扁平化

方法一:

arr.join(',').split(',').sort((a,b) => a - b).map(Number)

方法二:

a = arr.flat(4)

b = Array.from(new Set(a))

c = b.sort((a, b)=>a-b)

方法三:

const deepFlatten = arr => [].concat(...arr.map(v => (Array.isArray(v) ? deepFlatten(v) : v)));

deepFlatten(arr).sort((a, b)=>a-b)

方法四:

[...new Set(arr.toString().split(','))].sort((a,b)=>a-b)

4,实现一个new

先理清楚 new 关键字调用函数的具体过程,那么写出来就很清楚了

  1. 首先创建一个空的对象,空对象的proto属性指向构造函数的原型对象

  2. 把上面创建的空对象赋值构造函数内部的this,用构造函数内部的方法修改空对象

  3. 如果构造函数返回一个非基本类型的值,则返回这个值,否则返回上面创建的对象

function _new(fn, ...arg) {
 var obj = Object.create(fn.prototype);
 const result = fn.apply(obj, ...arg);
 return Object.prototype.toString.call(result) == '[object Object]' ? result : obj;
}

apply的含义:

function Product(name, price) {
 this.name = name;
 this.price = price;
}

function Food(name, price) {
 Product.call(this, name, price);
 this.category = 'food';
}

console.log(new Food('cheese', 5).name);

5,单个或者多个数组合并去重

function combine(){
 let arr = [].concat.apply([],arguments);
 return Array.from(new Set(arr))
}
//合并两个数组
var a = [1,2,3];
var b = [4,5,6];
var newA = [...a,...b]

6,ES5组合继承

function SuperType(name){
 this.name = name
 this.gender = 'male'
}
SuperType.prototype.getName = function(){
 console.log(this.name)
}
function SubType(name,age){
 SuperType.call(this,name)
 this.age = age
}
SubType.prototype = new SuperType()
SubType.constructor = SubType
SubType.prototype.getAge = function(){
 console.log(this.age)
}
subtype = new SubType('小明', 21)

原理:
1,SuperType.call(this)使得子类继承父类属性,SuperType.call(this,name)传name参数使得子类以传值的继承并改变父类属性值
2,SubType.prototype = new SuperType()使得子类继承父类方法
缺点:
1,调用了两次父类构造函数
2,子类的原型上多了不需要的属性,造成内存资源的浪费

7,寄生组合继承

function inheritPrototype(subType,superType){
 let prototype = Object.create(superType.prototype);//创建父类的副本
 prototype.constructor = subType;//为创建的副本定义constructor
 subType.prototype = prototype;//将父类的副本赋值给子类
}
function SuperType(name){
 this.name = name
}
SuperType.prototype.getName = function(){
 console.log(this.name)
}
function SubType(name,age){
 SuperType.call(this,name);
 this.age = age
}
inheritPrototype(SubType, SuperType)
SubType.prototype.getAge = function(){
 console.log(this.age)
}

上面的代码等同于下面:

function SuperType(name){
 this.name = name
}
SuperType.prototype.getName = function(){
 console.log(this.name)
}
function SubType(name,age){
 SuperType.call(this,name);
 this.age = age
}
SubType.prototype = Object.create(SuperType.prototype);
SubType.constructor = SubType
SubType.prototype.getAge = function(){
 console.log(this.age)
}

优缺点:
1,避免了两次调用父类构造函数
2,解决了组合继承造成的子类的原型上多了不需要的属性的问题,节省内存资源

8,浅拷贝和深拷贝

  • 浅拷贝
    方法一:
  // ES6展开运算符
    // 浅拷贝数组
    let a = [1,2,3]
    let b = [...a]
    b.push(4)
    a // [1,2,3]
    b // [1,2,3,4]
    // b与a互相不影响
    
    // 浅拷贝对象
    let aaa = {a:1,b:2}
    let bbb = {...aaa}
    bbb.a = 3
    bbb // {a: 3, b: 2}
    aaa // {a: 1, b: 2}
    // aaa与bbb互不影响
    方法二:
    // Object.assign
    // 浅拷贝数组
    let a = [1,2,3]
    let b = Object.assign([],a)
    b.push(4)
    a // [1,2,3]
    b // [1,2,3,4]
    // b与a互相不影响
    
    // 浅拷贝对象
    let aaa = {a:1,b:2}
    let bbb = Object.assign({},aaa)
    bbb.a = 3
    bbb // {a: 3, b: 2}
    aaa // {a: 1, b: 2}
    // aaa与bbb互不影响
*   **深拷贝**
    方法一:
     // JSON.stringfy
       let obj1 = {
        a:1,
        b:{
        b1:1
        }
       }
       let obj2 = JSON.parse(JSON.stringfy(obj1))
       obj2.b.b1 = 2;
       obj2 // {a:1,b:{b1:2}}
       obj1 // {a:1,b:{b1:1}}
       // obj2改变不会影响obj1
       方法二:
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;
       }

9,Async/Await 如何通过同步的方式实现异步

    基本概念:async/await是使用generator+run函数(自动执行generator)

    1,Async/Await是参照Generator封装的一套异步处理方案,本质是generator的语法糖

    2,generator依赖于迭代器Iterator

    3,Iterator思想来源于单向链表

    **所以源头来源于单向链表**

    *   单向链表实现

    *   Iterator迭代器简单实现
const makeIterator = arr =>{
         let nextIndex = 0
         return {
         next: ()=>{
         nextIndex

10,ES6继承

class SuperType {
        constructor(name,gender){
        this.name = name
        this.gender = gender
        }
        getName(){
        console.log(this.name)
        }
       }
       class SubType extend SuperType {
        constructor(name,age){
        super(this);
        this.age = age
        }
        getAge(){
        console.log(this.age)
        }
       }

10,Call,Apply,Bind实现

        // call
        Function.prototype.myCall = function (context) {
         context = context ? Object(context) : window 
         context.fn = this;
        
         let args = [...arguments].slice(1);
         const result = context.fn(...args);
         delete context.fn;
         return result;
        }
        
        // apply
        Function.prototype.myApply = function (context) {
         context = context ? Object(context) : window;
         context.fn = this;
        
         let args = [...arguments][1];
         let result;
         if (args.length === 0) {
         result = context.fn();
         } else {
         result = context.fn(args);
         }
         delete context.fn;
         return result;
        }
        
        // bind
        Function.prototype.myBind = function (context) {
         let self = this;
         let args = [...arguments].slice(1); 
         return function() {
         let newArgs = [...arguments];
         return self.apply(context, args.concat(newArgs));
         }
        }

11,基本排序

  • 使用原生sort函数
function originSort(arr){
    return arr.sort((a,b)=>a-b)
}
  • 冒泡排序
function bubbleSort(arr){
         if (arr.length<=1) return arr
         for(let i = 0;i
  • 选择排序
function chooseSort(arr){
         let oldArr = arr 
         let newArr = []
         function findMinNumber(param){
         return Math.min.apply(null,...arguments)
         }

         while(oldArr.length){
         let minValue = findMinNumber(oldArr)
         let minValueIndex = arr.indexOf(minValue)
         oldArr.splice(minValueIndex,1)
         newArr.push(minValue)
         }
         return newArr
        }

9,深拷贝

function deepClone(obj){
        var newObj= obj instanceof Array ? []:{};
        for(var item in obj){
        var temple= typeof obj[item] == 'object' ? deepClone(obj[item]):obj[item];
        newObj[item] = temple;
        }
        return newObj;
        }

10,阶乘

n! = n*(n-1)!;

        function mul (n){
         if(n == 1){
         return 1;
         }
         return n*mul(n-1);
        }

11,斐波那契数列

function fb(n){
         if( n == 1 ||n ==2 ){
         return 1;
         }
         return fb(n-1) + fb(n-2);
        }
### 12,函数柯里化

var add = (a, b, c) => a + b + c
function curry(fn, ...args) {
const length = fn.length
let lists = args || []
let listLen
return function (..._args) {
lists = [...lists, ..._args]
listLen = lists.length
if (listLen < length) {
const that = lists
lists = []
return curry(fn, ...that)
} else if (listLen === length) {
const that = lists
lists = []
return fn.apply(this, that)
}
}
}

### 13,代码组合

var toUpperCase = (str) => str.toUpperCase()
var reverse = (arr) => arr.reverse()
var head = (arr) => arr[0]
var reverseHeadUpperCase = (arr) => toUpperCase(head(reverse(arr)))
var compose = (...args) => (initValue) => args.reduceRight((a, c) => c(a), initValue)

### 14,发布订阅

class EventEmiter {
constructor() {
this.events = {}
}
emit(event, ...args) {
this.events[event].forEach(fn => {
fn.apply(this, args)
})
}
on(event, fn) {
if (this.events[event]) {
this.events[event].push(fn)
} else {
this.events[event] = [fn]
}
}
remove(event) {
delete this.events[event]
}
}

    const eventHub = new EventEmiter()
    
    eventHub.on('test', data => {
     console.log(data)
    })
    
    eventHub.emit('test', 1)
    console.log(2)
### 15,手写promise

//初始版本的promise
function myPromise(constructor){
let self=this;
self.status="pending" //定义状态改变前的初始状态
self.value=undefined;//定义状态为resolved的时候的状态
self.reason=undefined;//定义状态为rejected的时候的状态
function resolve(value){
//两个==="pending",保证了状态的改变是不可逆的
if(self.status==="pending"){
self.value=value;
self.status="resolved";
}
}
function reject(reason){
//两个==="pending",保证了状态的改变是不可逆的
if(self.status==="pending"){
self.reason=reason;
self.status="rejected";
}
}
//捕获构造异常
try{
constructor(resolve,reject);
}catch(e){
reject(e);
}
}
myPromise.prototype.then=function(onFullfilled,onRejected){
let self=this;
switch(self.status){
case "resolved":
onFullfilled(self.value);
break;
case "rejected":
onRejected(self.reason);
break;
default:
}
}

    // 异步调用的promise
    function myPromise(constructor){
     let self=this;
     self.status="pending" //定义状态改变前的初始状态
     self.value=undefined;//定义状态为resolved的时候的状态
     self.reason=undefined;//定义状态为rejected的时候的状态
     self.onFullfilledArray=[];
     self.onRejectedArray=[];
     function resolve(value){
     if(self.status==="pending"){
     self.value=value;
     self.status="resolved";
     self.onFullfilledArray.forEach(function(f){
     f(self.value);
     //如果状态从pending变为resolved,
     //那么就遍历执行里面的异步方法
     });

     }
     }
     function reject(reason){
     if(self.status==="pending"){
     self.reason=reason;
     self.status="rejected";
     self.onRejectedArray.forEach(function(f){
     f(self.reason);
     //如果状态从pending变为rejected, 
     //那么就遍历执行里面的异步方法
     })
     }
     }
     //捕获构造异常
     try{
     constructor(resolve,reject);
     }catch(e){
     reject(e);
     }
    }
    // then 方法
    myPromise.prototype.then=function(onFullfilled,onRejected){
     let self=this;
     switch(self.status){
     case "pending":
     self.onFullfilledArray.push(function(){
     onFullfilled(self.value)
     });
     self.onRejectedArray.push(function(){
     onRejected(self.reason)
     });
     case "resolved":
     onFullfilled(self.value);
     break;
     case "rejected":
     onRejected(self.reason);
     break;
     default: 
     }
    }
    // 链式调用
    myPromise.prototype.then=function(onFullfilled,onRejected){
     let self=this;
     let promise2;
     switch(self.status){
     case "pending":
     promise2=new myPromise(function(resolve,reject){
     self.onFullfilledArray.push(function(){
     try{
     let temple=onFullfilled(self.value);
     resolve(temple)
     }catch(e){
     reject(e) //error catch
     }
     });
     self.onRejectedArray.push(function(){
     try{
     let temple=onRejected(self.reason);
     reject(temple)
     }catch(e){
     reject(e)// error catch
     }
     });
     })
     case "resolved":
     promise2=new myPromise(function(resolve,reject){
     try{
     let temple=onFullfilled(self.value);
     //将上次一then里面的方法传递进下一个Promise的状态
     resolve(temple);
     }catch(e){
     reject(e);//error catch
     }
     })
     break;
     case "rejected":
     promise2=new myPromise(function(resolve,reject){
     try{
     let temple=onRejected(self.reason);
     //将then里面的方法传递到下一个Promise的状态里
     resolve(temple); 
     }catch(e){
     reject(e);
     }
     })
     break;
     default: 
     }
     return promise2;
    }
### 16,回文字符串

function countSubstrings(s) {
const n = s.length;
let ans = 0;
for (let i = 0; i < 2 * n - 1; ++i) {
let l = i / 2, r = i / 2 + i % 2;
while (l >= 0 && r < n && s.charAt(l) == s.charAt(r)) {
--l;
++r;
++ans;
}
}
return ans;
};

### 17,给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标。

nums = [2,7,11,15];
var result = {};
for (var i = 0; i < nums.length; i++) {
result[nums[i]] = i;
}
console.log(result)
var twoSum = function(nums, target) {
let result = {};
let length = nums.length;
for (var i = 0; i < length; i++) {
let difference = target - nums[i];
if (difference in result) {
return [result[difference], i]
}
result[nums[i]] = i;
}
};

你可能感兴趣的:(常见的手撕代码题)