【Step-By-Step】高頻面試題深入解析 / 週刊05

關於【Step-By-Step】

Step-By-Step (點擊進入項目) 是我於 2019-05-20 開始的一個項目,每個工作日發佈一道面試題。

每個週末我會仔細閱讀大家的答案,整理最一份較優答案出來,因本人水平有限,有誤的地方,大家及時指正。

如果想 加羣 學習,可以通過文末的公衆號,添加我爲好友。

__

本週面試題一覽:
  • 實現 Promise.race 方法
  • JSONP 原理及簡單實現
  • 實現一個數組去重的方法
  • 清楚浮動的方法有哪些
  • 編寫一個通用的柯里化函數 currying

20. 實現 Promise.race 方法

在實現 Promise.race 方法之前,我們首先要知道 Promise.race 的功能和特點,因爲在清楚了 Promise.race 功能和特點的情況下,我們才能進一步去寫實現。

Promise.race 功能

Promise.race(iterable) 返回一個 promise,一旦 iterable 中的一個 promise 狀態是 fulfilled / rejected ,那麼 Promise.race 返回的 promise 狀態是 fulfilled / rejected.

let p = Promise.race([p1, p2, p3]);

只要p1、p2、p3之中有一個實例率先改變狀態,p的狀態就跟着改變。那個率先改變的 Promise 實例的返回值,就傳遞給 p 的回調函數。

Promise.race 的特點

Promise.race 的返回值是一個 promise 實例
  • 如果傳入的參數爲空的可迭代對象,那麼 Promise.race 返回的 promise 永遠是 pending
  • 如果傳入的參數中不包含任何 promisePromise.race 會返回一個處理中(pending)的 promise
  • 如果 iterable 包含一個或多個非 promise 值或已經解決的promise,則 Promise.race 將解析爲 iterable 中找到的第一個值。

Promise.race 的實現

Promise.race = function (promises) {
    //promises傳入的是可迭代對象(省略參數合法性判斷)
    promises = Array.from(promises);//將可迭代對象轉換爲數組
    return new Promise((resolve, reject) => {
        if (promises.length === 0) {
            //空的可迭代對象;
            //用於在pending態
        } else {
            for (let i = 0; i < promises.length; i++) {
                Promise.resolve(promises[i]).then((data) => {
                    resolve(data);
                }).catch((reason) => {
                    reject(reason);
                })
            }
        }
    });
}

21. JSONP原理及簡單實現

儘管瀏覽器有同源策略,但是 <script> 標籤的 src 屬性不會被同源策略所約束,可以獲取任意服務器上的腳本並執行。jsonp 通過插入 script 標籤的方式來實現跨域,參數只能通過 url 傳入,僅能支持 get 請求。

實現原理:

  • Step1: 創建 callback 方法
  • Step2: 插入 script 標籤
  • Step3: 後臺接受到請求,解析前端傳過去的 callback 方法,返回該方法的調用,並且數據作爲參數傳入該方法
  • Step4: 前端執行服務端返回的方法調用

jsonp源碼實現

function jsonp({url, params, callback}) {
    return new Promise((resolve, reject) => {
        //創建script標籤
        let script = document.createElement('script');
        //將回調函數掛在 window 上
        window[callback] = function(data) {
            resolve(data);
            //代碼執行後,刪除插入的script標籤
            document.body.removeChild(script);
        }
        //回調函數加在請求地址上
        params = {...params, callback} //wb=b&callback=show
        let arrs = [];
        for(let key in params) {
            arrs.push(`${key}=${params[key]}`);
        }
        script.src = `${url}?${arrs.join('&')}`;
        document.body.appendChild(script);
    });
}

使用:

function show(data) {
    console.log(data);
}
jsonp({
    url: 'http://localhost:3000/show',
    params: {
        //code
    },
    callback: 'show'
}).then(data => {
    console.log(data);
});

服務端代碼(node):

//express啓動一個後臺服務
let express = require('express');
let app = express();

app.get('/show', (req, res) => {
    let {callback} = req.query; //獲取傳來的callback函數名,callback是key
    res.send(`${callback}('Hello!')`);
});
app.listen(3000);

22. 實現一個數組去重的方法

法1: 利用ES6新增數據類型 Set

Set類似於數組,但是成員的值都是唯一的,沒有重複的值。

function uniq(arry) {
    return [...new Set(arry)];
}

法2: 利用 indexOf

function uniq(arry) {
    var result = [];
    for (var i = 0; i < arry.length; i++) {
        if (result.indexOf(arry[i]) === -1) {
            //如 result 中沒有 arry[i],則添加到數組中
            result.push(arry[i])
        }
    }
    return result;
}

法3: 利用 includes

function uniq(arry) {
    var result = [];
    for (var i = 0; i < arry.length; i++) {
        if (!result.includes(arry[i])) {
            //如 result 中沒有 arry[i],則添加到數組中
            result.push(arry[i])
        }
    }
    return result;
}

法4:利用 reduce

function uniq(arry) {
    return arry.reduce((prev, cur) => prev.includes(cur) ? prev : [...prev, cur], []);
}

法5:利用 Map

function uniq(arry) {
    let map = new Map();
    let result = new Array();
    for (let i = 0; i < arry.length; i++) {
        if (map.has(arry[i])) {
            map.set(arry[i], true);
        } else {
            map.set(arry[i], false);
            result.push(arry[i]);
        }
    }
    return result;
}

23. 清除浮動的方法有哪些?

當容器的高度爲auto,且容器的內容中有浮動(float爲left或right)的元素,在這種情況下,容器的高度不能自動伸長以適應內容的高度,使得內容溢出到容器外面而影響(甚至破壞)佈局的現象。這個現象叫浮動溢出,爲了防止這個現象的出現而進行的CSS處理,就叫CSS清除浮動。
<style>
    .inner {
        width: 100px;
        height: 100px;
        float: left;
    }
</style>
<div class='outer'>
    <div class='inner'></div>
    <div class='inner'></div>
    <div class='inner'></div>
</div>

1. 利用 clear 屬性

<div class='outer'> 內創建一個空元素,對其設置 clear: both; 的樣式。

  • 優點:簡單,代碼少,瀏覽器兼容性好。
  • 缺點:需要添加大量無語義的html元素,代碼不夠優雅,後期不容易維護。

2. 利用 clear 屬性 + 僞元素

.outer:after{
    content: '';
    display: block;
    clear: both;
    visibility: hidden;
    height: 0;
}

IE8以上和非IE瀏覽器才支持:after,如果想要支持IE6、7,需要給 outer 元素,設置樣式 zoom: 1;

3. 利用 BFC 佈局規則

根據 BFC 的規則,計算 BFC 的高度時,浮動元素也參與計算。因此清除浮動,只需要觸發一個BFC即可。

可以使用以下方法來觸發BFC
  • position 爲 absolute 或 relative
  • overflow 不爲 visible 的塊元素
  • display 爲 inline-block, table-cell, table-caption

如:

.outer {
    overflow: hidden;
}

注意使用 display: inline-block 會產生間隙。

24. 編寫一個通用的柯里化函數 currying

在開始之前,我們首先需要搞清楚函數柯里化的概念。

函數柯里化是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,並且返回接受餘下的參數而且返回結果的新函數的技術。

const currying = (fn, ...args) =>
    args.length < fn.length
        //參數長度不足時,重新柯里化該函數,等待接受新參數
        ? (...arguments) => currying(fn, ...args, ...arguments)
        //參數長度滿足時,執行函數
        : fn(...args);
function sumFn(a, b, c) {
    return a + b + c;
}
var sum = currying(sumFn);
console.log(sum(2)(3)(5));//10
console.log(sum(2, 3, 5));//10
console.log(sum(2)(3, 5));//10
console.log(sum(2, 3)(5));//10
函數柯里化的主要作用:
  • 參數複用
  • 提前返回 – 返回接受餘下的參數且返回結果的新函數
  • 延遲執行 – 返回新函數,等待執行

參考文章:

[1] 珠峯架構課(牆裂推薦)

[2] CSS-清除浮動

[3] 詳解JS函數柯里化

[4] JavaScript數組去重

謝謝各位小夥伴願意花費寶貴的時間閱讀本文,如果本文給了您一點幫助或者是啓發,請不要吝嗇你的贊和Star,您的肯定是我前進的最大動力。 https://github.com/YvetteLau/...

關注公衆號,加入技術交流羣。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章