1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        一文幫你搞定90%的JS手寫題

        共 45975字,需瀏覽 92分鐘

         ·

        2021-05-26 20:40

        點(diǎn)擊上方 前端Q,關(guān)注公眾號(hào)

        回復(fù)加群,加入前端Q技術(shù)交流群



        還在害怕手寫題嗎,本文可以幫你擴(kuò)展并鞏固自己的JS基礎(chǔ),順便搞定90%的手寫題。在工作中還可以對(duì)常用的需求進(jìn)行手寫實(shí)現(xiàn),比如深拷貝、防抖節(jié)流等可以直接用于往后的項(xiàng)目中,提高項(xiàng)目開發(fā)效率。不說廢話了,下面就直接上代碼吧。

        1.call的實(shí)現(xiàn)

        • 第一個(gè)參數(shù)為null或者undefined時(shí),this指向全局對(duì)象window,值為原始值的指向該原始值的自動(dòng)包裝對(duì)象,如 String、Number、Boolean
        • 為了避免函數(shù)名與上下文(context)的屬性發(fā)生沖突,使用Symbol類型作為唯一值
        • 將函數(shù)作為傳入的上下文(context)屬性執(zhí)行
        • 函數(shù)執(zhí)行完成后刪除該屬性
        • 返回執(zhí)行結(jié)果
        Function.prototype.myCall = function(context,...args){
            let cxt = context || window;
            //將當(dāng)前被調(diào)用的方法定義在cxt.func上.(為了能以對(duì)象調(diào)用形式綁定this)
            //新建一個(gè)唯一的Symbol變量避免重復(fù)
            let func = Symbol() 
            cxt[func] = this;
            args = args ? args : []
            //以對(duì)象調(diào)用形式調(diào)用func,此時(shí)this指向cxt 也就是傳入的需要綁定的this指向
            const res = args.length > 0 ? cxt[func](...args) : cxt[func]();
            //刪除該方法,不然會(huì)對(duì)傳入對(duì)象造成污染(添加該方法)
            delete cxt[func];
            return res;
        }

        2.apply的實(shí)現(xiàn)

        • 前部分與call一樣
        • 第二個(gè)參數(shù)可以不傳,但類型必須為數(shù)組或者類數(shù)組
        Function.prototype.myApply = function(context,args = []){
            let cxt = context || window;
            //將當(dāng)前被調(diào)用的方法定義在cxt.func上.(為了能以對(duì)象調(diào)用形式綁定this)
            //新建一個(gè)唯一的Symbol變量避免重復(fù)
            let func = Symbol()
            cxt[func] = this;
            //以對(duì)象調(diào)用形式調(diào)用func,此時(shí)this指向cxt 也就是傳入的需要綁定的this指向
            const res = args.length > 0 ? cxt[func](...args) : cxt[func]();
            delete cxt[func];
            return res;
        }

        3.bind的實(shí)現(xiàn)

        需要考慮:

        • bind() 除了 this 外,還可傳入多個(gè)參數(shù);
        • bind 創(chuàng)建的新函數(shù)可能傳入多個(gè)參數(shù);
        • 新函數(shù)可能被當(dāng)做構(gòu)造函數(shù)調(diào)用;
        • 函數(shù)可能有返回值;

        實(shí)現(xiàn)方法:

        • bind 方法不會(huì)立即執(zhí)行,需要返回一個(gè)待執(zhí)行的函數(shù);(閉包)
        • 實(shí)現(xiàn)作用域綁定(apply)
        • 參數(shù)傳遞(apply 的數(shù)組傳參)
        • 當(dāng)作為構(gòu)造函數(shù)的時(shí)候,進(jìn)行原型繼承
        Function.prototype.myBind = function (context, ...args) {
            //新建一個(gè)變量賦值為this,表示當(dāng)前函數(shù)
            const fn = this
            //判斷有沒有傳參進(jìn)來,若為空則賦值[]
            args = args ? args : []
            //返回一個(gè)newFn函數(shù),在里面調(diào)用fn
            return function newFn(...newFnArgs) {
                if (this instanceof newFn) {
                    return new fn(...args, ...newFnArgs)
                }
                return fn.apply(context, [...args,...newFnArgs])
            }
        }
        • 測(cè)試
        let name = '小王',age =17;
        let obj = {
            name:'小張',
            age: this.age,
            myFun: function(from,to){
                console.log(this.name + ' 年齡 ' + this.age+'來自 '+from+'去往'+ to)
            }
        }
        let db = {
            name: '德瑪',
            age: 99
        }

        //結(jié)果
        obj.myFun.myCall(db,'成都','上海');     // 德瑪 年齡 99  來自 成都去往上海
        obj.myFun.myApply(db,['成都','上海']);      // 德瑪 年齡 99  來自 成都去往上海
        obj.myFun.myBind(db,'成都','上海')();       // 德瑪 年齡 99  來自 成都去往上海
        obj.myFun.myBind(db,['成都','上海'])();   // 德瑪 年齡 99  來自 成都, 上海去往 undefined

        4.寄生式組合繼承

        function Person(obj) {
            this.name = obj.name
            this.age = obj.age
        }
        Person.prototype.add = function(value){
            console.log(value)
        }
        var p1 = new Person({name:"番茄", age: 18})

        function Person1(obj) {
            Person.call(this, obj)
            this.sex = obj.sex
        }
        // 這一步是繼承的關(guān)鍵
        Person1.prototype = Object.create(Person.prototype);
        Person1.prototype.constructor = Person1;

        Person1.prototype.play = function(value){
            console.log(value)
        }
        var p2 = new Person1({name:"雞蛋", age: 118, sex: "男"})

        5.ES6繼承

        //class 相當(dāng)于es5中構(gòu)造函數(shù)
        //class中定義方法時(shí),前后不能加function,全部定義在class的protopyte屬性中
        //class中定義的所有方法是不可枚舉的
        //class中只能定義方法,不能定義對(duì)象,變量等
        //class和方法內(nèi)默認(rèn)都是嚴(yán)格模式
        //es5中constructor為隱式屬性
        class People{
          constructor(name='wang',age='27'){
            this.name = name;
            this.age = age;
          }
          eat(){
            console.log(`${this.name} ${this.age} eat food`)
          }
        }
        //繼承父類
        class Woman extends People
           constructor(name = 'ren',age = '27'){ 
             //繼承父類屬性
             super(name, age); 
           } 
            eat(){ 
             //繼承父類方法
              super.eat() 
            } 

        let wonmanObj=new Woman('xiaoxiami'); 
        wonmanObj.eat();

        //es5繼承先創(chuàng)建子類的實(shí)例對(duì)象,然后再將父類的方法添加到this上(Parent.apply(this))。 
        //es6繼承是使用關(guān)鍵字super先創(chuàng)建父類的實(shí)例對(duì)象this,最后在子類class中修改this。

        6.new的實(shí)現(xiàn)

        • 一個(gè)繼承自 Foo.prototype 的新對(duì)象被創(chuàng)建。
        • 使用指定的參數(shù)調(diào)用構(gòu)造函數(shù) Foo,并將 this 綁定到新創(chuàng)建的對(duì)象。new Foo 等同于 new Foo(),也就是沒有指定參數(shù)列表,F(xiàn)oo 不帶任何參數(shù)調(diào)用的情況。
        • 由構(gòu)造函數(shù)返回的對(duì)象就是 new 表達(dá)式的結(jié)果。如果構(gòu)造函數(shù)沒有顯式返回一個(gè)對(duì)象,則使用步驟1創(chuàng)建的對(duì)象。
        • 一般情況下,構(gòu)造函數(shù)不返回值,但是用戶可以選擇主動(dòng)返回對(duì)象,來覆蓋正常的對(duì)象創(chuàng)建步驟
        function Ctor(){
            ....
        }

        function myNew(ctor,...args){
            if(typeof ctor !== 'function'){
              throw 'myNew function the first param must be a function';
            }
            var newObj = Object.create(ctor.prototype); //創(chuàng)建一個(gè)繼承自ctor.prototype的新對(duì)象
            var ctorReturnResult = ctor.apply(newObj, args); //將構(gòu)造函數(shù)ctor的this綁定到newObj中
            var isObject = typeof ctorReturnResult === 'object' && ctorReturnResult !== null;
            var isFunction = typeof ctorReturnResult === 'function';
            if(isObject || isFunction){
                return ctorReturnResult;
            }
            return newObj;
        }

        let c = myNew(Ctor);

        7.instanceof的實(shí)現(xiàn)

        • instanceof 是用來判斷A是否為B的實(shí)例,表達(dá)式為:A instanceof B,如果A是B的實(shí)例,則返回true,否則返回false。
        • instanceof 運(yùn)算符用來測(cè)試一個(gè)對(duì)象在其原型鏈中是否存在一個(gè)構(gòu)造函數(shù)的 prototype 屬性。
        • 不能檢測(cè)基本數(shù)據(jù)類型,在原型鏈上的結(jié)果未必準(zhǔn)確,不能檢測(cè)null,undefined
        • 實(shí)現(xiàn):遍歷左邊變量的原型鏈,直到找到右邊變量的 prototype,如果沒有找到,返回 false
        function myInstanceOf(a,b){
            let left = a.__proto__;
            let right = b.prototype;
            while(true){
                if(left == null){
                    return false
                }
                if(left == right){
                    return true
                }
                left = left.__proto__
            }
        }

        //instanceof 運(yùn)算符用于判斷構(gòu)造函數(shù)的 prototype 屬性是否出現(xiàn)在對(duì)象的原型鏈中的任何位置。
        function myInstanceof(left, right) {
            let proto = Object.getPrototypeOf(left), // 獲取對(duì)象的原型
            prototype = right.prototype; // 獲取構(gòu)造函數(shù)的 prototype 對(duì)象
            // 判斷構(gòu)造函數(shù)的 prototype 對(duì)象是否在對(duì)象的原型鏈上
            while (true) {
                if (!proto) return false;
                if (proto === prototype) return true;
                proto = Object.getPrototypeOf(proto);
            }
        }

        8.Object.create()的實(shí)現(xiàn)

        • MDN文檔
        • Object.create()會(huì)將參數(shù)對(duì)象作為一個(gè)新創(chuàng)建的空對(duì)象的原型, 并返回這個(gè)空對(duì)象
        //簡(jiǎn)略版
        function myCreate(obj){
            // 新聲明一個(gè)函數(shù)
            function C(){};
            // 將函數(shù)的原型指向obj
            C.prototype = obj;
            // 返回這個(gè)函數(shù)的實(shí)力化對(duì)象
            return new C()
        }
        //官方版Polyfill
        if (typeof Object.create !== "function") {
            Object.create = function (proto, propertiesObject) {
                if (typeof proto !== 'object' && typeof proto !== 'function') {
                    throw new TypeError('Object prototype may only be an Object: ' + proto);
                } else if (proto === null) {
                    throw new Error("This browser's implementation of Object.create is a shim and doesn't support 'null' as the first argument.");
                }

                if (typeof propertiesObject !== 'undefined'throw new Error("This browser's implementation of Object.create is a shim and doesn't support a second argument.");

                function F() {}
                F.prototype = proto;

                return new F();
            };
        }

        9.實(shí)現(xiàn) Object.assign

        Object.assign2 = function(target, ...source) {
            if (target == null) {
                throw new TypeError('Cannot convert undefined or null to object')
            }
            let ret = Object(target) 
            source.forEach(function(obj) {
                if (obj != null) {
                    for (let key in obj) {
                        if (obj.hasOwnProperty(key)) {
                            ret[key] = obj[key]
                        }
                    }
                }
            })
            return ret
        }

        10.Promise的實(shí)現(xiàn)

        實(shí)現(xiàn) Promise 需要完全讀懂 Promise A+ 規(guī)范,不過從總體的實(shí)現(xiàn)上看,有如下幾個(gè)點(diǎn)需要考慮到:

        • Promise本質(zhì)是一個(gè)狀態(tài)機(jī),且狀態(tài)只能為以下三種:Pending(等待態(tài))、Fulfilled(執(zhí)行態(tài))、Rejected(拒絕態(tài)),狀態(tài)的變更是單向的,只能從Pending -> Fulfilled 或 Pending -> Rejected,狀態(tài)變更不可逆
        • then 需要支持鏈?zhǔn)秸{(diào)用
        class Promise {
            callbacks = [];
            state = 'pending';//增加狀態(tài)
            value = null;//保存結(jié)果
            constructor(fn) {
                fn(this._resolve.bind(this), this._reject.bind(this));
            }
            then(onFulfilled, onRejected) {
                return new Promise((resolve, reject) => {
                    this._handle({
                        onFulfilled: onFulfilled || null,
                        onRejected: onRejected || null,
                        resolve: resolve,
                        reject: reject
                    });
                });
            }
            _handle(callback) {
                if (this.state === 'pending') {
                    this.callbacks.push(callback);
                    return;
                }
         
                let cb = this.state === 'fulfilled' ? callback.onFulfilled : callback.onRejected;
         
                if (!cb) {//如果then中沒有傳遞任何東西
                    cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;
                    cb(this.value);
                    return;
                }
         
                let ret = cb(this.value);
                cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;
                cb(ret);
            }
            _resolve(value) {
         
                if (value && (typeof value === 'object' || typeof value === 'function')) {
                    var then = value.then;
                    if (typeof then === 'function') {
                        then.call(value, this._resolve.bind(this), this._reject.bind(this));
                        return;
                    }
                }
         
                this.state = 'fulfilled';//改變狀態(tài)
                this.value = value;//保存結(jié)果
                this.callbacks.forEach(callback => this._handle(callback));
            }
            _reject(error) {
                this.state = 'rejected';
                this.value = error;
                this.callbacks.forEach(callback => this._handle(callback));
            }
        }

        Promise.resolve

        • Promsie.resolve(value) 可以將任何值轉(zhuǎn)成值為 value 狀態(tài)是 fulfilled 的 Promise,但如果傳入的值本身是 Promise 則會(huì)原樣返回它。
        Promise.resolve(value) {
          if (value && value instanceof Promise) {
            return value;
          } else if (value && typeof value === 'object' && typeof value.then === 'function') {
            let then = value.then;
            return new Promise(resolve => {
              then(resolve);
            });
          } else if (value) {
            return new Promise(resolve => resolve(value));
          } else {
            return new Promise(resolve => resolve());
          }
        }

        Promise.reject

        • 和 Promise.resolve() 類似,Promise.reject() 會(huì)實(shí)例化一個(gè) rejected 狀態(tài)的 Promise。但與 Promise.resolve() 不同的是,如果給 Promise.reject() 傳遞一個(gè) Promise 對(duì)象,則這個(gè)對(duì)象會(huì)成為新 Promise 的值。
        Promise.reject = function(reason) {
            return new Promise((resolve, reject) => reject(reason))
        }

        Promise.all

        • 傳入的所有 Promsie 都是 fulfilled,則返回由他們的值組成的,狀態(tài)為 fulfilled 的新 Promise;
        • 只要有一個(gè) Promise 是 rejected,則返回 rejected 狀態(tài)的新 Promsie,且它的值是第一個(gè) rejected 的 Promise 的值;
        • 只要有一個(gè) Promise 是 pending,則返回一個(gè) pending 狀態(tài)的新 Promise;
        Promise.all = function(promiseArr) {
            let index = 0, result = []
            return new Promise((resolve, reject) => {
                promiseArr.forEach((p, i) => {
                    Promise.resolve(p).then(val => {
                        index++
                        result[i] = val
                        if (index === promiseArr.length) 
        {
                            resolve(result)
                        }
                    }, err => {
                        reject(err)
                    })
                })
            })
        }

        Promise.race

        • Promise.race 會(huì)返回一個(gè)由所有可迭代實(shí)例中第一個(gè) fulfilled 或 rejected 的實(shí)例包裝后的新實(shí)例。
        Promise.race = function(promiseArr) {
            return new Promise((resolve, reject) => {
                promiseArr.forEach(p => {
                    Promise.resolve(p).then(val => {
                        resolve(val)
                    }, err => {
                        rejecte(err)
                    })
                })
            })
        }

        11.Ajax的實(shí)現(xiàn)

        function ajax(url,method,body,headers){
            return new Promise((resolve,reject)=>{
                let req = new XMLHttpRequest();
                req.open(methods,url);
                for(let key in headers){
                    req.setRequestHeader(key,headers[key])
                }
                req.onreadystatechange(()=>{
                    if(req.readystate == 4){
                        if(req.status >= '200' && req.status <= 300){
                            resolve(req.responeText)
                        }else{
                            reject(req)
                        }
                    }
                })
                req.send(body)
            })
        }

        12.實(shí)現(xiàn)防抖函數(shù)(debounce)

        • 連續(xù)觸發(fā)在最后一次執(zhí)行方法,場(chǎng)景:輸入框匹配
        let debounce = (fn,time = 1000) => {
            let timeLock = null

            return function (...args)
        {
                clearTimeout(timeLock)
                timeLock = setTimeout(()=>{
                    fn(...args)
                },time)
            }
        }

        13.實(shí)現(xiàn)節(jié)流函數(shù)(throttle)

        • 在一定時(shí)間內(nèi)只觸發(fā)一次,場(chǎng)景:長(zhǎng)列表滾動(dòng)節(jié)流
        let throttle = (fn,time = 1000) => {
            let flag = true;

            return function (...args){
                if(flag){
                    flag = false;
                    setTimeout(()=>{
                        flag = true;
                        fn(...args)
                    },time)
                }
            }
        }

        14.深拷貝(deepclone)

        • 判斷類型,正則和日期直接返回新對(duì)象
        • 空或者非對(duì)象類型,直接返回原值
        • 考慮循環(huán)引用,判斷如果hash中含有直接返回hash中的值
        • 新建一個(gè)相應(yīng)的new obj.constructor加入hash
        • 遍歷對(duì)象遞歸(普通key和key是symbol情況)
        function deepClone(obj,hash = new WeakMap()){
            if(obj instanceof RegExp) return new RegExp(obj);
            if(obj instanceof Date) return new Date(obj);
            if(obj === null || typeof obj !== 'object'return obj;
            //循環(huán)引用的情況
            if(hash.has(obj)){
                return hash.get(obj)
            }
            //new 一個(gè)相應(yīng)的對(duì)象
            //obj為Array,相當(dāng)于new Array()
            //obj為Object,相當(dāng)于new Object()
            let constr = new obj.constructor();
            hash.set(obj,constr);
            for(let key in obj){
                if(obj.hasOwnProperty(key)){
                    constr[key] = deepClone(obj[key],hash)
                }
            }
            //考慮symbol的情況
            let symbolObj = Object.getOwnPropertySymbols(obj)
            for(let i=0;i<symbolObj.length;i++){
                if(obj.hasOwnProperty(symbolObj[i])){
                    constr[symbolObj[i]] = deepClone(obj[symbolObj[i]],hash)
                }
            }
            return constr
        }

        15.數(shù)組扁平化的實(shí)現(xiàn)(flat)

        let arr = [1,2,[3,4,[5,[6]]]]
        console.log(arr.flat(Infinity))//flat參數(shù)為指定要提取嵌套數(shù)組的結(jié)構(gòu)深度,默認(rèn)值為 1
        //用reduce實(shí)現(xiàn)
        function fn(arr){
           return arr.reduce((prev,cur)=>{
              return prev.concat(Array.isArray(cur)?fn(cur):cur)
           },[])
        }

        16.函數(shù)柯里化

        function sumFn(a,b,c){return a+ b + c};
        let sum = curry(sumFn);
        sum(2)(3)(5)//10
        sum(2,3)(5)//10
        function curry(fn,...args){
          let fnLen = fn.length,
              argsLen = args.length;
          //對(duì)比函數(shù)的參數(shù)和當(dāng)前傳入?yún)?shù)
          //若參數(shù)不夠就繼續(xù)遞歸返回curry
          //若參數(shù)夠就調(diào)用函數(shù)返回相應(yīng)的值
          if(fnLen > argsLen){
            return function(...arg2s){
              return curry(fn,...args,...arg2s)
            }
          }else{
            return fn(...args)
          }
        }

        17.使用閉包實(shí)現(xiàn)每隔一秒打印 1,2,3,4

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

        18.手寫一個(gè) jsonp

        const jsonp = function (url, data) {
            return new Promise((resolve, reject) => {
                // 初始化url
                let dataString = url.indexOf('?') === -1 ? '?' : ''
                let callbackName = `jsonpCB_${Date.now()}`
                url += `${dataString}callback=${callbackName}`
                if (data) {
                    // 有請(qǐng)求參數(shù),依次添加到url
                    for (let k in data) {
                        url += `${k}=${data[k]}`
                    }
                }
                let jsNode = document.createElement('script')
                jsNode.src = url
                // 觸發(fā)callback,觸發(fā)后刪除js標(biāo)簽和綁定在window上的callback
                window[callbackName] = result => {
                    delete window[callbackName]
                    document.body.removeChild(jsNode)
                    if (result) {
                        resolve(result)
                    } else {
                        reject('沒有返回?cái)?shù)據(jù)')
                    }
                }
                // js加載異常的情況
                jsNode.addEventListener('error', () => {
                    delete window[callbackName]
                    document.body.removeChild(jsNode)
                    reject('JavaScript資源加載失敗')
                }, false)
                // 添加js節(jié)點(diǎn)到document上時(shí),開始請(qǐng)求
                document.body.appendChild(jsNode)
            })
        }
        jsonp('http://192.168.0.103:8081/jsonp', {
            a: 1,
            b: 'heiheihei'
        })
        .then(result => {
            console.log(result)
        })
        .catch(err => {
            console.error(err)
        })

        19.手寫一個(gè)觀察者模式

        class Subject{
          constructor(name){
            this.name = name
            this.observers = []
            this.state = 'XXXX'
          }
          // 被觀察者要提供一個(gè)接受觀察者的方法
          attach(observer){
            this.observers.push(observer)
          }

          // 改變被觀察著的狀態(tài)
          setState(newState){
            this.state = newState
            this.observers.forEach(o=>{
              o.update(newState)
            })
          }
        }

        class Observer{
          constructor(name){
            this.name = name
          }

          update(newState){
            console.log(`${this.name}say:${newState}`)
          }
        }

        // 被觀察者 燈
        let sub = new Subject('燈')
        let mm = new Observer('小明')
        let jj = new Observer('小健')
         
        // 訂閱 觀察者
        sub.attach(mm)
        sub.attach(jj)
         
        sub.setState('燈亮了來電了')

        20.EventEmitter 實(shí)現(xiàn)

        class EventEmitter {
            constructor() {
                this.events = {};
            }
            on(event, callback) {
                let callbacks = this.events[event] || [];
                callbacks.push(callback);
                this.events[event] = callbacks;
                return this;
            }
            off(event, callback) {
                let callbacks = this.events[event];
                this.events[event] = callbacks && callbacks.filter(fn => fn !== callback);
                return this;
            }
            emit(event, ...args) {
                let callbacks = this.events[event];
                callbacks.forEach(fn => {
                    fn(...args);
                });
                return this;
            }
            once(event, callback) {
                let wrapFun = function (...args) {
                    callback(...args);
                    this.off(event, wrapFun);
                };
                this.on(event, wrapFun);
                return this;
            }
        }

        21.生成隨機(jī)數(shù)的各種方法?

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

        22.如何實(shí)現(xiàn)數(shù)組的隨機(jī)排序?

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

        23.寫一個(gè)通用的事件偵聽器函數(shù)。

        const EventUtils = {
          // 視能力分別使用dom0||dom2||IE方式 來綁定事件
          // 添加事件
          addEvent: function(element, type, handler) {
            if (element.addEventListener) {
              element.addEventListener(type, handler, false);
            } else if (element.attachEvent) {
              element.attachEvent("on" + type, handler);
            } else {
              element["on" + type] = handler;
            }
          },
          // 移除事件
          removeEvent: function(element, type, handler) {
            if (element.removeEventListener) {
              element.removeEventListener(type, handler, false);
            } else if (element.detachEvent) {
              element.detachEvent("on" + type, handler);
            } else {
              element["on" + type] = null;
            }
          },
         // 獲取事件目標(biāo)
          getTarget: function(event) {
            return event.target || event.srcElement;
          },
          // 獲取 event 對(duì)象的引用,取到事件的所有信息,確保隨時(shí)能使用 event
          getEvent: function(event) {
            return event || window.event;
          },
         // 阻止事件(主要是事件冒泡,因?yàn)?nbsp;IE 不支持事件捕獲)
          stopPropagation: function(event) {
            if (event.stopPropagation) {
              event.stopPropagation();
            } else {
              event.cancelBubble = true;
            }
          },
          // 取消事件的默認(rèn)行為
          preventDefault: function(event) {
            if (event.preventDefault) {
              event.preventDefault();
            } else {
              event.returnValue = false;
            }
          }
        };

        24.使用迭代的方式實(shí)現(xiàn) flatten 函數(shù)。

        var arr = [123, [45], [6, [7, [8]]]]
        /** * 使用遞歸的方式處理 * wrap 內(nèi)保
        存結(jié)果 ret * 返回一個(gè)遞歸函數(shù) **/

        function wrap() {
            var ret = [];
            return function flat(a) {
                for (var item of
                    a) {
                        if (item.constructor === Array) {
                            ret.concat(flat(item))
                        } else {
                            ret.push(item)
                        }
                }
                return ret
            }

        console.log(wrap()(arr));

        25.怎么實(shí)現(xiàn)一個(gè)sleep

        • sleep函數(shù)作用是讓線程休眠,等到指定時(shí)間在重新喚起。
        function sleep(delay) {
          var start = (new Date()).getTime();
          while ((new Date()).getTime() - start < delay) {
            continue;
          }
        }

        function test() {
          console.log('111');
          sleep(2000);
          console.log('222');
        }

        test()

        26.實(shí)現(xiàn)正則切分千分位(10000 => 10,000)

        //無小數(shù)點(diǎn)
        let num1 = '1321434322222'
        num1.replace(/(\d)(?=(\d{3})+$)/g,'$1,')
        //有小數(shù)點(diǎn)
        let num2 = '342243242322.3432423'
        num2.replace(/(\d)(?=(\d{3})+\.)/g,'$1,')

        27.對(duì)象數(shù)組去重

        輸入:
        [{a:1,b:2,c:3},{b:2,c:3,a:1},{d:2,c:2}]
        輸出:
        [{a:1,b:2,c:3},{d:2,c:2}]
        • 首先寫一個(gè)函數(shù)把對(duì)象中的key排序,然后再轉(zhuǎn)成字符串
        • 遍歷數(shù)組利用Set將轉(zhuǎn)為字符串后的對(duì)象去重
        function objSort(obj){
            let newObj = {}
            //遍歷對(duì)象,并將key進(jìn)行排序
            Object.keys(obj).sort().map(key => {
                newObj[key] = obj[key]
            })
            //將排序好的數(shù)組轉(zhuǎn)成字符串
            return JSON.stringify(newObj)
        }

        function unique(arr){
            let set = new Set();
            for(let i=0;i<arr.length;i++){
                let str = objSort(arr[i])
                set.add(str)
            }
            //將數(shù)組中的字符串轉(zhuǎn)回對(duì)象
            arr = [...set].map(item => {
                return JSON.parse(item)
            })
            return arr
        }

        28.解析 URL Params 為對(duì)象

        let url = 'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled';
        parseParam(url)
        /* 結(jié)果
        { user: 'anonymous',
          id: [ 123, 456 ], // 重復(fù)出現(xiàn)的 key 要組裝成數(shù)組,能被轉(zhuǎn)成數(shù)字的就轉(zhuǎn)成數(shù)字類型
          city: '北京', // 中文需解碼
          enabled: true, // 未指定值得 key 約定為 true
        }
        */

        function parseParam(url) {
          const paramsStr = /.+\?(.+)$/.exec(url)[1]; // 將 ? 后面的字符串取出來
          const paramsArr = paramsStr.split('&'); // 將字符串以 & 分割后存到數(shù)組中
          let paramsObj = {};
          // 將 params 存到對(duì)象中
          paramsArr.forEach(param => {
            if (/=/.test(param)) { // 處理有 value 的參數(shù)
              let [key, val] = param.split('='); // 分割 key 和 value
              val = decodeURIComponent(val); // 解碼
              val = /^\d+$/.test(val) ? parseFloat(val) : val; // 判斷是否轉(zhuǎn)為數(shù)字

              if (paramsObj.hasOwnProperty(key)) { // 如果對(duì)象有 key,則添加一個(gè)值
                paramsObj[key] = [].concat(paramsObj[key], val);
              } else { // 如果對(duì)象沒有這個(gè) key,創(chuàng)建 key 并設(shè)置值
                paramsObj[key] = val;
              }
            } else { // 處理沒有 value 的參數(shù)
              paramsObj[param] = true;
            }
          })

          return paramsObj;
        }

        29.模板引擎實(shí)現(xiàn)

        let template = '我是{{name}},年齡{{age}},性別{{sex}}';
        let data = {
          name: '姓名',
          age: 18
        }
        render(template, data); // 我是姓名,年齡18,性別undefined
        function render(template, data) {
          const reg = /\{\{(\w+)\}\}/; // 模板字符串正則
          if (reg.test(template)) { // 判斷模板里是否有模板字符串
            const name = reg.exec(template)[1]; // 查找當(dāng)前模板里第一個(gè)模板字符串的字段
            template = template.replace(reg, data[name]); // 將第一個(gè)模板字符串渲染
            return render(template, data); // 遞歸的渲染并返回渲染后的結(jié)構(gòu)
          }
          return template; // 如果模板沒有模板字符串直接返回
        }

        30.轉(zhuǎn)化為駝峰命名

        var s1 = "get-element-by-id"
        // 轉(zhuǎn)化為 getElementById
        var f = function(s) {
            return s.replace(/-\w/g, function(x) {
                return x.slice(1).toUpperCase();
            })
        }

        31.查找字符串中出現(xiàn)最多的字符和個(gè)數(shù)

        • 例: abbcccddddd -> 字符最多的是d,出現(xiàn)了5次
        let str = "abcabcabcbbccccc";
        let num = 0;
        let char = '';

         // 使其按照一定的次序排列
        str = str.split('').sort().join('');
        // "aaabbbbbcccccccc"

        // 定義正則表達(dá)式
        let re = /(\w)\1+/g;
        str.replace(re,($0,$1) => {
            if(num < $0.length){
                num = $0.length;
                char = $1;        
            }
        });
        console.log(`字符最多的是${char},出現(xiàn)了${num}次`);

        32.圖片懶加載

        let imgList = [...document.querySelectorAll('img')]
        let length = imgList.length

        const imgLazyLoad = function() {
            let count = 0
            return (function() {
                let deleteIndexList = []
                imgList.forEach((img, index) => {
                    let rect = img.getBoundingClientRect()
                    if (rect.top < window.innerHeight) {
                        img.src = img.dataset.src
                        deleteIndexList.push(index)
                        count++
                        if (count === length) {
                            document.removeEventListener('scroll', imgLazyLoad)
                        }
                    }
                })
                imgList = imgList.filter((img, index) => !deleteIndexList.includes(index))
            })()
        }

        // 這里最好加上防抖處理
        document.addEventListener('scroll', imgLazyLoad)

        參考資料


        作者:xpsilvester

        原文鏈接:https://juejin.cn/post/6963167124881670152


        內(nèi)推社群


        我組建了一個(gè)氛圍特別好的騰訊內(nèi)推社群,如果你對(duì)加入騰訊感興趣的話(后續(xù)有計(jì)劃也可以),我們可以一起進(jìn)行面試相關(guān)的答疑、聊聊面試的故事、并且在你準(zhǔn)備好的時(shí)候隨時(shí)幫你內(nèi)推。下方加 winty 好友回復(fù)「面試」即可。


        瀏覽 46
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            国产精品高潮呻呤欠久A片 | 泰剧19禁啪啪无遮挡大尺度 | 欧美精品老牛影视 | 国产一区二区四区 | 午夜色综合 | 国产女人18水真多毛片18精品 | 东京热自拍 | 国产一级操逼视频片 | 亚洲美女黄片 | 色欲熟妇 |