前端常見面試題(七)@郝晨光


簡述同步和異步的區(qū)別

眾所周知,javascript是單線程的語言,所謂的單線程,就是從上至下,依次執(zhí)行,當(dāng)然這里的依次執(zhí)行要拋開javascript的預(yù)解析機制。
這樣做的原因是因為javascript最初是為了操作DOM,運行在瀏覽器環(huán)境下的,而操作DOM的時候,不能是異步的,不然的話兩個異步任務(wù)同時修改DOM結(jié)構(gòu)的話,會導(dǎo)致瀏覽器不知道該執(zhí)行哪一個。
但是這樣做也有缺點,當(dāng)遇到一個響應(yīng)時間特別長的任務(wù)時,容易導(dǎo)致頁面加載錯誤或者瀏覽器未響應(yīng)的情況。

同步就是所有的任務(wù)都處在同一隊列中,不可以插隊,一個任務(wù)執(zhí)行完接著開始執(zhí)行下一個,相對于瀏覽器而言,同步的效率過低,一些耗費時間比較長的任務(wù)應(yīng)該用異步來執(zhí)行。

異步就是將一個任務(wù)放入到異步隊列中,當(dāng)這個任務(wù)執(zhí)行完成之后,再從異步隊列中提取出來,插隊到同步隊列中,拿到異步任務(wù)的結(jié)果,可以提升代碼執(zhí)行的效率,不需要因為一個耗費時長的代碼而一直等待。

javascript異步的幾種實現(xiàn)方式
  1. 回調(diào)函數(shù) callback
    最簡單的回調(diào)函數(shù)的實現(xiàn)方式,利用setTimeout會進(jìn)入計時器隊列,而不是正常隊列的特性,來實現(xiàn)回調(diào)函數(shù)
    計時器隊列與正常隊列同時運行,不影響正常隊列的運行,在正常隊列空閑的時候,并且計時器隊列的任務(wù)倒計時已經(jīng)結(jié)束,就將計時器隊列的任務(wù)提取到正常隊列中獲得最終結(jié)果。

    • function syncMethod(callback) {
        setTimeout(function() {
            callback(count);
            console.log('第一次調(diào)用回調(diào)函數(shù)!');
            setTimeout(function() {
                callback(count);
                console.log('第二次調(diào)用回調(diào)函數(shù)!');
            })
            for(let i=0;i<100;i++) {
                count++;
            }
        }, 0);
        let count = 1;
        for(let i=0;i<100;i++) {
            count++;
        }
      }
      syncMethod(function(count) {
          console.log(count); // 調(diào)用兩次,第一次返回101,第二次返回201
      })
      
      callback示例返回值
  2. 事件驅(qū)動

    • javascript是基于事件驅(qū)動的語言,當(dāng)我們給元素綁定事件的時候,并不會立即執(zhí)行,而是當(dāng)觸發(fā)指定的事件時,才會執(zhí)行對應(yīng)的方法
    •  const btn = document.querySelector('button');
       btn.addEventlistener('click', function() {
           console.log('clickBtn')
       })
      
  3. 發(fā)布者訂閱者模式(觀察者模式)

    • 發(fā)布者訂閱者模式是通過保存事件,然后在需要使用的時候直接發(fā)布事件,就可以觸發(fā)保存的回調(diào)
    •   function Watcher() {
            this.events = {}; // 定義事件對象,用來保存需要觸發(fā)的事件
        };
        // $on方法,用來監(jiān)聽事件,訂閱者
        Watcher.prototype.$on = function(event, handler) {
            this.events[event] = this.events[event] || []; 
            // 如果當(dāng)前事件已經(jīng)被監(jiān)聽,則在原基礎(chǔ)上進(jìn)行添加新的事件,如果沒有的話新建一個數(shù)組,用來存儲事件
            this.events[event].push({
                handler: handler
            })
            // 將事件存儲到對應(yīng)的事件模型中
        };
        // $emit方法,用來發(fā)布事件,發(fā)布者
        Watcher.prototype.$emit = function(event,...arg) {
           // 如果沒有訂閱當(dāng)前事件的話,直接返回
            if(!this.events[event]) {
                return
            }
            // 循環(huán)遍歷對應(yīng)事件,將對應(yīng)的事件根據(jù)填入的先后順序觸發(fā)
            for(let i = 0; i< this.events[event].length; i++) {
                this.events[event][i].handler(...arg)
            }
        };
        // 關(guān)閉監(jiān)聽事件
        Watcher.prototype.$close = function(event) {
            // 如果沒有訂閱當(dāng)前事件的話,直接返回
            if(!this.events[event]) {
                return
            }
            // 刪除訂閱事件
            delete this.events[event];
        };
        // 實例化一個觀察者
        let watcher= new Watcher();
        // 監(jiān)聽事件,并傳入事件需要觸發(fā)的函數(shù)
        watcher.$on('handler1', function(name, age) {
            console.log(name, age, '我是第一個函數(shù)')
        });
        // 監(jiān)聽不同的事件
        watcher.$on('handler2', function(sex) {
            console.log(sex)
        });
        // 監(jiān)聽同一個事件,觸發(fā)不同的函數(shù)
        watcher.$on('handler1', function(name, age) {
            console.log(name, age, '我是第二個函數(shù)')
        });
        // 發(fā)布事件,并傳參
        watcher.$emit('handler2', "男");
        watcher.$emit('handler1', '張三',20);
        // 關(guān)閉事件
        watcher.$close('handler1');
        // 事件不會觸發(fā),因為已經(jīng)關(guān)閉了對應(yīng)事件的監(jiān)聽
        watcher.$emit('handler1', '張三',20);
      
      Image 3.jpg
  4. promise 異步編程的解決方案

    • Promise是ES6新增的異步編程的解決方案,它有三種狀態(tài)分別是pending進(jìn)行中、resolved已完成、rejected已失敗。
    • Promise可以鏈?zhǔn)秸{(diào)用,避免層層嵌套,異步操作更加容易方便,提升代碼可讀性。
    • Promise一旦創(chuàng)建就不可以取消,一旦在resolvedrejected中確立一種狀態(tài),就不可以再發(fā)生改變
    •  function asynMethod(flag) {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    if(flag) {
                        resolve('郝晨光精心寫作,如有錯誤請指出,謝謝!');
                    }else {
                        reject('錯誤信息!')
                    }
                }, 3000)
            })
        }
        asynMethod(true).then(data => {
            console.log(data);
            return asynMethod(true)
        }).then(data => {
            console.log(data);
            return asynMethod(false)
        }).catch(err =>{
            console.log(err);
        })
      



數(shù)組去重(手寫代碼)

  1. 使用ES6的Set去重
    • Set是ES6新增的數(shù)據(jù)類型,Set 的成員具有唯一性
    • function distinct(arr) {
        return Array.from(new Set(arr));
      }      
      
  2. 使用ES6的Set去重(超級簡化版)
    • [...new Set(arr)]   // [...new Set(需要去重的數(shù)組)]
      
  3. 使用splice配合兩重for循環(huán)去重
    • function distinct(arr) {
        for(let i = 0; i < arr.length; i++) {
            for(let j = i + 1; j < arr.length; j++) {
                if(arr[i] === arr[j]) {
                    arr.splice(j, 1);
                    j--;
                }
            }
        }
        return arr;
      }
      
  4. 使用for循環(huán)配合indexOf去重
    •  function distinct(arr) {
           let newArr = [];
           for(let i = 0; i < arr.length; i++) {
               if(newArr.indexOf(arr[i]) === -1) {
                   newArr.push(arr[i]);
               }
           }
           return newArr;
      }
      
  5. 使用for循環(huán)配合sort排序去重
    • function distinct(arr) {
            arr = arr.sort();
            let newArr = [];
            for(let i = 0; i < arr.length; i++) {
                if(arr[i] !== arr[i-1]) {
                    newArr.push(arr[i]);
                }
            }
            return newArr;
        }
      
  6. 使用for循環(huán)配合includes去重
    •   function distinct(arr) {
            let newArr = [];
            for(let i = 0; i < arr.length; i++) {
                if(!newArr.includes(arr[i])) {
                    newArr.push(arr[i]);
                }
            }
            return newArr;
       }
      
  7. 使用filter配合indexOf去重
    • function distinct(arr) {
            return arr.filter((item,index, arr) => arr.indexOf(item) === index);
      }
      
  8. 使用reduce去重
    • function distinct(arr) {
            return arr.reduce((prev,next) => {
                if (!prev.includes(next)) {
                  prev.push(next)
                }
                return prev
            }, []);
      }
      



在JavaScript中什么是偽數(shù)組?如何將偽數(shù)組轉(zhuǎn)化為標(biāo)準(zhǔn)數(shù)組

javascript中的偽數(shù)組(類數(shù)組):不具有數(shù)組的push,pop等方法,但是具有l(wèi)ength,以及可以利用for循環(huán)遍歷等特性,例如函數(shù)的 arguments 參數(shù)集合,還有通過document.getElementsByTagName等方法獲取的NodeList等都是類數(shù)組

如何將偽數(shù)組轉(zhuǎn)化為標(biāo)準(zhǔn)數(shù)組

  1.   let nodeList = document.getElementsByTagName('li');
      let nodeArr = Array.from(nodeList);
    
  2.    let nodeList = document.getElementsByTagName('li');
       let nodeArr = Array.prototype.slice.call(nodeList);
    

當(dāng)轉(zhuǎn)化為標(biāo)準(zhǔn)數(shù)組以后,就可以調(diào)用數(shù)組實例上的方法了,如push、pop、splice、filter、map、forEach等等



SPA路由history模式,打包上線都遇到了哪些問題?你是如何解決的?

  1. 資源路徑404問題
    • vue-cli3環(huán)境下,在根目錄新建vue.config.js,在該文件中寫入如下
    •  module.exports = {
           publicPath: './'
       }
      
  2. 頁面刷新404問題



JavaScript中callee和caller的作用

  1. callee是函數(shù)arguments對象內(nèi)的指針,它指向當(dāng)前的函數(shù),使得在函數(shù)內(nèi)部遞歸調(diào)用當(dāng)前函數(shù)時,不需要調(diào)用函數(shù)名稱,減少函數(shù)內(nèi)部對于函數(shù)名的依賴
    function test() {
        console.log(arguments.callee);
    }
    test();
    
  2. caller是函數(shù)的一個屬性,它指向調(diào)用當(dāng)前函數(shù)的函數(shù),如果當(dāng)前函數(shù)在其他函數(shù)內(nèi)被調(diào)用,則返回調(diào)用它的那個函數(shù),如果是在全局環(huán)境下被調(diào)用,則返回 null
    我們可以利用caller的特性跟蹤函數(shù)的調(diào)用鏈
    function fn1() {
        console.log(fn1.caller);
        console.log(arguments.callee.caller);
    }
    function fn2() {
        fn1()
    }
    fn2();
    



本文 CSDN 地址: 前端常見面試題(七)@郝晨光

結(jié)言
感謝您的查閱,本文由郝晨光整理并總結(jié),代碼冗余或者有錯誤的地方望不吝賜教;菜鳥一枚,請多關(guān)照
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容