一文帮你搞定 90% 的 JS 手写题!面试手写题不慌了

开发 前端
本文可以帮你扩展并巩固自己的JS基础,顺便搞定90%的手写题。在工作中还可以对常用的需求进行手写实现,比如深拷贝、防抖节流等可以直接用于往后的项目中,提高项目开发效率。不说废话了,下面就直接上代码吧。

[[415862]]

还在害怕手写题吗,本文可以帮你扩展并巩固自己的JS基础,顺便搞定90%的手写题。在工作中还可以对常用的需求进行手写实现,比如深拷贝、防抖节流等可以直接用于往后的项目中,提高项目开发效率。不说废话了,下面就直接上代码吧。

1.call的实现

  •  第一个参数为null或者undefined时,this指向全局对象window,值为原始值的指向该原始值的自动包装对象,如 String、Number、Boolean
  •  为了避免函数名与上下文(context)的属性发生冲突,使用Symbol类型作为唯一值
  •  将函数作为传入的上下文(context)属性执行
  •  函数执行完成后删除该属性
  •  返回执行结果 
  1. Function.prototype.myCall = function(context,...args){  
  2.     let cxt = context || window;  
  3.     //将当前被调用的方法定义在cxt.func上.(为了能以对象调用形式绑定this)  
  4.     //新建一个唯一的Symbol变量避免重复  
  5.     let func = Symbol()   
  6.     cxt[func] = this;  
  7.     argsargs = args ? args : []  
  8.     //以对象调用形式调用func,此时this指向cxt 也就是传入的需要绑定的this指向  
  9.     const res = args.length > 0 ? cxt[func](...args) : cxt[func]();  
  10.     //删除该方法,不然会对传入对象造成污染(添加该方法)  
  11.     delete cxt[func];  
  12.     return res;  

2.apply的实现

  •  前部分与call一样
  •  第二个参数可以不传,但类型必须为数组或者类数组 
  1. Function.prototype.myApply = function(context,args = []){  
  2.     let cxt = context || window;  
  3.     //将当前被调用的方法定义在cxt.func上.(为了能以对象调用形式绑定this)  
  4.     //新建一个唯一的Symbol变量避免重复  
  5.     let func = Symbol()  
  6.     cxt[func] = this;  
  7.     //以对象调用形式调用func,此时this指向cxt 也就是传入的需要绑定的this指向  
  8.     const res = args.length > 0 ? cxt[func](...args) : cxt[func]();  
  9.     delete cxt[func];  
  10.     return res;  

3.bind的实现

需要考虑:

  •  bind() 除了 this 外,还可传入多个参数;
  •  bind 创建的新函数可能传入多个参数;
  •  新函数可能被当做构造函数调用;
  •  函数可能有返回值;

实现方法:

  •  bind 方法不会立即执行,需要返回一个待执行的函数;(闭包)
  •  实现作用域绑定(apply)
  •  参数传递(apply 的数组传参)
  •  当作为构造函数的时候,进行原型继承 
  1. Function.prototype.myBind = function (context, ...args) {  
  2.     //新建一个变量赋值为this,表示当前函数  
  3.     const fn = this  
  4.     //判断有没有传参进来,若为空则赋值[]  
  5.     argsargs = args ? args : []  
  6.     //返回一个newFn函数,在里面调用fn  
  7.     return function newFn(...newFnArgs) {  
  8.         if (this instanceof newFn) {  
  9.             return new fn(...args, ...newFnArgs)  
  10.         }  
  11.         return fn.apply(context, [...args,...newFnArgs])  
  12.     }  
  •     测试 
  1. let name = '小王',age =17 
  2. let obj = {  
  3.     name:'小张',  
  4.     age: this.age,  
  5.     myFun: function(from,to){  
  6.         console.log(this.name + ' 年龄 ' + this.age+'来自 '+from+'去往'+ to)  
  7.     }  
  8.  
  9. let db = {  
  10.     name: '德玛', 
  11.     age: 99  
  12.  
  13. //结果  
  14. obj.myFun.myCall(db,'成都','上海');     // 德玛 年龄 99  来自 成都去往上海  
  15. obj.myFun.myApply(db,['成都','上海']);      // 德玛 年龄 99  来自 成都去往上海  
  16. obj.myFun.myBind(db,'成都','上海')();       // 德玛 年龄 99  来自 成都去往上海  
  17. obj.myFun.myBind(db,['成都','上海'])();   // 德玛 年龄 99  来自 成都, 上海去往 undefined 

4.寄生式组合继承 

  1. function Person(obj) {  
  2.     this.name = obj.name  
  3.     this.age = obj.age  
  4.  
  5. Person.prototype.add = function(value){  
  6.     console.log(value)  
  7.  
  8. var p1 = new Person({name:"番茄", age: 18}) 
  9. function Person1(obj) {  
  10.     Person.call(this, obj)  
  11.     this.sex = obj.sex  
  12.  
  13. // 这一步是继承的关键  
  14. Person1.prototype = Object.create(Person.prototype);  
  15. Person1Person1.prototype.constructor = Person1;  
  16. Person1.prototype.play = function(value){  
  17.     console.log(value)  
  18. var p2 = new Person1({name:"鸡蛋", age: 118, sex: "男"}) 

5.ES6继承 

  1. //class 相当于es5中构造函数  
  2. //class中定义方法时,前后不能加function,全部定义在class的protopyte属性中  
  3. //class中定义的所有方法是不可枚举的  
  4. //class中只能定义方法,不能定义对象,变量等  
  5. //class和方法内默认都是严格模式  
  6. //es5中constructor为隐式属性  
  7. class People{  
  8.   constructor(name='wang',age='27'){  
  9.     this.name = name;  
  10.     this.age = age;  
  11.   }  
  12.   eat(){  
  13.     console.log(`${this.name} ${this.age} eat food`)  
  14.   }  
  15.  
  16. //继承父类  
  17. class Woman extends People{   
  18.    constructor(name = 'ren',age = '27'){   
  19.      //继承父类属性  
  20.      super(name, age);  
  21.    }   
  22.     eat(){   
  23.      //继承父类方法  
  24.       super.eat()   
  25.     }   
  26. }   
  27. let wonmanObj=new Woman('xiaoxiami');   
  28. wonmanObj.eat();  
  29. //es5继承先创建子类的实例对象,然后再将父类的方法添加到this上(Parent.apply(this))。   
  30. //es6继承是使用关键字super先创建父类的实例对象this,最后在子类class中修改this。 

6.new的实现

  •  一个继承自 Foo.prototype 的新对象被创建。
  •  使用指定的参数调用构造函数 Foo,并将 this 绑定到新创建的对象。new Foo 等同于 new Foo(),也就是没有指定参数列表,Foo 不带任何参数调用的情况。
  •  由构造函数返回的对象就是 new 表达式的结果。如果构造函数没有显式返回一个对象,则使用步骤1创建的对象。
  •  一般情况下,构造函数不返回值,但是用户可以选择主动返回对象,来覆盖正常的对象创建步骤 
  1. function Ctor(){  
  2.     ....  
  3.  
  4. function myNew(ctor,...args){  
  5.     if(typeof ctor !== 'function'){  
  6.       throw 'myNew function the first param must be a function';  
  7.     }  
  8.     var newObj = Object.create(ctor.prototype); //创建一个继承自ctor.prototype的新对象  
  9.     var ctorctorReturnResult = ctor.apply(newObj, args); //将构造函数ctor的this绑定到newObj中  
  10.     var isObject = typeof ctorReturnResult === 'object' && ctorReturnResult !== null;  
  11.     var isFunction = typeof ctorReturnResult === 'function';  
  12.     if(isObject || isFunction){  
  13.         return ctorReturnResult;  
  14.     }  
  15.     return newObj;  
  16. let c = myNew(Ctor); 

7.instanceof的实现

  •  instanceof 是用来判断A是否为B的实例,表达式为:A instanceof B,如果A是B的实例,则返回true,否则返回false。
  •  instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。
  •  不能检测基本数据类型,在原型链上的结果未必准确,不能检测null,undefined
  •  实现:遍历左边变量的原型链,直到找到右边变量的 prototype,如果没有找到,返回 false 
  1. function myInstanceOf(a,b){  
  2.     let left = a.__proto__;  
  3.     let right = b.prototype;  
  4.     while(true){  
  5.         if(left == null){  
  6.             return false  
  7.         }  
  8.         if(left == right){  
  9.             return true  
  10.         }  
  11.         leftleft = left.__proto__  
  12.     }  
  13.  
  14. //instanceof 运算符用于判断构造函数的 prototype 属性是否出现在对象的原型链中的任何位置。  
  15. function myInstanceof(left, right) {  
  16.     let proto = Object.getPrototypeOf(left), // 获取对象的原型  
  17.     prototype = right.prototype; // 获取构造函数的 prototype 对象  
  18.     // 判断构造函数的 prototype 对象是否在对象的原型链上  
  19.     while (true) {  
  20.         if (!proto) return false;  
  21.         if (proto === prototype) return true;  
  22.         proto = Object.getPrototypeOf(proto);  
  23.     }  

8.Object.create()的实现

  •  MDN文档
  •  Object.create()会将参数对象作为一个新创建的空对象的原型, 并返回这个空对象 
  1. //简略版  
  2. function myCreate(obj){  
  3.     // 新声明一个函数  
  4.     function C(){};  
  5.     // 将函数的原型指向obj  
  6.     C.prototype = obj 
  7.     // 返回这个函数的实力化对象  
  8.     return new C()  
  9.  
  10. //官方版Polyfill  
  11. if (typeof Object.create !== "function") {  
  12.     Object.create = function (proto, propertiesObject) {  
  13.         if (typeof proto !== 'object' && typeof proto !== 'function') {  
  14.             throw new TypeError('Object prototype may only be an Object: ' + proto);  
  15.         } else if (proto === null) {  
  16.             throw new Error("This browser's implementation of Object.create is a shim and doesn't support 'null' as the first argument."); 
  17.          }  
  18.         if (typeof propertiesObject !== 'undefined') throw new Error("This browser's implementation of Object.create is a shim and doesn't support a second argument."); 
  19.          function F() {}  
  20.         F.prototype = proto;  
  21.         return new F();  
  22.     };  

9.实现 Object.assign 

  1. Object.assign2 = function(target, ...source) {  
  2.     if (target == null) {  
  3.         throw new TypeError('Cannot convert undefined or null to object')  
  4.     }  
  5.     let ret = Object(target)   
  6.     source.forEach(function(obj) {  
  7.         if (obj != null) {  
  8.             for (let key in obj) {  
  9.                 if (obj.hasOwnProperty(key)) {  
  10.                     ret[key] = obj[key]  
  11.                 }  
  12.             }  
  13.         }  
  14.     })  
  15.     return ret  

10.Promise的实现

实现 Promise 需要完全读懂 Promise A+ 规范,不过从总体的实现上看,有如下几个点需要考虑到:

  •  Promise本质是一个状态机,且状态只能为以下三种:Pending(等待态)、Fulfilled(执行态)、Rejected(拒绝态),状态的变更是单向的,只能从Pending -> Fulfilled 或 Pending -> Rejected,状态变更不可逆
  •  then 需要支持链式调用 
  1. class Promise {  
  2.     callbacks = [];  
  3.     state = 'pending';//增加状态  
  4.     value = null;//保存结果  
  5.     constructor(fn) {  
  6.         fn(this._resolve.bind(this), this._reject.bind(this));  
  7.     }  
  8.     then(onFulfilled, onRejected) {  
  9.         return new Promise((resolve, reject) => {  
  10.             this._handle({  
  11.                 onFulfilled: onFulfilled || null,  
  12.                 onRejected: onRejected || null,  
  13.                 resolve: resolve,  
  14.                 reject: reject  
  15.             });  
  16.         });  
  17.     }  
  18.     _handle(callback) {  
  19.         if (this.state === 'pending') {  
  20.             this.callbacks.push(callback);  
  21.             return;  
  22.         }  
  23.         let cb = this.state === 'fulfilled' ? callback.onFulfilled : callback.onRejected;  
  24.         if (!cb) {//如果then中没有传递任何东西  
  25.             cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;  
  26.             cb(this.value);  
  27.             return;  
  28.         }  
  29.         let ret = cb(this.value);  
  30.         cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;  
  31.         cb(ret);  
  32.     }  
  33.     _resolve(value) {  
  34.         if (value && (typeof value === 'object' || typeof value === 'function')) {  
  35.             var then = value.then;  
  36.             if (typeof then === 'function') {  
  37.                 then.call(value, this._resolve.bind(this), this._reject.bind(this));  
  38.                 return;  
  39.             }  
  40.         }  
  41.         this.state = 'fulfilled';//改变状态  
  42.         this.value = value;//保存结果  
  43.         this.callbacks.forEach(callback => this._handle(callback));  
  44.     }  
  45.     _reject(error) {  
  46.         this.state = 'rejected' 
  47.         this.value = error 
  48.         this.callbacks.forEach(callback => this._handle(callback));  
  49.     }  

Promise.resolve

  •  Promsie.resolve(value) 可以将任何值转成值为 value 状态是 fulfilled 的 Promise,但如果传入的值本身是 Promise 则会原样返回它。 
  1. Promise.resolve(value) {  
  2.   if (value && value instanceof Promise) {  
  3.     return value;  
  4.   } else if (value && typeof value === 'object' && typeof value.then === 'function') {  
  5.     let then = value.then;  
  6.     return new Promise(resolve => {  
  7.       then(resolve);  
  8.     });  
  9.   } else if (value) {  
  10.     return new Promise(resolve => resolve(value));  
  11.   } else {  
  12.     return new Promise(resolve => resolve()); 
  13.   }  

Promise.reject

  •  和 Promise.resolve() 类似,Promise.reject() 会实例化一个 rejected 状态的 Promise。但与 Promise.resolve() 不同的是,如果给 Promise.reject() 传递一个 Promise 对象,则这个对象会成为新 Promise 的值。 
  1. Promise.reject = function(reason) {  
  2.     return new Promise((resolve, reject) => reject(reason))  

Promise.all

  •  传入的所有 Promsie 都是 fulfilled,则返回由他们的值组成的,状态为 fulfilled 的新 Promise;
  •  只要有一个 Promise 是 rejected,则返回 rejected 状态的新 Promsie,且它的值是第一个 rejected 的 Promise 的值;
  •  只要有一个 Promise 是 pending,则返回一个 pending 状态的新 Promise; 
  1. Promise.all = function(promiseArr) {  
  2.     let index = 0result = []  
  3.     return new Promise((resolve, reject) => {  
  4.         promiseArr.forEach((p, i) => {  
  5.             Promise.resolve(p).then(val => {  
  6.                 index++  
  7.                 result[i] = val  
  8.                 if (index === promiseArr.length) {  
  9.                     resolve(result)  
  10.                 }  
  11.             }, err => {  
  12.                 reject(err)  
  13.             })  
  14.         })  
  15.     })  

Promise.race

  •  Promise.race 会返回一个由所有可迭代实例中第一个 fulfilled 或 rejected 的实例包装后的新实例。 
  1. Promise.race = function(promiseArr) {  
  2.     return new Promise((resolve, reject) => {  
  3.         promiseArr.forEach(p => {  
  4.             Promise.resolve(p).then(val => {  
  5.                 resolve(val)  
  6.             }, err => {  
  7.                 rejecte(err)  
  8.             })  
  9.         })  
  10.     })  

11.Ajax的实现 

  1. function ajax(url,method,body,headers){  
  2.     return new Promise((resolve,reject)=> 
  3.         let req = new XMLHttpRequest();  
  4.         req.open(methods,url);  
  5.         for(let key in headers){  
  6.             req.setRequestHeader(key,headers[key]) 
  7.         }  
  8.         req.onreadystatechange(()=> 
  9.             if(req.readystate == 4){  
  10.                 if(req.status >= '200' && req.status <= 300){  
  11.                     resolve(req.responeText)  
  12.                 }else{  
  13.                     reject(req)  
  14.                 }  
  15.             }  
  16.         })  
  17.         req.send(body)  
  18.     })  

12.实现防抖函数(debounce)

  •  连续触发在最后一次执行方法,场景:输入框匹配 
  1. let debounce = (fn,time = 1000) => {  
  2.     let timeLock = null  
  3.     return function (...args){  
  4.         clearTimeout(timeLock)  
  5.         timeLock = setTimeout(()=> 
  6.             fn(...args)  
  7.         },time)  
  8.     }  

13.实现节流函数(throttle)

  •  在一定时间内只触发一次,场景:长列表滚动节流 
  1. let throttle = (fn,time = 1000) => {  
  2.     let flag = true 
  3.     return function (...args){  
  4.         if(flag){  
  5.             flag = false 
  6.             setTimeout(()=> 
  7.                 flag = true 
  8.                 fn(...args)  
  9.             },time)  
  10.         }  
  11.     }  

14.深拷贝(deepclone)

  •  判断类型,正则和日期直接返回新对象
  •  空或者非对象类型,直接返回原值
  •  考虑循环引用,判断如果hash中含有直接返回hash中的值
  •  新建一个相应的new obj.constructor加入hash
  •  遍历对象递归(普通key和key是symbol情况) 
  1. function deepClone(obj,hash = new WeakMap()){  
  2.     if(obj instanceof RegExp) return new RegExp(obj);  
  3.     if(obj instanceof Date) return new Date(obj);  
  4.     if(obj === null || typeof obj !== 'object') return obj;  
  5.     //循环引用的情况  
  6.     if(hash.has(obj)){  
  7.         return hash.get(obj)  
  8.     }  
  9.     //new 一个相应的对象  
  10.     //obj为Array,相当于new Array()  
  11.     //obj为Object,相当于new Object()  
  12.     let constr = new obj.constructor();  
  13.     hash.set(obj,constr);  
  14.     for(let key in obj){  
  15.         if(obj.hasOwnProperty(key)){  
  16.             constr[key] = deepClone(obj[key],hash)  
  17.         }  
  18.     }  
  19.     //考虑symbol的情况  
  20.     let symbolObj = Object.getOwnPropertySymbols(obj)  
  21.     for(let i=0;i<symbolObj.length;i++){  
  22.         if(obj.hasOwnProperty(symbolObj[i])){  
  23.             constr[symbolObj[i]] = deepClone(obj[symbolObj[i]],hash)  
  24.         }  
  25.     }  
  26.     return constr  

15.数组扁平化的实现(flat) 

  1. let arr = [1,2,[3,4,[5,[6]]]]  
  2. console.log(arr.flat(Infinity))//flat参数为指定要提取嵌套数组的结构深度,默认值为 1 //用reduce实现 
  1. function fn(arr){  
  2.    return arr.reduce((prev,cur)=> 
  3.       return prev.concat(Array.isArray(cur)?fn(cur):cur)  
  4.    },[])  

16.函数柯里化 

  1. function sumFn(a,b,c){return a+ b + c};  
  2. let sum = curry(sumFn);  
  3. sum(2)(3)(5)//10  
  4. sum(2,3)(5)//10  
  1. function curry(fn,...args){  
  2.   let fnfnLen = fn.length,  
  3.       argsargsLen = args.length;  
  4.   //对比函数的参数和当前传入参数  
  5.   //若参数不够就继续递归返回curry  
  6.   //若参数够就调用函数返回相应的值  
  7.   if(fnLen > argsLen){  
  8.     return function(...arg2s){  
  9.       return curry(fn,...args,...arg2s)  
  10.     }  
  11.   }else{  
  12.     return fn(...args)  
  13.   } 
  14.  

17.使用闭包实现每隔一秒打印 1,2,3,4 

  1. for (var i=1; i<=5; i++) {  
  2.   (function (i) {  
  3.     setTimeout(() => console.log(i), 1000*i)  
  4.   })(i)  

18.手写一个 jsonp 

  1. const jsonp = function (url, data) {  
  2.     return new Promise((resolve, reject) => {  
  3.         // 初始化url  
  4.         let dataString = url.indexOf('?') === -1 ? '?' : ''  
  5.         let callbackName = `jsonpCB_${Date.now()}`  
  6.         url += `${dataString}callback=${callbackName}`  
  7.         if (data) {  
  8.             // 有请求参数,依次添加到url  
  9.             for (let k in data) {  
  10.                 url += `${k}=${data[k]}`  
  11.             }  
  12.         }  
  13.         let jsNode = document.createElement('script') 
  14.         jsNode.src = url  
  15.         // 触发callback,触发后删除js标签和绑定在window上的callback  
  16.         window[callbackName] = result => {  
  17.             delete window[callbackName]  
  18.             document.body.removeChild(jsNode)  
  19.             if (result) {  
  20.                 resolve(result)  
  21.             } else {  
  22.                 reject('没有返回数据')  
  23.             }  
  24.         }  
  25.         // js加载异常的情况  
  26.         jsNode.addEventListener('error', () => {  
  27.             delete window[callbackName]  
  28.             document.body.removeChild(jsNode)  
  29.             reject('JavaScript资源加载失败')  
  30.         }, false)  
  31.         // 添加js节点到document上时,开始请求  
  32.         document.body.appendChild(jsNode)  
  33.     })  
  34.  
  35. jsonp('http://192.168.0.103:8081/jsonp', {  
  36.     a: 1,  
  37.     b: 'heiheihei'  
  38. })  
  39. .then(result => {  
  40.     console.log(result)  
  41. })  
  42. .catch(err => {  
  43.     console.error(err)  
  44. }) 

19.手写一个观察者模式 

  1. class Subject{  
  2.   constructor(name){  
  3.     this.name = name  
  4.     this.observers = []  
  5.     this.state = 'XXXX'  
  6.   }  
  7.   // 被观察者要提供一个接受观察者的方法  
  8.   attach(observer){  
  9.     this.observers.push(observer)  
  10.   }  
  11.   // 改变被观察着的状态  
  12.   setState(newState){  
  13.     this.state = newState 
  14.     this.observers.forEach(o=> 
  15.       o.update(newState)  
  16.     })  
  17.   }  
  18.  
  19. class Observer{  
  20.   constructor(name){  
  21.     this.name = name  
  22.   }  
  23.   update(newState){  
  24.     console.log(`${this.name}say:${newState}`)  
  25.   }  
  26.  
  27. // 被观察者 灯  
  28. let sub = new Subject('灯')  
  29. let mm = new Observer('小明')  
  30. let jj = new Observer('小健')  
  31. // 订阅 观察者  
  32. sub.attach(mm)  
  33. sub.attach(jj)  
  34. sub.setState('灯亮了来电了') 

20.EventEmitter 实现 

  1. class EventEmitter {  
  2.     constructor() {  
  3.         this.events = {};  
  4.     }  
  5.     on(event, callback) {  
  6.         let callbacks = this.events[event] || [];  
  7.         callbacks.push(callback);  
  8.         this.events[event] = callbacks;  
  9.         return this;  
  10.     }  
  11.     off(event, callback) { 
  12.         let callbacks = this.events[event];  
  13.         this.events[event] = callbacks && callbacks.filter(fn => fn !== callback);  
  14.         return this;  
  15.     }  
  16.     emit(event, ...args) {  
  17.         let callbacks = this.events[event];  
  18.         callbacks.forEach(fn => {  
  19.             fn(...args);  
  20.         });  
  21.         return this;  
  22.     }  
  23.     once(event, callback) {  
  24.         let wrapFun = function (...args) {  
  25.             callback(...args);  
  26.             this.off(event, wrapFun);  
  27.         }; 
  28.          this.on(event, wrapFun);  
  29.         return this;  
  30.     }  

21.生成随机数的各种方法? 

  1. function getRandom(min, max) {  
  2.   return Math.floor(Math.random() * (max - min)) + min     

22.如何实现数组的随机排序? 

  1. let arr = [2,3,454,34,324,32]  
  2. arr.sort(randomSort)  
  3. function randomSort(a, b) {  
  4.   return Math.random() > 0.5 ? -1 : 1;  

23.写一个通用的事件侦听器函数。 

  1. const EventUtils = {  
  2.   // 视能力分别使用dom0||dom2||IE方式 来绑定事件  
  3.   // 添加事件  
  4.   addEvent: function(element, type, handler) {  
  5.     if (element.addEventListener) {  
  6.       element.addEventListener(type, handler, false);  
  7.     } else if (element.attachEvent) {  
  8.       element.attachEvent("on" + type, handler);  
  9.     } else {  
  10.       element["on" + type] = handler;  
  11.     }  
  12.   },  
  13.   // 移除事件  
  14.   removeEvent: function(element, type, handler) {  
  15.     if (element.removeEventListener) {  
  16.       element.removeEventListener(type, handler, false);  
  17.     } else if (element.detachEvent) { 
  18.        element.detachEvent("on" + type, handler);  
  19.     } else {  
  20.       element["on" + type] = null;  
  21.     }  
  22.   },  
  23.  // 获取事件目标  
  24.   getTarget: function(event) {  
  25.     return event.target || event.srcElement;  
  26.   },  
  27.   // 获取 event 对象的引用,取到事件的所有信息,确保随时能使用 event  
  28.   getEvent: function(event) {  
  29.     return event || window.event;  
  30.   }, 
  31.   // 阻止事件(主要是事件冒泡,因为 IE 不支持事件捕获)  
  32.   stopPropagation: function(event) {  
  33.     if (event.stopPropagation) {  
  34.       event.stopPropagation();  
  35.     } else {  
  36.       event.cancelBubble = true 
  37.     }  
  38.   },  
  39.   // 取消事件的默认行为  
  40.   preventDefault: function(event) {  
  41.     if (event.preventDefault) {  
  42.       event.preventDefault();  
  43.     } else {  
  44.       event.returnValue = false 
  45.     }  
  46.   }  
  47. }; 

24.使用迭代的方式实现 flatten 函数。 

  1. var arr = [1, 2, 3, [4, 5], [6, [7, [8]]]]  
  2. /** * 使用递归的方式处理 * wrap 内保  
  3. 存结果 ret * 返回一个递归函数 **/  
  4. function wrap() {  
  5.     var ret = [];  
  6.     return function flat(a) {  
  7.         for (var item of  
  8.             a) {  
  9.                 if (item.constructor === Array) {  
  10.                     ret.concat(flat(item))  
  11.                 } else {  
  12.                     ret.push(item)  
  13.                 }  
  14.         }  
  15.         return ret  
  16.     }  
  17. }   
  18. console.log(wrap()(arr)); 

25.怎么实现一个sleep

  •  sleep函数作用是让线程休眠,等到指定时间在重新唤起。 
  1. function sleep(delay) {  
  2.   var start = (new Date()).getTime();  
  3.   while ((new Date()).getTime() - start < delay) {  
  4.     continue;  
  5.   }  
  6. function test() {  
  7.   console.log('111');  
  8.   sleep(2000);  
  9.   console.log('222');  
  10.  
  11. test() 

26.实现正则切分千分位(10000 => 10,000) 

  1. //无小数点  
  2. let num1 = '1321434322222'  
  3. num1.replace(/(\d)(?=(\d{3})+$)/g,'$1,')  
  4. //有小数点  
  5. let num2 = '342243242322.3432423'  
  6. num2.replace(/(\d)(?=(\d{3})+\.)/g,'$1,') 

27.对象数组去重

  1. 输入: 
  2. [{a:1,b:2,c:3},{b:2,c:3,a:1},{d:2,c:2}]  
  3. 输出: 
  4. [{a:1,b:2,c:3},{d:2,c:2}] 
  •  首先写一个函数把对象中的key排序,然后再转成字符串
  •  遍历数组利用Set将转为字符串后的对象去重 
  1. function objSort(obj){  
  2.     let newObj = {}  
  3.     //遍历对象,并将key进行排序  
  4.     Object.keys(obj).sort().map(key => {  
  5.         newObj[key] = obj[key]  
  6.     })  
  7.     //将排序好的数组转成字符串  
  8.     return JSON.stringify(newObj)  
  9.  
  10. function unique(arr){ 
  11.     let set = new Set();  
  12.     for(let i=0;i<arr.length;i++){  
  13.         let str = objSort(arr[i])  
  14.         set.add(str)  
  15.     }  
  16.     //将数组中的字符串转回对象  
  17.     arr = [...set].map(item => {  
  18.         return JSON.parse(item)  
  19.     })  
  20.     return arr  

28.解析 URL Params 为对象 

  1. let url = 'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled' 
  2. parseParam(url)  
  3. /* 结果  
  4. { user: 'anonymous',  
  5.   id: [ 123, 456 ], // 重复出现的 key 要组装成数组,能被转成数字的就转成数字类型  
  6.   city: '北京', // 中文需解码  
  7.   enabled: true, // 未指定值得 key 约定为 true  
  8.  
  9. */  
  1. function parseParam(url) {  
  2.   const paramsStr = /.+\?(.+)$/.exec(url)[1]; // 将 ? 后面的字符串取出来  
  3.   const paramsArr = paramsStr.split('&'); // 将字符串以 & 分割后存到数组中  
  4.   let paramsObj = {};  
  5.   // 将 params 存到对象中  
  6.   paramsArr.forEach(param => {  
  7.     if (/=/.test(param)) { // 处理有 value 的参数  
  8.       let [key, val] = param.split('='); // 分割 key 和 value  
  9.       val = decodeURIComponent(val); // 解码  
  10.       val = /^\d+$/.test(val) ? parseFloat(val) : val; // 判断是否转为数字  
  11.       if (paramsObj.hasOwnProperty(key)) { // 如果对象有 key,则添加一个值  
  12.         paramsObj[key] = [].concat(paramsObj[key], val);  
  13.       } else { // 如果对象没有这个 key,创建 key 并设置值  
  14.         paramsObj[key] = val;  
  15.       }  
  16.     } else { // 处理没有 value 的参数  
  17.       paramsObj[param] = true;  
  18.     }  
  19.   }) 
  20.   return paramsObj;  

29.模板引擎实现 

  1. let template = '我是{{name}},年龄{{age}},性别{{sex}}' 
  2. let data = {  
  3.   name: '姓名',  
  4.   age: 18  
  5.  
  6. render(template, data); // 我是姓名,年龄18,性别undefined  
  1. function render(template, data) {  
  2.   const reg = /\{\{(\w+)\}\}/; // 模板字符串正则  
  3.   if (reg.test(template)) { // 判断模板里是否有模板字符串  
  4.     const name = reg.exec(template)[1]; // 查找当前模板里第一个模板字符串的字段  
  5.     templatetemplate = template.replace(reg, data[name]); // 将第一个模板字符串渲染  
  6.     return render(template, data); // 递归的渲染并返回渲染后的结构  
  7.   }  
  8.   return template; // 如果模板没有模板字符串直接返回  

30.转化为驼峰命名 

  1. var s1 = "get-element-by-id"  
  2. // 转化为 getElementById  
  1. var f = function(s) {  
  2.     return s.replace(/-\w/g, function(x) {  
  3.         return x.slice(1).toUpperCase(); 
  4.     })  

31.查找字符串中出现最多的字符和个数

  •  例: abbcccddddd -> 字符最多的是d,出现了5次 
  1. let str = "abcabcabcbbccccc" 
  2. let num = 0 
  3. let char = '' 
  4.  // 使其按照一定的次序排列  
  5. strstr = str.split('').sort().join('');  
  6. // "aaabbbbbcccccccc"  
  7. // 定义正则表达式  
  8. let re = /(\w)\1+/g;  
  9. str.replace(re,($0,$1) => {  
  10.     if(num < $0.length){  
  11.         num = $0.length;  
  12.         char = $1;      
  13.      }  
  14. });  
  15. console.log(`字符最多的是${char},出现了${num}次`);

32.图片懒加载 

  1. let imgList = [...document.querySelectorAll('img')]  
  2. let length = imgList.length  
  3. const imgLazyLoad = function() {  
  4.     let count = 0  
  5.     return (function() {  
  6.         let deleteIndexList = []  
  7.         imgList.forEach((img, index) => {  
  8.             let rect = img.getBoundingClientRect()  
  9.             if (rect.top < window.innerHeight) {  
  10.                 imgimg.src = img.dataset.src  
  11.                 deleteIndexList.push(index)  
  12.                 count++  
  13.                 if (count === length) {  
  14.                     document.removeEventListener('scroll', imgLazyLoad)  
  15.                 }  
  16.             }  
  17.         })  
  18.         imgListimgList = imgList.filter((img, index) => !deleteIndexList.includes(index))  
  19.     })()  
  20.  
  21. // 这里最好加上防抖处理  
  22. document.addEventListener('scroll', imgLazyLoad)  

 

责任编辑:庞桂玉 来源: 前端大全
相关推荐

2022-04-15 08:03:41

SaaS应用管理市场

2021-04-02 07:53:35

js前端手写题

2021-08-13 05:50:01

ContainerdDockerKubernetes

2024-03-26 00:33:59

JVM内存对象

2020-03-03 17:47:07

UDP TCP面试题

2022-02-08 18:09:20

JS引擎解析器

2024-01-09 08:24:47

JMM核心线程

2021-03-28 18:40:02

LinuxWindowsJava

2021-10-25 16:01:01

Linux设备树字符串

2019-08-27 14:46:59

ElasticSearES数据库

2019-09-23 10:51:14

JavaJava虚拟机Linux

2020-12-18 10:13:19

晋升职级协议

2019-06-17 05:03:37

memcache内核架构

2022-08-17 18:25:37

Java分布式搜索引擎

2021-08-31 07:02:20

Diff算法DOM

2017-03-28 08:53:22

2021-08-04 06:56:49

HTTP缓存前端

2020-10-29 08:55:04

微服务

2021-10-06 20:23:08

Linux共享内存

2021-08-31 07:02:34

数据响应Vue侦测数据变化
点赞
收藏

51CTO技术栈公众号