前端拾遺-JavaScript-手寫題和基礎

寫在最前面

  • 本篇介紹出現頻率較高的 JavaScript 手寫相關的基礎題目,JavaScript 基礎很是重要。

JavaScript 基礎手寫題目

導航

  • 一、變量提高和函數提高
  • 二、閉包
  • 三、模糊搜索和防抖和節流
  • 四、call, apply, bind
  • 五、javascript 設計模式考察
  • 六、JavaScript 鏈式調用和異步處理
  • 七、JavaScript 算法-遞歸的思想

一、變量提高和 ES6

  • 說說箭頭函數和普通函數的區別,能講講其中 this 的使用嗎?
  • fn() 輸出什麼?,若是咱們使用 let 會發生什麼,爲何?
function fn() {
    console.log('a', a);
    var a = 1; // let a = 1;
    function a () {
        console.log('I am a function');
    }
}
fn() 
// ƒ a () {console.log('I am a function');}
複製代碼

keywords: 變量提高函數提高 && let const 和 var 的區別 && 箭頭函數和普通函數的區別javascript

二、閉包問題

  • 什麼是閉包?使用場景
  • 閉包就是:可以讀取其餘函數內部變量的函數參考阮一峯老師的博客
  • 閉包的做用:能夠讀取函數內部的變量,和****
for(var i=0;i<10;i++){

    setTimeout(function(){

        console.log(i)//10個10

    },1000) 

 }
 // 輸出的什麼?Q&A 10 個 10

複製代碼

若是想輸出 0-9 能夠怎麼作?html

for(var i=0;i<10;i++){
    ((j)=>{

        setTimeout(function(){

            console.log(j)//10個10

        },1000) 
    })(i)
}
複製代碼

三、使用 JavaScript 寫一個模糊搜索

  • 咱們在考慮一個搜索框須要注意哪些部分?
function filterMap(arr: any[],value:string){
	return arr.filter(item => String(item).includes(value))
}
複製代碼

什麼是防抖

原理是利用閉包,外部使用內部的值。 防抖就是限制必定的時間才觸發的函數,好比搜索框input,用戶輸入的時候不能一輸入就觸發函數,等必定的時間(好比 1s)才觸發相關的函數。前端

什麼是節流

原理同上,利用閉包。 節流就是在規定時間內只執行一次,好比頁面 scrolljava

// 設置一個 timer = null, 傳入 time 爲
// setTimeout的函數的時間,返回一個函數,當 timer 存在的時候clearTimeout

// 防抖
function debounce(fn,time){
    let timer = null;
    return function(){
        if(timer){
            clearTimeout(timer)
        }
        timer = setTimeout(()=>{
            fn.apply(this,arguments)
        },time)
    }
}


// 設置一個 canRun = true,傳入 fn 和 time
// 一個閉包的函數,先設置 canRun = false,直接執行setTimeout在其中把 canRun 置爲 true,如今又能夠執行了

function throttle(fn,time){
    let canRun = true;
    return function(){
        if(!canRun){
            return
        }
        canRun = false;
        setTimeout(() => {
            fn.apply(this,arguments);
            canRun = true;
        },time)
    }
}
複製代碼

四、call,bind,apply

  • 咱們通常使用什麼判斷 JS 對象類型(typeof類型檢測,instanceof 原型檢測,constructor 構造函數檢測, Object.prototype.toString.call([]))?
  • 提到了call,call,bind,apply講一下啊能簡單的說一下他們的使用和區別嗎?
    • call、apply、bind的做用是改變函數運行時this的指向。
    • 區別:bind返回對應函數, 便於稍後調用; apply, call則是當即調用;call 接收參數對象,apply 接收參數數組。
  • 手寫一個 call,apply,和bind?
//call 重寫
Function.prototype.MyCall = function(context, ...args){
    context = context || window; // 由於傳遞過來的context有多是null
    context.fn = this; // 讓fn的上下文爲context
    const result = context.fn(...args);
    delete context.fn;
    return result; // 由於有可能this函數會有返回值return
}

//apply 重寫
Function.prototype.MyApply = function(context, arr: any[]){
    context = context || window; 
    context.fn = this; 
    const result = context.fn(...args);
    delete context.fn;
    return result; 
}

// bind 重寫
Function.prototype.MyBind = function(context, ...args){
    var fn = this; 
    return function () {
        fn.call(context, ...args, ...arguments);
    }
}
複製代碼
  • 理解上面的代碼(MyCall)
    • 1:把傳入的第一個參數做爲 call 函數內部的一個臨時對象 context;
    • 2:給 context 對象一個新屬性 fn , 我稱呼其爲實際執行函數 context.fn ;讓 this 關鍵字(僅僅是關鍵字,而不是this對象)指向這個屬性 ,即 context.fn = this ; 注意 : 在這裏的 this 對象指向的是調用call()函數的函數對象。
    • 3:傳入 args 而後執行 context.fn,返回的結果保存在 result 中。
    • 4:執行完成後再把 context.fn 刪除。返回執行 result

五、JavaScript 設計模式考察

先講講平時設計開發中經常使用的設計模式?react

  • 設計原理:單一責任原則(SRP),最少知識原則(LKP),開放-封閉原則(OCP)
  • 設計模式: 他的原則是「找出 程序中變化的地方,並將變化封裝起來」,它的關鍵是意圖,而不是結構。
  • 平時設計模式很是重要,這裏只記錄了兩種考察得比較多設計模式,其餘的還須要你們繼續複習。

單例模式

  • 保證一個類僅有一個實例,並提供一個訪問它的全局訪問點
function SetManager(name) {
    this.manager = name;
}

SetManager.prototype.getName = function() {
    console.log(this.manager);
};

var SingletonSetManager = (function() {
    var manager = null;

    return function(name) {
        if (!manager) {
            manager = new SetManager(name);
        }

        return manager;
    } 
})();

SingletonSetManager('a').getName(); // a
SingletonSetManager('b').getName(); // a
SingletonSetManager('c').getName(); // a
複製代碼

發佈-訂閱模式(觀察者模式)

  • 定義了對象間的一種一對多的依賴關係,當一個對象的狀態發 生改變時,全部依賴於它的對象都將獲得通知
// 觀察者
var observer = {
    // 訂閱集合
    subscribes: [],

    // 訂閱
    subscribe: function(type, fn) {
        if (!this.subscribes[type]) {
            this.subscribes[type] = [];
        }
        
        // 收集訂閱者的處理
        typeof fn === 'function' && this.subscribes[type].push(fn);
    },

    // 發佈 可能會攜帶一些信息發佈出去
    publish: function() {
        var type = [].shift.call(arguments),
            fns = this.subscribes[type];
        
        // 不存在的訂閱類型,以及訂閱時未傳入處理回調的
        if (!fns || !fns.length) {
            return;
        }
        
        // 挨個處理調用
        for (var i = 0; i < fns.length; ++i) {
            fns[i].apply(this, arguments);
        }
    },
    
    // 刪除訂閱
    remove: function(type, fn) {
        // 刪除所有
        if (typeof type === 'undefined') {
            this.subscribes = [];
            return;
        }

        var fns = this.subscribes[type];

        // 不存在的訂閱類型,以及訂閱時未傳入處理回調的
        if (!fns || !fns.length) {
            return;
        }

        if (typeof fn === 'undefined') {
            fns.length = 0;
            return;
        }

        // 挨個處理刪除
        for (var i = 0; i < fns.length; ++i) {
            if (fns[i] === fn) {
                fns.splice(i, 1);
            }
        }
    }
};
複製代碼

使用 emit,on 來現實這個類面試

class Event {
    constructor() {
        this.eventTypeObj = {}
        this.cacheObj = {}
    }
    on(eventType, fn) {
        if (!this.eventTypeObj[eventType]) {
            // 按照不一樣的訂閱事件類型,存儲不一樣的訂閱回調
            this.eventTypeObj[eventType] = []
        }
        this.eventTypeObj[eventType].push(fn)

        // 若是是先發布,則在訂閱者訂閱後,則根據發佈後緩存的事件類型和參數,執行訂閱者的回調
        if (this.cacheObj[eventType]) {
            var cacheList = this.cacheObj[eventType]
            for (var i = 0; i < cacheList.length; i++) {
                cacheList[i]()
            }
        }
    }
    emit() {
        // 能夠理解爲arguments借用shift方法
        var eventType = Array.prototype.shift.call(arguments)
        var args = arguments
        var that = this

        function cache() {
            if (that.eventTypeObj[eventType]) {
                var eventList = that.eventTypeObj[eventType]
                for (var i = 0; i < eventList.length; i++) {
                    eventList[i].apply(eventList[i], args)
                }
            }
        }
        if (!this.cacheObj[eventType]) {
            this.cacheObj[eventType] = []
        }

        // 若是先訂閱,則直接訂閱後發佈
        cache(args)

        // 若是先發布後訂閱,則把發佈的事件類型與參數保存起來,等到有訂閱後執行訂閱
        this.cacheObj[eventType].push(cache)
    }
}
複製代碼

六、JavaScript 鏈式調用和異步處理

  • 實現一個 lazyMan
    • LazyMan("Hank").eat("dinner").sleepFirst(5).sleep(2).eat("supper"); 打印以下
// Hank 
 // ...(延遲5s)
 // dinner
 // ... (延遲2s)
 // supper
複製代碼

》 實現如上函數算法

  • 重點在於 next,咱們把執行的函數放進 queue 中,在運行的時候 next(), 取出數組中的函數執行。
function _LazyMan(name){
    this.nama = name;
    this.queue = [];
    this.queue.push(() => {
        console.log("Hi! This is " + name + "!");
        this.next();
    })
    setTimeout(()=>{
        this.next()
    },0)
  }
  
_LazyMan.prototype.eat = function(name){
this.queue.push(() =>{
    console.log("Eat " + name + "~");
    this.next()
})
return this;
}

_LazyMan.prototype.next = function(){
var fn = this.queue.shift();
fn && fn();
}

_LazyMan.prototype.sleep = function(time){
this.queue.push(() =>{
    setTimeout(() => {
        console.log("Wake up after " + time + "s!");
        
        this.next()
    },time * 1000)
})
return this;
}

_LazyMan.prototype.sleepFirst = function(time){
this.queue.unshift(() =>{
    setTimeout(() => {
        console.log("Wake up after " + time + "s!");
        
        this.next()
    },time * 1000)
})
return this;
}

function LazyMan(name){
return new _LazyMan(name)
}

複製代碼

補充: 若是咱們使用 promise 怎麼實現如上的 lazyMan?設計模式

算法-遞歸

  • 一、實現一個有緩存的斐波拉契數列
var cache = { };
var count = 0;
function fib(n){
    count++;
    if(n === 1 || n === 2){
        return 1;
    }
    if(cache[n]){
        return cache[n];
    }else{
        var ret = fib(n - 1) + fib(n - 2);
        cache[n] = ret;
        return ret;
    }
}
複製代碼
  • 這個的時間複雜度是多少 O(2^n) 指數增加的複雜度很是高了?若是不用遞歸,還有什麼其餘方法嗎?
    • 迭代的思想, 用空間換時間:
function fibonacci(n) {
    var one = 1;
    var two = 1;
    for(var i = 3; i <= n; i++) {
       var three = one + two;
        one = two;
        two = three;

    }
    if (n==1||n==2) {
        return one;
    }
    return three;
}
複製代碼
  • 二、棋盤問題(一個機器人位於一個 m x n 網格的左上角 (起始點在圖中標記爲「Start」 )。 機器人每次只能向下或者向右移動一步。機器人試圖達到網格的右下角.跨域

  • 遞歸的思想數組

function robotPath(m,n) {
        if(m == 1 && n == 1) return 1;
        if(m == 1) return robotPath(m, n - 1);
        if(n == 1) return robotPath(m - 1, n);
        return robotPath(m-1, n) + robotPath(m, n - 1);
    }
複製代碼

待續後續補充 和更多 js 常見算法 promise 手寫相關的知識

外鏈

相關文章
相關標籤/搜索