面試!你真的準備好了嗎?|手寫API系列梳理

"不畏懼,不講究,將來的日子好好努力"——你們好!我是小芝麻😄javascript

標題黨,它又、又、又來了......css

這篇文章是集合了小芝麻以前全部寫過和沒寫過的API的彙總,爲後面總體複習作準備,若是你也恰好須要,那就跟小芝麻一塊兒來複習吧😄 ;前端

1、仿寫系列

一、重寫call

(proto => {
    function myCall(thisArg, ...args) {
        thisArg = thisArg == undefined ? window : thisArg;
        let type = typeof thisArg;
        if (!/^(object|function)$/.test(type)) {
            if (/^(symbol|bigint)$/.test(type)) {
                thisArg = Object(thisArg);
            } else {
                thisArg = new thisArg.constructor(thisArg);
            }
        }
        let key = Symbol('key');
        thisArg[key] = this;
        let result = thisArg[key](...args);
        delete thisArg[key];
        return result;
    }
    proto.myCall = myCall;
})(Function.prototype)
複製代碼

二、重寫apply

(proto => {
    function myApply(thisArg, args) {
        thisArg = thisArg == undefined ? window : thisArg;
        let type = typeof thisArg;
        if (!/^(object|function)$/.test(type)) {
            if (/^(symbol|bigint)$/.test(type)) {
                thisArg = Object(thisArg);
            } else {
                thisArg = new thisArg.constructor(thisArg);
            }
        }
        let key = Symbol('key');
        thisArg[key] = this;
        let result = thisArg[key](args);
        delete thisArg[key];
        return result;
    }
    proto.myApply = myApply;
})(Function.prototype)
複製代碼

三、重寫bind

(proto => {
    function myBind(thisArg, ...args) {
        let _this = this;
        thisArg = thisArg == undefined ? window : thisArg;
        let type = typeof thisArg;
        if (!/^(object|function)$/.test(type)) {
            if (/^(symbol|bigint)$/.test(type)) {
                thisArg = Object(thisArg);
            } else {
                thisArg = new thisArg.constructor(thisArg);
            }
        }
        return function an(...innerArgs) {
            _this.call(thisArg, ...args.concat(innerArgs));
        }
    }
    proto.myBind = myBind;
})(Function.prototype)
複製代碼

四、重寫new

function Dog(name) {
    this.name = name;
}
Dog.prototype.bark = function () {
    console.log('wangwang');
}
Dog.prototype.sayName = function () {
    console.log('my name is ' + this.name);
}
//==========重寫開始
function _new(Func, ...args) {
    let obj = Object.create(Func.prototype);
    let result = Func.call(obj, ...args);
    if (result !== null && /^(object|function)$/.test(typeof result)) return result;
    return obj;
}
//==========重寫結束
let sanmao = _new(Dog, '三毛');
sanmao.bark(); //=>"wangwang"
sanmao.sayName(); //=>"my name is 三毛"
console.log(sanmao instanceof Dog); //=>true 
複製代碼

2、工具方法系列

一、防抖

debounce:函數防抖不是完成某個事件就去執行某函數,而是在某個間隔時間內只執行一次,避免函數的過多執行java

  • @params:
    • func:須要執行的函數
    • wait:設置的間隔時間
    • immediate:設置爲ture時,調用觸發於開始邊界而不是結束邊界
  • @return: 返回可被調用的函數
let debounce = function (func, wait, immediate) {
    //=>result用來存儲函數執行返回的結果
    //=>timeout記錄定時器
    let result,
        timeout = null;
    //=>返回可被執行的函數
    return function (...args) {
        //=>now記錄的是事件觸發的時候當即執行,仍是須要等待間隔事件後執行
        let context = this,
            now = immediate && !timeout;
        //=>每一次設置新的定時器等待以前,都要先清空上一次設置的,確保間隔時間內只執行一次
        clearTimeout(timeout);
        //=>設置定時器:到達時間間隔後執行函數
        timeout = setTimeout(() => {
            timeout = null;
            if (!immediate) result = func.apply(context, args);
        }, wait);
        //=>若是是事件觸發就執行,把函數執行便可
        if (now) result = func.apply(context, args);
        return result;
    };
};
複製代碼

二、節流

throttle:函數節流是爲了縮減執行頻率,當達到了必定的時間間隔就會執行一次shell

  • @params:
    • func:須要執行的函數
    • wait:設置的間隔時間
  • @return: 返回可被調用的函數
let throttle = function (func, wait) {
    let timeout = null,
        result = null,
        previous = 0; //=>上次執行時間點
    return function (...args) {
        let now = new Date,
            context = this;
        //=>remaining小於等於0,表示上次執行至此所間隔時間已經超過一個時間間隔
        let remaining = wait - (now - previous);
        if (remaining <= 0) {
            clearTimeout(timeout);
            previous = now;
            timeout = null;
            result = func.apply(context, args);
        } else if (!timeout) {
            timeout = setTimeout(() => {
                previous = new Date;
                timeout = null;
                result = func.apply(context, args);
            }, remaining);
        }
        return result;
    };
};
複製代碼

三、深克隆

深克隆 VS 淺克隆|深比較 VS 淺比較|回調函數數組

方法一:markdown

let clone = JSON.parse(JSON.stringify(obj));
複製代碼

方法二:數據結構

function _cloneDeep(obj) {
    if (obj === null) return null;
    if (typeof obj !== "object") return obj;
    if (obj instanceof RegExp) return new RegExp(obj);
    if (obj instanceof Date) return new Date(obj);
    let cloneObj = new obj.constructor;
    for (let key in obj) {
        if (!obj.hasOwnProperty(key)) continue;
        cloneObj[key] = _cloneDeep(obj[key]);
    }
    return cloneObj;
}
複製代碼

受高人指點,這裏把break修改成continue,確保漏掉任何一個私有屬性,更爲妥帖😄app

四、深合併

function _assignDeep(obj1, obj2) {
    let obj = _cloneDeep(obj1);
    for (let key in obj2) {
        if (!obj2.hasOwnProperty(key)) continue;
        let v2 = obj2[key],
            v1 = obj[key];
        if ((v1 !== null && typeof v1 === "object") && (v2 !== null && typeof v2 === "object")) {
            obj[key] = _assignDeep(v1, v2);
            continue;
        }
        obj[key] = v2;
    }
    return obj;
}
複製代碼

五、深層次比較

  • 基本數據類型的值,基於 === 比較 便可
  • 函數:都轉換爲字符串再進行比較
  • 對象:
    • => 正則/日期:都轉換爲字符串再進行比較
    • => 普通對象/數組對象等:
      • 1)私有屬性的個數
      • 2)分別遍歷每一個屬性,看看屬性值是否一致
function _is(val1, val2) {
    const type1 = val1 === null ? 'null' : typeof val1,
        type2 = val2 === null ? 'null' : typeof val2;
    // 函數
    if (type1 === "function" && type2 === "function") {
        return val1.toString() === val2.toString();
    }
    // 對象
    if (type1 === "object" && type2 === "object") {
        // 正則和日期
        const ct1 = val1.constructor,
            ct2 = val2.constructor;
        if ((ct1 === RegExp && ct2 === RegExp) || (ct1 === Date && ct2 === Date)) {
            return val1.toString() === val2.toString();
        }
        // 其它對象
        const keys1 = Object.keys(val1),
            keys2 = Object.keys(val2);
        if (keys1.length !== keys2.length) return false;
        for (let i = 0; i < keys1.length; i++) {
            let key1 = keys1[i],
                key2 = keys2[i];
            if (key1 !== key2) return false;
            let item1 = val1[key1],
                item2 = val2[key2];
            let flag = _is(item1, item2);
            if (!flag) return false;
        }
        return true;
    }
    // 其它
    return val1 === val2;
}
複製代碼

六、數據類型檢測

function toType(obj) {
    let class2type = {};
    ["Boolean", "Number", "String", "Function", "Array", "Date", "RegExp", "Object", "Error", "Symbol"].forEach(
        item => {
            class2type["[object " + item + "]"] = item.toLowerCase();
        });
    if (obj == null) return obj + "";
    return typeof obj === "object" || typeof obj === "function" ?
        class2type[class2type.toString.call(obj)] || "object" :
        typeof obj;
}

let obj = function () {};
console.log(toType(obj))
複製代碼

七、原型繼承

繼承這裏詳細能夠看:JS中的多種繼承方式ide

function Parent() {
    this.x = 100;
}

function Child() {
    this.y = 200;
}
//=> 讓子類的原型等於父類的實例
Child.prototype = new Parent; //=>原型繼承

Child.prototype.getY = function getY() {
    return this.y;
};

let c1 = new Child;
console.log(c1);
複製代碼

八、CALL繼承

function Parent() {
    this.x = 100;
}
Parent.prototype.getX = function getX() {
    return this.x;
};

function Child() {
    // 在子類構造函數中,把父類當作普通方法執行(沒有父類實例,父類原型上的那些東西也就和它不要緊了)
    // this -> Child的實例c1
    Parent.call(this); // this.x=100 至關於強制給c1這個實例設置一個私有的屬性x,屬性值100,至關於讓子類的實例繼承了父類的私有的屬性,而且也變爲了子類私有的屬性 「拷貝式」
    this.y = 200;
}
Child.prototype.getY = function getY() {
    return this.y;
};

let c1 = new Child;
console.log(c1);
複製代碼

九、寄生組合式繼承

function Parent() {
    this.x = 100;
}
Parent.prototype.getX = function getX() {
    return this.x;
};
function Child() {
    Parent.call(this);
    this.y = 200;
}

Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;

Child.prototype.getY = function getY() {
    return this.y;
};

let c1 = new Child;
console.log(c1);
複製代碼

十、格式化時間字符串

String.prototype.formatTime = function formatTime(template) {
    // 1.根據操做的時間字符串獲取年月日小時分鐘秒等信息
    let arr = this.match(/\d+/g).map(item => {
        return item.length < 2 ? '0' + item : item;
    });

    // 2.解析格式化的模板,找到對應的時間,替換模板中的內容
    template = template || '{0}年{1}月{2}日 {3}時{4}分{5}秒';
    return template.replace(/\{(\d+)\}/g, (_, group) => {
        return arr[group] || "00";
    });
};
複製代碼

十一、處理URL參數

JS中URL參數處理的三種方法

方法一:replace處理

(proto => {
    function queryURLParams() {
        let obj = {};
        this.replace(/([^?=&#]+)=([^?=&#]+)/g, (_, key, value) => obj[key] = value);
        this.replace(/#([^?=&#]+)/g, (_, hash) => obj['HASH'] = hash);
        return obj;
    }
    proto.queryURLParams = queryURLParams;
})(String.prototype);

console.log('http://www.xxxxxxx.cn/?lx=1&name=JS&from=baidu#video'.queryURLParams());
複製代碼

方法二:利用A標籤內置方法

function queryURLParams(url) {
    // 1.建立A標籤(A元素對象)來獲取到問號參數和哈希值
    let link = document.createElement('a');
    link.href = url;
    let askText = link.search.substr(1),
        polText = link.hash.substr(1),
        obj = {};
    // 2.向對象中進行存儲
    polText ? obj['HASH'] = polText : null;
    if (askText) {
        let arr = askText.split(/(?:&|=)/g); //=>同時按照兩個字符來拆分:["lx", "1", "name", "JS", "from", "baidu"]
        for (let i = 0; i < arr.length; i += 2) {
            // console.log(arr[i], arr[i + 1]); 屬性名和屬性值
            obj[arr[i]] = arr[i + 1];
        }
    }
    return obj;
}
let result = queryURLParams('http://www.xxxxxxx.cn/?lx=1&name=JS&from=baidu#video');
console.log(result);

/* <a href="http://www.xxxxxxx.cn/?lx=1&name=JS&from=baidu#video" id="link">*/
複製代碼

十二、實現千分符

String.prototype.millimeter = function millimeter() {
    return this.replace(/\d{1,3}(?=(\d{3})+$)/g, value => {
        return value + ',';
    });
};
複製代碼

1三、字符串中出現最多的字符

let str = "hello";
let ary = [...new Set(str.split(''))];
let max = 0;
let code = '';
for (let i = 0; i < ary.length; i++) {
    let reg = new RegExp(ary[i], 'g');
    let val = str.match(reg).length;  
    if (val > max) {
        max = val;
        code = ary[i];
    } else if (val === max) {
        code = `${code}${ary[i]}`;
    }
}
console.log(`出現次數最多的字符是:${code},次數爲:${max}`); 
複製代碼

該方法引自:掘金做者:李開心呀【建議收藏】一份超簡潔的前端總結 ;(PS:聽說是一位很漂亮的小姐姐喲🌹 )

1四、數組去重

JS中數組去重的三種方法

方法一:雙for循環

for (let i = 0; i < arr.length - 1; i++) {
    let item = arr[i];
    for (let j = i + 1; j < arr.length; j++) {
        if (item === arr[j]) {
            arr[j] = arr[arr.length - 1];
            arr.length--;
            j--;
        }
    }
}
console.log(arr);
複製代碼

方法二:對象鍵值對的方式

let arr = [1, 2, 3, 1, 1, 4, 2, 3];
let obj = {};
for (let i = 0; i < arr.length; i++) {
    let item = arr[i];
    if (obj[item] !== undefined) {
        arr[i] = arr[arr.length - 1];
        arr.length--;
        i--;
        continue;
    }
    obj[item] = item;
}
console.log(arr);
複製代碼

方法三:ES6利用Set方式

/* ES6中沒有提供現成的去重辦法,可是提供了一些去重的方式 :Set數據結構*/
let obj = { y: 200 };
let arr = [obj, 1, 2, 3, 1, obj, 1, 4, 2, 3, '3', { x: 100 }, { x: 100 }];
arr = Array.from(new Set(arr));
console.log(arr);
複製代碼

1五、數組排序

方法一:冒泡排序

// 交換位置函數
function swap(arr, i, j) {
    let temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
    return arr;
}
Array.prototype.bubble = function bubble() {
    // 外層循環I控制比較的輪數
    for (let i = 0; i < this.length - 1; i++) {
        // 裏層循環控制每一輪比較的次數J
        for (let j = 0; j < this.length - 1 - i; j++) {
            if (this[j] > this[j + 1]) {
                // 當前項大於後一項,交換位置
                swap(this,j,j+1);
            }
        }
    }
    return this;
}
let ary = [12, 8, 24, 16, 1];
ary.bubble();
console.log(ary);
複製代碼

方法二:選擇排序

Array.prototype.select = function select() {
    for (let j = 0; j < this.length - 1; j++) {
        let min = j,
            temp = null;
        // 找到比當前項還小的這一項索引
        for (let i = min + 1; i < this.length; i++) {
            if (this[i] < this[min]) {
                min = i;
            }
        }
        // 讓最小的項和當前首位交換位置
        swap(this,min,j);
    }
    return this;
};
let ary = [12, 8, 24, 16, 1];
ary.select();
console.log(ary);
複製代碼

方法三:插入排序

Array.prototype.insert = function insert() {
    // 1.準備一個新數組,用來存儲抓到手裏的牌,開始先抓一張牌進來
    let handle = [];
    handle.push(this[0]);

    // 2.從第二項開始依次抓牌,一直到把檯面上的牌抓光
    for (let i = 1; i < this.length; i++) {
        // A是新抓的牌
        let A = this[i];
        // 和HANDDLE手裏的牌依次比較(從後向前比)
        for (let j = handle.length - 1; j >= 0; j--) {
            // 每一次要比較的手裏的牌
            let B = handle[j];
            // 若是當前新牌A比要比較的牌B大了,把A放到B的後面
            if (A > B) {
                handle.splice(j + 1, 0, A);
                break;
            }
            // 已經比到第一項,咱們把新牌放到手中最前面便可
            if (j === 0) {
                handle.unshift(A);
            }
        }
    }
    return handle;
}
let ary = [12, 8, 24, 16, 1];
ary.insert();
console.log(ary);
複製代碼

方法四:快速排序

Array.prototype.quick = function quick() {
    // 4.結束遞歸(當數組中小於等於一項,則不用處理)
    if (this.length <= 1) {
        return this;
    }
    // 1.找到數組的中間項,在原有的數組中把它移除
    let middleIndex = Math.floor(this.length / 2);
    let middleValue = this.splice(middleIndex, 1)[0];
    // 2.準備左右兩個數組,循環剩下數組中的每一項,比當前項小的放到左邊數組中,反之放到右邊數組中
    let aryLeft = [],
        aryRight = [];
    for (let i = 0; i < this.length; i++) {
        let item = this[i];
        item < middleValue ? aryLeft.push(item) : aryRight.push(item);
    }
    // 3.遞歸方式讓左右兩邊的數組持續這樣處理,一直到左右兩邊都排好序爲止(最後讓左邊+中間+右邊拼接成爲最後的結果)
    return quick(aryLeft).concat(middleValue, quick(aryRight));
}
let ary = [12, 8, 15, 16, 1, 24];
ary.quick();
console.log(ary);
複製代碼

方法四:希爾排序

Array.prototype.shell = function shell() {
    let gap = Math.floor(this.length / 2);
    while (gap >= 1) {
        for (let i = gap; i < this.length; i++) {
            while (i - gap >= 0 && this[i] < this[i - gap]) {
                swap(this, i, i - gap);
                i = i - gap;
            }
        }
        gap = Math.floor(gap / 2);
    }
};
let arr = [58, 23, 67, 36, 40, 46, 35, 28, 20, 10];
arr.shell();
console.log(arr);
複製代碼

1六、數組中的最大值/最小值

獲取數組中最大值/最小值的三種基礎方法

方法一:基於sort

ary.sort(function (a, b) {
	return a - b;
});
let min = ary[0];
let max = ary[ary.length - 1];
console.log(min, max);
複製代碼

方法二:利用Math.min/max

let min = Math.min(...ary);
console.log(min);
//==========================
let min = Math.min.apply(null,ary);
console.log(min);
複製代碼

方法三:假設法

let max = ary[0];
for (let i = 1; i < ary.length; i++) {
	let item = ary[i];
	item > max ? max = item : null;
}
//=> for 循環 也能夠改寫爲 forEach
ary.forEach(item => {
	item > max ? max = item : null;
});
console.log(max); 
複製代碼

1七、獲取元素到BODY的偏移量

function offset(element) {
    let parent = element.offsetParent,
        top = element.offsetTop,
        left = element.offsetLeft;
    while (parent) {
        if (!/MSIE 8/.test(navigator.userAgent)) {
            left += parent.clientLeft;
            top += parent.clientTop;
        }
        left += parent.offsetLeft;
        top += parent.offsetTop;
        parent = parent.offsetParent;
    }
    return {
        top,
        left
    };
}
複製代碼

1八、獲取和設置CSS樣式

/* 獲取CSS樣式 */
function getCss(element, attr) {
    let value = window.getComputedStyle(element)[attr],
        reg = /^\d+(px|rem|em)?$/i;
    if (reg.test(value)) {
        value = parseFloat(value);
    }
    return value;
}
/* 設置CSS樣式:單個設置 */
function setCss(element, attr, value) {
    if (attr === "opacity") {
        element['style']['opacity'] = value;
        element['style']['filter'] = `alpha(opacity=${value*100})`;
        return;
    }
    let reg = /^(width|height|margin|padding)?(top|left|bottom|right)?$/i;
    if (reg.test(attr)) {

        if (!isNaN(value)) {
            value += 'px';
        }
    }
    element['style'][attr] = value;
}
/* 設置CSS樣式:對象形式設置 */
function setGroupCss(element, options) {
    for (let key in options) {
        if (!options.hasOwnProperty(key)) break;
        setCss(element, key, options[key]);
    }
}

function css(element) {
    let len = arguments.length,
        attr = arguments[1],
        value = arguments[2];
    if (len >= 3) {
        // 單一設置樣式
        setCss(element, attr, value);
        return;
    }
    if (attr !== null && typeof attr === "object") {
        // 批量設置
        setGroupCss(element, attr);
        return;
    }
    // 獲取樣式
    return getCss(element, attr);
}
複製代碼

今天的內容就先到這裏吧,原本還想把梳理的部分源碼也放進來,可是考慮到一堆代碼原本就夠枯燥乏味的了,篇幅在很大的話,恐怕小芝麻本身都看不進去 😓 ;

同時也歡迎你們提供新的思路,和一些經常使用的方法,小芝麻感激涕零🙏 😄

相關文章
相關標籤/搜索