前端學習總結——JavaScript基礎語法

變量類型和計算ajax

  • 值類型和引用類型
    • 值在棧中存儲,i = 1,將 i 的值賦值給 j ,在棧中新開闢一塊區域存儲 j = 1,j 跟 i 是兩個獨立的值,各自佔用一塊內存空間;
    • 引用類型在堆中存儲,a = {x: 20},a在堆中申請一個內存地址1,把值放在這個地址上,變量a在棧中保存,其值爲內存地址1,這個地址指向{x: 20}這個對象,當把a賦值給b,其實是把地址1賦值給b,經過b修改這個對象,a也會改變,也就是「傳址」;
    • 值類型和引用類型的保存其實是計算機一種性能優化的手段,幾乎全部語言都是這樣保存數據的,因爲值類型比引用類型佔用的內存小,賦一次值開闢一塊內存來存儲一個值類型的數據也還無妨。
    • 寫個深拷貝的實現:
function deepClone (obj = {}) {
    if (typeof obj !== 'object' || obj == null){
        // 不是對象或者數組, 直接返回
        return obj;
    }
    let result;
    if (obj instanceof Array) {
        result = [];
    } else {
        result = {};
    }

    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            // 保證 key 不是原型的屬性
            //遞歸
            result[key] = deepClone(obj[key]);
        }
    }
    return result;
}
  • 類型判斷
    • typeof能判斷哪些類型?
      • 數據類型分簡單數據類型/複雜數據類型(原始類型/對象類型, 值類型/引用類型 ),簡單數據類型有: undefined\number\string\Boolean\symbol,複雜數據類型:object\function\null\array\RegExp;
      • typeof 可以檢測全部值類型, 識別函數, 判斷是不是引用類型(object: array, obj, null), 不可再細分, 更具體的 array 等引用類型須要用到 instanceof 來進行檢測;
      • 函數 function 是特殊的引用類型, 但不用於存儲數據, 因此沒有拷貝\複製函數一說。

原型和原型鏈編程

  • 原型:建立一個構造函數函數,它會按照規則建立一個prototype屬性,這個屬性指向原型對象,這個原型對象上有一個默認constructor屬性指回這個構造函數。原型上的非手動添加的屬性和方法都繼承自上一級構造函數的原型,調用這個構造函數建立新的實例,這個實例上的__proto__指針就被賦值爲構造函數的原型對象。

   實例與構造函數的原型之間有直接的聯繫,可是實例與構造函數之間沒有。數組

  • 原型鏈:原型是有層級的,實例經過原型繼承屬性和方法。每一個構造函數都有一個原型,原型上有一個屬性指回構造函數,而實例上有一個內部指針__proto__指向原型。原型多是另外一個構造函數的實例,那麼他也有一個指針__proto__指向它的構造函數的原型,這種關係能夠是不少層的,這就在實例和原型之間造成一條原型鏈。

    原型鏈的頂端是object.prototype,其上定義的toString()、valueOf()、hasOwnProperty()。promise

    實例的隱式原型引用的是對應構造函數的顯式原型:children.__proto__  === Parent.prototype
瀏覽器

  • instanceof操做符:instanceof 返回一個布爾值,方法只要原型鏈上沾邊就是true, [] instancemof Array/Object 返回true;
  •  手寫jQuery考慮插件和擴展性代碼
class jQuery {
    constructor(selector) {
        const result = document.querySelectorAll(selector);
        const length = result.length;
        for (let i = 0; i < length; i++) {
            this[i] = result[i];
        }
        this.length = length;
        this.selector = selector;
    }
    get(index) {
        return this[index];;
    }
    each(fn) {
        for (let i = 0; i < this.length; i++) {
            const elem = this[i];
            fn(elem)
        }
    }
    on(type, fn) {
        return this.each(elem => {
            elem.addEventListener(type, fn, false)
        })
    }
}
    // 本身寫的jQuery插件
    jQuery.prototype.dialog = function (info) {
    alert(info);
}
// 造輪子
class myJQuery extends jQuery {
    constructor(selector) {
        super(selector);
    }
    // 擴展本身的方法
    addClass(className) {
        
    }
    style(data) {

    }
}

 

做用域和閉包性能優化

  • 自由變量:一個變量在當前做用域內沒有被定義,可是被使用了,會向上層做用域逐級尋找,直到頂級做用域沒找到報錯:XX is not defined
  • 閉包:是做用域應用的特殊狀況,函數做爲參數被傳遞或者函數做爲返回值返回,函數做爲返回值的時候,函數定義在局部做用域,在全局做用域調用的時候,局部做用域內定義的自由變量,在定義的地方向上級逐級查找變量的值,而不是在全局做用域處調用的地方向上查找自由變量的值;函數做爲參數被傳遞的時候,函數定義在全局做用域,在局部做爲參數被調用,仍是在定義的地方去向上級查找自由變量的值。
  • 利用閉包隱藏數據, 作一個簡單的cache工具
function createCache() {
    const data = {}; // 閉包中的數據, 被隱藏, 不被外界訪問
    return {
        set: function(key, val) {
            data[key] = val;
        },
        get: function(key) {
            return data[key];
        }
    }
}

const c = createCache();
c.set('a', 100);
console.log(c.get('a'));

 

  • this指向:this的指向是由調用的時候決定的,不是由定義的時候決定的:
    • 在普通函數中:window
    • 在對象方法中被調用:指向對象自己
    • 經過call、apply、bind:根據傳入的值來決定
    • 在class中:當前實例自己
    • 在箭頭函數中:箭頭函數沒有this,須要向上級做用域的this尋找
  • 寫一個bind方法:
Function.prototype.bind1 = function () {
    // **將參數列表拆解爲數組
    const args = Array.prototype.slice.call(arguments); 
    // 獲取this ( 數組第一項 )
    const this1 = args.shift();
    // fn1.bind( ... )中的 fn1
    const self = this;
    // 返回一個函數
    return function() {
        return self.apply(this1, args);
    };
}

 

  • 寫一個call方法:
Function.prototype.myCall = function (ctx, ...args) {
            if (typeof this !== 'function') {
                throw new Error('not a function')
            }
            let fn = Symbol();
            ctx = ctx || window;
            ctx[fn] = this;
            ctx[fn](...args);
            delete ctx[fn];
        }
        function fn1(a, b) {
            console.log(this.name);
            console.log(a, b);
        }

 

  • 寫一個apply方法:
 Function.prototype.myApply = function (ctx, args = []) {
            if (typeof this !== 'function') {
                throw new Error('not a function')
            }
            let fn = Symbol();
            ctx = ctx || window;
            if(!(args instanceof Array)){
                throw new TypeError('請輸入數組做爲參數');
            }
            ctx[fn] = this;
            ctx[fn](...args);
            delete ctx[fn];
        }

 

異步網絡

  • 如何理解JS是單線程語言?
    • JS主線程在同一時間只能執行一個任務,而像網絡請求、定時器等須要耗時的任務,咱們瀏覽器遇到這些不能等待,會將耗時的任務放在隊列中,等待主線程的任務執行完,再執行。這種異步的方式不阻塞代碼的執行。
  • 什麼是異步編程?
    • JS是單線程語言,主線程只能同一時間執行一個任務,待主線程執行完再去任務隊列裏面輪詢任務,在執行任務期間,中間進來的任務都是排列在任務隊列裏。代碼間有相互依賴關係,要求任務隊列按照必定的次序排列,大量會用到嵌套來解決這個問題,但嵌套會增長代碼複雜性,甚至產生「回調地獄」。ES6的promise語法是一種異步編程機制,解決大量的回調的問題。
  • Promise:
    • 類型:Function
    • 參數:執行器函數,通常爲兩個函數參數,用於控制期約的狀態轉換。
    • 期約的狀態:
      • pending:能夠轉化爲兌現或者拒絕,一旦落定不可更改;
      • fulfilled/resolved
      • rejected
    • 傳入的參數爲一個promise對象,其兌現和拒絕的狀態會被保留 ,向後傳遞,期約狀態改變,不會再被重設,不可逆不可撤銷;
    • then返回的也是一個promise,鏈式,成對的promise-then關係,下個then處理上一個返回的promise;
    • then/catch默認返回的是解決狀態的promise
    • 最後放catch,統一對前面的錯誤進行處理
    • 用promise實現圖片加載的函數:
function ajax(url) {
    const p = new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open('get', url, true);
        xhr.onreadystatechange = function () {
            if(xhr.readyState === 4) {
                if(xhr.status === 200) {
                    resolve(
                        JSON.parse(xhr.responseText)
                    )
                } else if (xhr.status === 404) {
                    reject(new Error('404 not found'))
                }
            }
        }
        xhr.send(null);
    })
    return p;
}
相關文章
相關標籤/搜索