撩課-Web大前端天天5道面試題-Day2

1. 如何實現瀑布流?

瀑布流佈局的原理:

1) 瀑布流佈局要求要進行佈置的元素等寬,而後計算元素的寬度, 
與瀏覽器寬度之比,獲得須要佈置的列數;
2) 建立一個數組,長度爲列數,
裏面的值爲已佈置元素的總高度(最開始爲0);
3) 而後將未佈置的元素依次佈置到高度最小的那一列,
就獲得了瀑布流佈局;
4) 滾動加載, scroll事件獲得scrollTop,  與最後盒子的offsetTop對比,
符合條件就不斷滾動加載。

瀑布流佈局核心代碼:

/**
 * 實現瀑布流的佈局
 * @param {string}parentBox
 * @param {string}childBox
 */
function waterFull(parentBox, childBox) {
    // 1. 求出父盒子的寬度
    //  1.1 獲取全部的子盒子
    var allBox = $(parentBox).getElementsByClassName(childBox);
    // console.log(allBox);

    // 1.2 求出子盒子的寬度
    var boxWidth = allBox[0].offsetWidth;
    // console.log(boxWidth);

    // 1.3 獲取窗口的寬度
    var clientW = document.documentElement.clientWidth;
    // console.log(clientW);

    // 1.4 求出總列數
    var cols = Math.floor(clientW / boxWidth);
    // console.log(cols);

    // 1.5 父盒子居中
    $(parentBox).style.width = cols * boxWidth + 'px';
    $(parentBox).style.margin = '0 auto';

    // 2. 子盒子定位
    //  2.1 定義變量
    var heightArr = [], boxHeight = 0, minBoxHeight = 0, minBoxIndex = 0;

    // 2.2 遍歷全部的子盒子
    for (var i = 0; i < allBox.length; i++) {
        // 2.2.1 求出每個子盒子的高度
        boxHeight = allBox[i].offsetHeight;
        // console.log(boxHeight);
        // 2.2.2 取出第一行盒子的高度放入高度數組中
        if (i < cols) { // 第一行
            heightArr.push(boxHeight);
        } else { // 剩餘行的盒子
            // 2.2.3 取出數組中最矮的高度
            minBoxHeight = _.min(heightArr);
            // 2.2.4 求出最矮高度對應的索引
            minBoxIndex = getMinBoxIndex(heightArr, minBoxHeight);
            // 2.2.5 盒子定位
            allBox[i].style.position = 'absolute';
            allBox[i].style.left = minBoxIndex * boxWidth + 'px';
            allBox[i].style.top = minBoxHeight + 'px';
            // 2.2.6 更新最矮的高度
            heightArr[minBoxIndex] += boxHeight;

        }
    }
}

/**
 * 根據內容取出在數組中對應的索引
 * @param {object}arr
 * @param {number}val
 * @returns {boolean}
 */
 
function getMinBoxIndex(arr, val) {
    for (var i = 0; i < arr.length; i++) {
        if (arr[i] === val) return i;
    }
}

/**
 * 判斷是否具有加載子盒子的條件
 * @returns {boolean}
 */
function checkWillLoadImage() {
    // 1. 獲取最後一個盒子
    var allBox = $('main').getElementsByClassName('box');
    var lastBox = allBox[allBox.length - 1];

    // 2. 求出高度
    var lastBoxDis = lastBox.offsetHeight * 0.5 + lastBox.offsetTop;

    // 3. 求出窗口的高度
    var clientH = document.documentElement.clientHeight;

    // 4. 求出頁面滾動產生的高度
    var scrollTopH = scroll().top;

    // 5. 對比
    return lastBoxDis <= clientH + scrollTopH;
}
複製代碼

高清視頻講解(第53節)-->點我css

2. 原生JS都有哪些方式能夠實現兩個頁面間的通訊?

1) 經過url地址欄傳遞參數,例如:點擊列表頁中的每一條數據,
咱們跳轉到同一個詳細頁面,可是根據點擊的不同能夠看到
不一樣的內容,這樣的話咱們就能夠在URL中傳遞不一樣的值來區分了;

2) 經過本地存儲 cookie、localeStorage、sessionStroage...,
例如:京東的登陸,咱們在登陸頁登陸完成後,把用戶信息存儲到本地,
而後在其它頁面中若是須要使用的話,咱們直接從本地的存儲數據中拿
出來用便可;

3) 使用iframe在A頁面中嵌入B頁面,這樣的話,在A中能夠經過一些屬性
和方法實現和B頁面的通訊;

4) 利用postMessage實現頁面間通訊, 父窗口往子窗口傳遞信息,
子窗口往父窗口傳遞信息。

複製代碼

3. 原生JS動態向一個div中插入1000個div標籤,如何實現?

此題主要考性能!

1) 能夠用JS中的createElement建立div,每當建立一個就把它添加到div中, 
但會形成引起迴流的次數太多;

2) 使用字符串拼接的方式,把1000個div都拼接完成後,
統一的添加到頁面中, 但會對div原有的元素標籤產生影響:原來標籤綁定 
的事件都消失了

3) 綜合1和2可使用文檔碎片方式來處理。

追問:若是是建立1000萬個呢?

可採用的方案: 數據分批異步加載
1)  首先把前兩屏幕的數據量(例如:300條)先獲取到,
而後使用字符串拼接或者文檔碎片的方式綁定到頁面中; 

2) 當瀏覽器滾動到指定的區域的時候在加載300條...以此類推。

複製代碼

4. 程序出現bug了,你是如何調試的?

1) 在控制檯加斷點,F10->逐過程  F11->逐語句;
2) 在代碼重點的位置加入console.log輸出對應的值來進行調試;
3) debugger調試;
4) 代碼分割還原調試;
5) 異常捕獲機制, 記錄運行日誌;
6) 單元測試。
複製代碼

5. 開發中是如何進行性能優化的?

如今框架(vue, react,...)、構建工具(webpack, ...)已經給咱們解決掉大部分的性能優化問題, 
面試時, 能夠就你瞭解的框架來深刻剖析, 但此題應該是考原生JS的範疇, 參考答案以下:

1) 雅虎35條性能優化黃金定律;
2) JS代碼優化:
   a. 項目中的JS/CSS文件最好一個頁面只用一個, 
      須要把JS/CSS進行合併壓縮, 而且減小頁面中的垃圾冗餘代碼。
      項目的資源文件在服務器上最好作一下GZIP壓縮。
   
  b. 解除文件緩存; 咱們修改代碼並上傳, 若是以前頁面訪問過該網站,
     頗有可能不能當即見效; 咱們在引入CSS/JS文件的時候, 
     在文件名的後面加上版本號(加時間戳), 好比:
     <script src='itlike.com.js?_=202001311606'></script>; 
     當咱們上傳新的文件後把時間戳改一下就能夠清除緩存了。

  c. 移動端儘可能少用圖片: icon能用svg畫的不用圖片;
     靜態資源圖:作佈局的時候就能肯定下來的圖片, 好比:
     1) css sprite圖片合併(針對於小圖片)
     2) 作圖片延遲加載(針對於大圖片 頭部的長條圖片、背景大圖...),
        開始給一張默認的小的圖片(最好維持在10kb之內)
     3) base64 (存在問題: 頁面的代碼太臃腫了,之後維護很差操做);  
        若是項目中因爲圖片太大實在解決不了, 改爲base64就解決了
   
  d. 動態數據圖: 經過ajax從後臺讀取回來的圖片 , 圖片懶加載;

  e. 音視頻文件的優化: 加載頁面的時候,儘可能不要加載音視頻文件,
     當頁面中的其餘資源加載完成後在開始加載音視頻文件; 
     目前移動端常常給音視頻作的優化是:走直播流文件(音頻後綴名是m3u8格式);

  f. 減小頁面資源請求的次數:若是當前只是一個宣傳頁,
     或者是一個簡單的頁面, 使用的css和js能夠採用內嵌式開發;

  g. ajax數據請求分批請求,  例如:一次要請求10000條數據的話,
     咱們每一次只請求100條,第一屏幕確定能看全了,
     當頁面滾動到對應的其它屏幕的時候,
     在加載下一個100條...

  h. 作數據的二次緩存,   能用CSS3作動畫的絕對不用JS,
     能使用transform儘可能使用,能用animation的進行不用transition...
     儘可能減小同步操做,多用異步操做;
     能使用原生JS本身編寫的絕對不用插件或者框架;  
複製代碼

yahoo35條黃金定律

6. 如何實現電商網站中的樓層效果?

1) 封裝緩動動畫函數;
2) 點擊切換, 滾動切換, 聯動處理;

核心代碼以下:

// 3. 監聽GPS上的li的點擊
for (var j = 0; j < olLis.length; j++) {
    (function (index) {
        var olLi = olLis[index];
        olLi.onmousedown = function () {

            isClick = true;

            // 3.1 排他
            for (var m = 0; m < olLis.length; m++) {
                olLis[m].className = ''
            }
            addClass(this, 'current');

            // 3.2 讓樓層滾動起來
            buffer(document.documentElement, {'scrollTop': index * client().height}, function () {
                isClick = false;
            })
        }
    })(j)
}

// 4. 監聽文檔的滾動
window.onscroll = function (ev1) {
    // 4.1 沒有點擊產生的滾動
    if (!isClick) {
        // 4.2 獲取頁面產出的頭部滾動的高度
        var roll = Math.ceil(scroll().top);
        console.log(roll);
        // 4.3 遍歷
        for (var i = 0; i < olLis.length; i++) {
            // 4.4 判斷
            if (roll >= ulLis[i].offsetTop) {
                for (var m = 0; m < olLis.length; m++) {
                    olLis[m].className = ''
                }
                addClass(olLis[i], 'current');
            }
        }
    }

}

緩動動畫函數:

/**
 * 緩動動畫(撩課學院)
 * @param {object}obj
 * @param {object}json
 * @param {function}fn
 */
function buffer(obj, json, fn) {
    // 1.1 清除定時器
    clearInterval(obj.timer);
    // 1.2 設置定時器
    var begin = 0, target = 0, speed = 0;
    obj.timer = setInterval(function () {
        // 1.3.0 旗幟
        var flag = true;
        for (var k in json) {
            // 1.3 獲取初始值
            if ("opacity" === k) { // 透明度
                begin = parseInt(parseFloat(getCSSAttrValue(obj, k)) * 100);
                target = parseInt(parseFloat(json[k]) * 100);
            } else if ("scrollTop" === k) {
                begin = Math.ceil(obj.scrollTop);
                target = parseInt(json[k]);
            } else { // 其餘狀況
                begin = parseInt(getCSSAttrValue(obj, k)) || 0;
                target = parseInt(json[k]);
            }
            // 1.4 求出步長
            speed = (target - begin) * 0.2;
            // 1.5 判斷是否向上取整
            speed = (target > begin) ? Math.ceil(speed) : Math.floor(speed);
            // 1.6 動起來
            if ("opacity" === k) { // 透明度
                // w3c的瀏覽器
                obj.style.opacity = (begin + speed) / 100;
                // ie 瀏覽器
                obj.style.filter = 'alpha(opacity:' + (begin + speed) + ')';
            } else if ("scrollTop" === k) {
                obj.scrollTop = begin + speed;
            } else if ("zIndex" === k) {
                obj.style[k] = json[k];
            } else {
                obj.style[k] = begin + speed + "px";
            }

            // 1.5 判斷
            if (begin !== target) {
                flag = false;
            }
        }

        // 1.3 清除定時器
        if (flag) {
            clearInterval(obj.timer);
            // 判斷有沒有回調函數
            if (fn) {
                fn();
            }
        }
    }, 20);
}
複製代碼

高清視頻講解(第192節)--->點我vue

相關文章
相關標籤/搜索