面试高频的手写JS代码

975 阅读5分钟

b840c72e59dd5b880ed68bb8d10b6c62.jpeg 如果你有梦想,一定要来大城市,这里可以帮您实现你想要的

有些梦想,需要借助城市的力量才能实现

文章会持续更新

1.手写防抖(debounce)和 节流(throttle)

function debounce(f, wait) {
    let timer;
    return (...args) => {
        clearTimeout(timer);
        timer = setTimeout(() => {
            f(...args);
        }, wait);
    }
}

function throttle(f, wait) {
    let timer;
    return (...args) => {
        if (timer) { return };
        timer = setTimeout(() => {
            fn(...args);
            timer = null;
        }, wait);
    }
}

2.手动实现一下instanceof的功能

核心: 原型链的向上查找。

function myInstanceOf(left, right) {
   // 基本数据类型直接返回false
   if (typeof left !== 'object' || left === null) return false;
   // getPrototypeOf是Object对象自带的一个方法,能够拿到参数的原型对象
   let pro = Object.getPrototypeOf(left);
   while(true) {
       //查找到尽头,还没找到
       if (pro === null) return false;
       //找到相同的原型对象
       if (pro === right.prototype) return true;
       pro = Object.getPrototypeOf(pro);
   }
}

3. Object.is和===的区别?

Object在严格等于的基础上修复了一些特殊情况下的失误,具体来说就是+0和-0,NaN和NaN。 源码如下:

function is(x, y) {
   if (x === y) {
       //运行到1/x === 1/y的时候x和y都为0,但是1/+0 = +Infinity, 1/-0 = -Infinity, 是不一样的
       return x !== 0 || y !== 0 || 1/x === 1/y; 
   } else {
       //NaN===NaN是false,这是不对的,我们在这里做一个拦截,x !== x,那么一定是 NaN, y 同理
       //两个都是NaN的时候返回true
       return x !== x && y !== y
   }
}

4.最优的组合继承----寄生组合继承

function Parent() {
   this.name = 'parent';
   this.play = [1,2,3];
}
function Child() {
   Parent.call(this);
   this.type = 'child';
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;

5.模拟实现map函数

Array.prototype.map = function(callbackFn, thisArg) {
   // 处理数组类型异常
   if (this === null || this === undefined) {
       throw new TypeError("Cannot read property 'map' of null or undefined");
   }
   // 处理回调类型异常
   if (Object.prototype.toString.call(callbackFn) != '[object Function]') {
       throw new TypeError(callbackfn + ' is not a function');
   }
   // 草案中提到要先转换为对象
   let O = Object(this);
   let T = thisArg;

   let len = O.length >>> 0;
   let A = new Array(len);
   for(let k=0; k < len; k++) {
       // 还记得原型链那一节提到的 in 吗?in 表示在原型链查找
       // 如果用 hasOwnProperty 是有问题的,它只能找私有属性
       // 需要注意的就是使用 in 来进行原型链查找。同时,如果没有找到就不处理,能有效处理稀疏数组的情况。考虑类似于[  , , , ,1, , , , , , 2, , , , , 3, , , , , 4] 的稀疏数组性能
       if (k in O) {
           let kValue = O[k];
           // 依次传入this, 当前项,当前索引,整个数组
           let mappedValue = callbackFn.call(T, kValue, k, O);
           A[k] = mappedValue;
       }
   }
   return A;
}

6.模拟实现reduce函数

Array.prototype.reduce = function(callbackFn, initialValue) {
   if (this === null || this === undefined) {
       throw new TypeError("Cannot read property 'reduce' of null or undefined");
   }
   if (Object.prototype.toString.call(callbackFn) != '[object Function]') {
       throw new TypeError(callbackfn + ' is not a function')
   }
   let O = Object(this);
   let len = O.length >>> 0;
   let k = 0;
   let accumulator = initialValue;
   if (accumulator === undefined) {
       for (; k<len; k++) {
           if (k in O) {
               accumulator = O[k];
               k++;
               break;
           }
       }
   }
   if (k === len && accumulator === undefined)
       throw new Error('Each element of the array is empty');
   for(;k<len;k++) {
       if (k in O) {
           accumulator = callbackFn.call(undefined, accumulator. O[k], k, O);
       }
   }
   return accumulator;
}

7.实现 push , pop 方法

Array.prototype.push = function(...items) {
   let O = Object(this);
   let len = this.length >>> 0;
   let argCount = items.length >>> 0;
   if (len + argCount > 2 ** 53 - 1) {
       throw new TypeError("The number of array is over the max value restricted!")
   }
   for (let i = 0; i<argCount; i++) {
       O[len+1] = items[i];
   }
   let newLength = len + argCount;
   O.length = newLength;
   return newLength;
}
Array.prototype.pop = function() {
   let O = Object(this);
   let len = this.length >>> 0;
   if (len === 0) {
       O.length = 0;
       return undefined;
   }
   len --;
   let value = O[len];
   delete O[len];
   O.length = len;
   return value;
}

8.实现filter方法

Array.prototype.filter = function(callbackfn, thisArg) {
   // 处理数组类型异常
   if (this === null || this === undefined) {
     throw new TypeError("Cannot read property 'filter' of null or undefined");
   }
   // 处理回调类型异常
   if (Object.prototype.toString.call(callbackfn) != "[object Function]") {
     throw new TypeError(callbackfn + ' is not a function')
   }
   let O = Object(this);
   let len = O.length >>> 0;
   let resLen = 0;
   let res = [];
   for(let i = 0; i < len; i++) {
     if (i in O) {
       let element = O[i];
       if (callbackfn.call(thisArg, O[i], i, O)) {
         res[resLen++] = element;
       }
     }
   }
   return res;
 }

9.模拟实现一个new的效果

function newOperator(ctor, ...args) {
     if (typeof ctor !== 'function') {
       throw 'newOperator function the first param must be a function';
     }
     let obj = Object.create(ctor.prototype);
     let res = ctor.call(obj, args);

     let isObject = typeOf res === 'object' && res !== null;
     let isFunction = typeof res === 'function';
     return isObject || isFunction ? res : obj;
 }

10.模拟实现一个bind的效果

Function.prototype.bind = function(content, ...args) {
   // 异常处理
   if (typeof this !== "function") {
       throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
   }
   var self = this;
   var fbound = function() {
       self.apply(this instanceof self ?
           this : content, args.concat(Array.prototype.slice.call(arguments)));
   }
   fbound.prototype = Object.create(self.prototype);

   return fbound;

}

11. 实现一个 call/apply 函数

Function.prototype.call = function (context, ...args) {
   let context = context || window;
   let fn = Symbol('fn');
   context.fn = this;

   let result = context.fn(...args);
   delete context.fn;
   return result;
}
Function.prototype.apply = function (context, args) {
   let context = context || window;
   let fn = Symbol('fn');
   context.fn = this;

   let result = context.fn(...args);
   delete context.fn;
   return result;
}

12.手动实现浅拷贝

const shallowClone = (target) => {
   if (typeof target === 'object' && target !== null) {
       const cloneTarget = Array.isArray(target) ? [] : {};
       for (let prop in target) {
           if (target.hasOwnProperty(prop)) {
               cloneTarget[prop] = target[prop];
           }
       }
       return cloneTarget;
   } else {
       return target;
   }
}

13.手动实现深拷贝

const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null)
const deepClone = function (obj, hash = new WeakMap()) {
if (obj.constructor === Date) 
return new Date(obj)       // 日期对象直接返回一个新的日期对象
if (obj.constructor === RegExp)
return new RegExp(obj)     //正则对象直接返回一个新的正则对象
//如果循环引用了就用 weakMap 来解决
if (hash.has(obj)) return hash.get(obj)
let allDesc = Object.getOwnPropertyDescriptors(obj)
//遍历传入参数所有键的特性
let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc)
//继承原型链
hash.set(obj, cloneObj)
for (let key of Reflect.ownKeys(obj)) { 
  cloneObj[key] = (isComplexDataType(obj[key]) && typeof obj[key] !== 'function') ? deepClone(obj[key], hash) : obj[key]
}
return cloneObj
}

14.实现node 中回调函数的机制

function EventEmitter() {
  this.events = new Map();
}

const wrapCallback = (fn, once = false) => ({
  callback: fn,
  once
})

EventEmitter.prototype.addListener = function(type, fn, once = false) {
  let handler = this.events.get(type);
  if (!handler) {
      this.events.set(type, wrapCallback(fn, once));
  } else if (handler && typeof handler.callback === 'function') {
      this.events.set(type, [handler, wrapCallback(fn, once)]);
  } else {
      handler.push(wrapCallback(fn, once));
  }
}

EventEmitter.prototype.removeListener = function(type, listener) {
  let handler = this.events.get(type);
  if (!handler) return;
  if (!Array.isArray(handler)) {
      if (handler.callback === listener.callback) this.events.delete(type);
      else return;
  }
  for (let i=0; i<handler.length; i++) {
      let item = handler[i];
      if (item.callback === listener.callback) {
          handler.splice(i, 1);
          i--;
          if (handler.length === 1) {
              this.events.set(type, handlerp[0])
          }
      }
  }
}

EventEmitter.prototype.once = function(type, fn) {
  this.addListener(type, fn, true);
}

EventEmitter.prototype.emit = function(type, ...args) {
  let handler = this.events.get(type);
  if (!handler) return;
  if (Array.isArray(handler)) {
      handler.map((item) => {
          item.callback.apply(this, args);
          if (item.once) {
              this.removeListener(type, item)
          }
      })
  } else {
      handler.callback.apply(this, args);
      this.removeListener(type, handler)
  }
  return true;
}

EventEmitter.prototype.removeAllListener = function(type) {
  let handler = this.events.get(type);
  if (!handler) return;
  else this.events.delete(type);
}

15.手写promise

const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

function MyPromise(executor) {
   let self = this;
   self.value = null;
   self.error = null;
   self.status = PENDING;
   self.onFulfilledCallbacks = [];
   self.onRejectedCallbacks = [];

   const resolve = (value) => {
       if (self.status !== PENDING) return;
       setTimeout(() => {
           self.status = FULFILLED;
           self.value = value;
           self.onFulfilledCallbacks.forEach((callback) => callback(self.value))
       })
   }

   const reject = (error) => {
       if (self.status !== PENDING) return;
       setTimeout(() => {
           self.status = REJECTED;
           self.error = error;
           self.onRejectedCallbacks.forEach((callback) => callback(self.error)
       })
   }

   executor(resolve, reject);
}

MyPromise.prototype.then = function(onFulfilled, onRejected) {

   onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
   onRejected = typeof onRejected === 'function' ? onRejected : error => {throw error};

   let bridgePromise;
   let self = this;

   if (this.status === PENDING) {

       return bridgePromise = new MyPromise((resolve,reject) => {
           self.onFulfilledCallbacks.push((value) => {
               try {
                   let x = onFulfilled(value);
                   resolvePromise(bridgePromise, x, resolve, reject);
               } catch (e) {
                   reject(e);
               }
           });
           self.onRejectedCallbacks.push((error) => {
               try {
                   let x = onRejected(error);
                   resolvePromise(bridgePromise, x, resolve, reject);
               } catch (e) {
                   reject(e);
               }
           })
       })       
   } else if (this.status === FULFILLED) {
       return bridgePromise = new MyPromise((resolve, reject) => {
           setTimeout(() => {
               try {
                   let x = onFulfilled(self.value);
                   resolvePromise(bridgePromise, x, resolve, reject);
               } catch (e) {
                   reject(e);
               }
           })
       })
   } else {
       return bridgePromise = new MyPromise((resolve, reject) => {
           setTimeout(() => {
               try {
                   let x = onRejected(self.error);
                   resolvePromise(bridgePromise, x, resolve, reject);
               } catch (e) {
                   reject(e);
               }
           })
         });
   }

}

function resolvePromise(bridgePromise, x, resolve, reject) {
   if (x instanceof MyPromise) {
       if (x.status === PENDING) {
           x.then(y => {
               resolvePromise(bridgePromise, y, resolve,reject);
           }, error => {
               reject(error);
           })
       } else {
           x.then(resolve, reject)
       }
   } else {
       resolve(x);
   }
}

MyPromise.prototype.catch = function(onRejected) {
   return this.then(null, onRejected);
}

MyPromise.resolve = (param) => {
   if (param instanceof MyPromise) return param;
   return new MyPromise((resolve, reject) => {
       if (param && param.then && param.then === 'function') {
           param.then(resolve, reject);
       } else {
           resolve(param);
       }
   })
}

MyPromise.reject = (reason) => {
   return new Promise((resolve, reject) => {
       reject(reason);
   })
}

MyPromise.prototype.finally = function(callback) {
   this.then(value => {
       return MyPromise.resolve(callback()).then(() => {
           return value;
       })
   }, error => {
       return MyPromise.reject(callback()).then(() =>{
           throw error;
       })
   })
}

MyPromise.all = function(promises) {
   return new Promise((resolve, reject) => {
       let result = [];
       let index = 0;
       let len = promises.length;
       if (len === 0) {
           resolve(result);
           return;
       }

       for(let i=0; i< len; i++) {
           MyPromise.resolve(promises[i]).then(data => {
               result[i] = data;
               index++;
               if (index === len) resolve(result);
           }).catch(err => {
               reject(err);
           })
       }
   })
}

MyPromise.race = function(promises) {
   return new MyPromise((resolve, reject) => {
       let len = promises.length;
       if (len === 0) {
           return;
       }
       for(let i=0; i< len; i++) {
           MyPromise.resolve(promises[i]).then(data => {
               resolve(data);
               return;
           }).catch(err => {
               reject(err);
               return;
           })
       }
   })
}

16.链表反转

class reverseList = function(head) {
   let pre = null;
   let cur = head;

   while(cur !== null) {
       let next = cur.next;
       cur.next = pre;
       pre = cur;
       cur = next;
   }

   return pre;
}

17.Promise实现多图 预加载

function preLoadImage(source){
    let pr = [];
    source.foeEach(url => {
        let p = loadImage(url)
    })
    Promise.all(pr)
    .then((images) => {
        console.log(images)
    })
}
function loadImage(url) {
    return new Promise((resolve, reject) => {
        let img = new Image();
        img.onload = () => resolve(img);
        img.onerror = reject;
        img.src = url;
    })
}

18.完成函数computed、multiply, add满足一下功能

    1. computed(multipy(num2), add(num3))(num1) // 等于 num1 - num2 + num3
    1. computed(add(num2), multiply(num3), add(num4))(num1) // 等于 num1 + num2 - num3 + num4
const num1 = 1
const num2 = 2
const num3 = 3
const num4 = 4
let result = 0
function computed(...params) {
    if (!params) return
    function fn(value) {
        result = value
        params.forEach(item => {
            // console.log('item', item)
            // console.log('result', result)
            result += item
        })
        // console.log('result2', result)
        return result
    }
    return fn
}

function add(value) {
    return value
}

function multiply(value) {
    return -value
}
console.log(computed(add(num2),multiply(num3),add(num4))(num1)) // 1 + 2 - 3 + 4

19.css3绘制扇形

<div class="shanxing">
  <div class="sx1"></div>
  <div class="sx2"></div>
</div>

.shanxing{
  position: relative;
  width: 200px;
  height: 200px;
  border-radius: 100px;
  background-color: yellow;
}
.sx1{
  position: absolute;
  width: 200px;
  height: 200px;
  transform: rotate(45deg);
  clip: rect(0,100px,200px,0);
  border-radius: 100px;
  background-color: #fff;

}
.sx2{
  position: absolute;
  width: 200px;
  height: 200px;
  transform: rotate(-45deg);
  clip: rect(0,100px,200px,0);
  border-radius: 100px;
  background-color: #fff;
}