常見的JS手寫函數彙總(代碼註釋、持續更新)

最近在複習面試中常見的JS手寫函數,順便進行代碼註釋和總結,方便本身回顧也加深記,內容也會陸陸續續進行補充和改善。面試

1、手寫深拷貝

    <script>
        const obj1 = {
            name: 'Leise',
            age: 23,
            address: {
                country: 'China',
                city: 'Guanzhou'
            }
        }
        const obj2 = deepClone(obj1)
        obj2.address.city = 'Hangzhou'
        console.log(obj1.address.city);
        console.log(obj2.address.city);
​
        function deepClone(obj) {
            //判斷是否爲非對象或者是null
            if (typeof obj !== 'object' || typeof pbj == null) {
                return obj
            }
            // 初始化拷貝結果
            let result
            // 判斷是數組仍是對象
            if (obj instanceof Array) {
                result = []
            } else {
                result = {}
            }
            for (let key in obj) {
             //保證key不是原型的屬性
                if (obj.hasOwnProperty(key)) {
             //  遞歸調用深拷貝 防止多重嵌套
                    result[key] = deepClone(obj[key])
                }
            }
            // 最後返回結果 
            return result
        }
    </script>
​

 

2、手寫bind函數

<script>
    Function.prototype.mybind = function () {
        //原型添加方法mybind
        const args = Array.prototype.slice.call(arguments)
        //將得到參數拆解成數組
        const t = args.shift()
        //獲取this(數組第一項,同時剔除第一項,shift能夠實現)
        const self = this
        //當前fn1.bind(...)中的fn1
        //console.log(this);
        //function fn1(a, b, c) {
        //console.log("this", this);
         //定義一個變量保留this指向
        //console.log(a, b, c);  }
        return function () {
            //console.log(this);window
            return self.apply(t, args)
            //這個函數須要執行
        }
    }
​
    function fn1(a, b, c) {
        console.log("this", this);
        console.log(a, b, c);
​
    }
    const fn2 = fn1.mybind({
        x: 1,
        y: 2
    }, 10, 20, )
    //bind以後須要執行
    const res = fn2()
    //保存運行後的結果並打印出來
    console.log(res);
</script>

 

3、手寫通用事件綁定函數

<script>
    function bindEvent(elem, type, selcetor, fn) {
        //判斷三個仍是四個,四個則是事件代理
        if (fn == null) {
            fn = selcetor
            selcetor = null
        }
        //開始綁定事件
        elem.addEventListener(type, event => {
            const target = event.target
            //判斷是事件代理仍是普通綁定
            if (selector) {
                //事件代理
                if (target.matches(selcetor)) {
                    fn.call(target, event)
                } else {
                    fn.call(target.event)
                }
            }
​
        })
    }
​
    function bindEvent(elem, type, selector, fn) {
        // 三個參數和四個參數的判斷處理
        if (fn == null) {
            fn = selector
            selector = null
        }
        elem.addEventListener(type, event => {
            const target = event.target
            if (selector) {
​
                // 有selector就是代理
if (target.matches(selector)) {
                    fn.call(target, event)
                }
            } else {
                fn.call(target, event)
            }
        })
    }
​
    const div3 = document.getElementById('div3')
    bindEvent(div3, 'click', 'a',
        function (event) {
            event.preventDefault()
            alert(this.innerHTML)
​
        })
</script>

 

4、手寫閉包的簡單應用

<script>
    function createCache() {
        // 閉包隱藏數據,只提供 API
        let obj = {}
        return {
            set: function (key, value) {
                obj[key] = value
            },
            get: function (key) {
                return obj[key]
            }
        }
    }
    const c = createCache()
    c.set('a', 100)
    console.log(c.get('a'))
    obj.d = 100 //undefined,必須使用get或者set
</script>

 

5、手寫promise

    <script>
        function loadImg(url) {
            return new Promise((resolve, reject) => {
                const img = document.createElement('img')
                img.src = url
                img.onload = () => {
                    resolve(img)
                }
                img.onerror = () => {
                    reject(new Error("圖片加載失敗"))
                }
            })
        }
        const url1 = 'http://img.mukewang.com/5e5c85e1000116c505400720-156-88.jpg'
        const url2 = 'https://img.mukewang.com/szimg/5dba8cee0969880506000338.jpg'
        loadImg(url1).then((img1) => {
            console.log(img1.width);
            return img1
        }).then((img1) => {
            console.log(img1.height);
            return loadImg(url2)
        }).then(img2 => {
            console.log(img2.width);
        })
    </script>

 

6、手寫Ajax

   <script>
        function ajax(url) {
            return new Promise((resolve, reject) => {
​
                //判斷谷歌等仍是IE
                let xhr
                if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari
                    xhr = new XMLHttpRequest()
​
                } else { // code for IE6, IE5
                    xhr = new ActiveXObject("Microsoft.XMLHTTP")
                }
                // xhr 具備一個 open 方法,這個方法的做用相似於初始化,並不會發起真正的請求
                // open 方法具備 5 個參數,可是經常使用的是前 3 個
                // method: 請求方式 —— get / post
                // url:請求的地址
                // async:是否異步請求,默認爲 true(異步)
                xhr.open("get", url, true)
                // send 方法發送請求,並接受一個可選參數
                // 當請求方式爲 post 時,能夠將請求體的參數傳入
                // 當請求方式爲 get 時,能夠不傳或傳入 null
                // 無論是 get 仍是 post,參數都須要經過 encodeURIComponent               編碼後拼接
                xhr.send(null)
                //當readyStatus的狀態發生改變時,會觸發 xhr 的事件onreadystatechange
                xhr.onreadystatechange = () => {
                    //readyStatus: 請求/響應過程的當前活動階段
                    if (xhr.readyState == 4) {
                    //HTTP 狀態在 200-300 之間表示請求成功
                   // HTTP 狀態爲 304 表示請求內容未發生改變,可直接從緩存中讀取
                        if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
​
                            let obj = JSON.parse(xhr.responseText)
                            // console.log(obj)
                            // callBack(obj)
                            resolve(obj)
                        } else {
                            reject(new Error("錯誤"))
                        }
                    }
                }
            })
        }
        const url1 = "http://jsonplaceholder.typicode.com/users"
        const url2 = "/a.json"
        const url3 = "/b.json"
​
        ajax(url1).then(res => {
            console.log(res);
            return ajax(url2)
        }).then(res => {
            console.log(res);
            return ajax(url3)
        }).then(() => {
            console.log("ok");
        }).catch((res) => {
            console.log(res) //設置一個統一出口 若是出現錯誤就終止
        })
    </script>

 

7、手寫防抖

   <script>
        const input1 = document.getElementById('input1')
​
        function debounce(fn, delay) {
            //timer必須在閉包中 纔不會被外面修改
            let timer = null
            return function () {
                if (timer) {
                    clearTimeout(timer)
                }
                timer = setTimeout(() => {
                    fn.apply(this, arguments)
                    timer = null
                }, delay);
            }
        }
        input1.addEventListener('keyup', debounce(function () {
            console.log(input1.value);
        }, 500))
    </script>

 

8、手寫節流

<script>
    function throttle(fn, delay) {
        let timer = null
        return function () {
            if (timer) {
                return
            }
            timer = setTimeout(() => {
                fn.apply(this, arguments)
                timer = null++
            }, delay);
        }
    }
    const one = document.getElementById('one')
    one.addEventListener('drag', throttle(function (e) {
        console.log(e.offsetX, e.offsetY);
    }, 1000))
</script>
相關文章
相關標籤/搜索