gitHub地址: 傳送門

工作中遇到的坑和思考

有不同意見歡迎指正交流

前排推薦 https://github.com/topics/javascript 關注JS開源框架動態

勤於總結和思考

1. ajax請求的結果要和後端約定好返回的資料格式。
- 比如:成功與否code、提示資訊msg、詳細的返回資料data。如果每次格式不一致會導致出錯。一個場景:後端在當前介面直接返回資料 response = [1, 2, 3] ,而後端驗證登入過期後返回的資料格式是response = { code: 2, msg: "登入過期", data: "" },這個時候如果你的異常處理沒有做好,就可能會出錯。

2. 注意程式碼的複用(切記)
- 寫邏輯的時候經常能抽離出公用程式碼,比如一個公共的方法、一個公共的邏輯、能抽離的一定要抽離!不要覺得抽離很難,就是把會變的設定成變數,傳遞進方法中就行。這樣你後期維護的時候會省很多事
“`javascript
/* 比如一個計算方法封裝到公共的JS中 */

function addResult(a, b, c){
    return a * b - c
}

/* 如果專案中多次用到這個方法 你就可以直接呼叫這個函式 */

// 頁面a
addResult( 9, 5, 5 );
// 頁面b
addResult( 9, 9, 6 );

/**
 * 尷尬的問題來了 產品說這種計算方法不對 要改一下 
 * 這時候就體現了這種封裝帶來的好處 
 * 如果你的頁面中每個都是單獨寫的一個方法 那你就要一個一個去改
 * 封裝進公共的JS中 我直接修改這個方法 所有的頁面引用這個函式的就都生效了 
 * 改一個的快捷方便性遠遠大於改幾個、幾十個頁面 對吧
 */

“`
- 我建議兩個或者三個以上頁面使用到了相同的邏輯或者函式就封裝進公共函式中 一個一個頁面去改真的很痛苦
- 封裝的公共方法註釋一定要寫清楚 不要覺得方法很簡單就不寫了
- 封裝的方法語法儘量友好,方便同事維護。不要覺得寫逼格很高的程式碼就很好,每個人的技術水平不同。
- 方法命名儘量符合規範 動詞 + 名詞
- 工具方法和邏輯方法可以分開封裝 方便維護

    // 比如這個檔案是 common.js
    let tools = {
        /**
         * @description 返回一個數字型別的數字
         * @param { * } -- num: 任意值
         */
        returnNum ( num ) {
            isNaN( num ) && ( num = 0 );
            return Number( num );
        }
    };
    let logic = {
        // 你的邏輯函式
    };

3. 做好異常檢測
- 善用 try{} catch(){}
- 資料型別驗證
- 後端返回的資料型別驗證,不要覺得後端每次返回的型別都是對的。比如有時候空的時候,後端沒返回 "",返回的是 null,你的頁面就可能報錯。
- 傳入公共函式中的引數型別驗證
- 現在比較流行的 TypeScript 就是強型別的JS 是JS的超集 資料驗證等 很強大
- 等等……

4. 寫完介面和邏輯一定要在常用瀏覽器中跑一下看看是否有相容性問題

5. 靜態資源管理

如果專案中使用的有webpack等打包工具 可以避免這個問題 如果沒有使用 就要注意了

  • 靜態資源存在快取的問題。一個場景就是頁面index.html中增加了一個公共方法addNum,公共方法寫在common.js中。當用戶訪問這個頁面時,common.js並沒有得到更新,addNum is not defined報錯了。我們目前使用的方案就是後端在檔案後面新增版本號common.js?v=xxxxx,這個版本號可以讓後端通過獲取檔案修改時間來新增,這樣就起到了版本控制的效果,最好的方式其實是common.dde65x.js這種。v版本的方式有的瀏覽器還是會無視,但是大部分都沒問題。
  • 圖片壓縮。 圖片壓縮的話webpack有外掛。我們可以使用 Tiny 這個線上壓縮的網站來壓縮我們的圖片。
  • css壓縮、整理、美化 CSS線上壓縮/整理/格式化工具, 很多編譯器的外掛都有這個功能啦,就不做太多介紹。
  • js壓縮、整理、混淆 JS線上壓縮/整理/格式化工具, 很多編譯器的外掛都有這個功能啦,就不做太多介紹。

6. 需求
- 儘量需求明確後再開始寫專案,避免返工。
- 不清楚的需求一定要問清楚,不要怕問。問清楚總比犯錯強。

7. es5塊級作用域
- es5和它之前js並沒有明確的模組化和塊級作用域規範,現在有es6的export/import{}塊級作用域等。很方便的,不要抗拒新技術,擁抱它你會愛上它。
- 如果你的專案中技術棧比較落後,沒有模組化工具比如 require.jssea.js等。你可以使用閉包匿名函式模擬一個塊級作用域。
- 使用babel寫es6在專案中也是一個不錯的選擇,babel可以單獨使用的,不用配合webpack

    var glob = {
        // 全域性函式 用來實現作用域間的通訊
        // 這裡定義全域性的屬性和一些別的
    };
    (function(){
        // 作用域1 這是業務邏輯A的部分 
    })();
    (function(){
        // 作用域2 這是業務邏輯B的部分
    })();

    // 注意了 閉包中的變數不會被垃圾回收機制刪除 用完記得null一下

8. input也是有長度限制的
- 在一次專案中,我使用隱藏的input來存放base64的圖片,傳遞給後端後,圖片只有一半了,被截取了。使用的POST傳輸。

9. z-index的優先順序
- 子元素的z-index受父元素影響。如果父元素的z-index是2,子元素的z-index66666。那麼子元素的66666只在當前父級下的同級和子集生效,父級以上和父級的兄弟元素仍示z-index: 66666z-index: 2

10. 神奇的<pre>標籤
- <pre> 標籤可定義預格式化的文字。
- 被包圍在 <pre> 標籤 元素中的文字通常會保留空格和換行符。而文字也會呈現為等寬字型。
- 一個場景: 比如我要解析出來的文字保留使用者在textarea中輸入的換行等,就可以使用這個標籤。
- 1. pre元素是塊級元素,但是隻能包含文字或行內元素。也就是說,任何塊級元素(常見為可以導致段落斷開的標籤)都不能位於pre元素中。
2. pre元素中允許的文字可以包含物理樣式和基於內容的樣式變化,還有連結、影象和水平分隔線。當把其他標籤,比如<a>標籤放到<pre>塊中時,就像放在HTML/XHTML文件的其他部分中一樣即可。
3. 製表符tab在<pre>標籤定義的塊當中可以起到應有的作用,每個製表符佔據8個字元的位置,但並不推薦使用tab,因為在不同的瀏覽器中,tab的表現形式各不相同。在用<pre>標籤格式化的文件段中使用空格,可以確保文字正確的水平位置。
4. 如果希望使用<pre>標籤來定義計算機原始碼,比如HTML原始碼,請使用符號實體來表示特殊字元。一般將<pre>標籤與<code>標籤結合起來使用,以獲得更加精確的語義,用於標記頁面中需要呈現的原始碼。
5. 如果想要把某一段規定好的文字格式放在HTML中,那麼就要利用<pre>元素的特性。

  • 可以使用white-spaceprepre-linepre-wrap 屬性達到相同的效果

11. 規範
- 最好弄清楚w3c規範
- 公司規範也要遵守
- 如果有好的建議要及時提出來

12. 沒事可以多逛逛社群
- segmentfault——思否
- 掘金
- 知乎
- 簡書
- 很多很多 找幾個自己比較喜歡的經常看 對自己瞭解技術動態、知識點檢測、學習很有幫助 共勉

13. substring、 substr、 slice

    substr(start [, length])
    substring(start [, end])
    slice(start [, end])
  • substring 有個神奇的地方 就是 start, end ,兩個引數 誰小 誰就是 start
    javascript
    var str = 'My name is: Jerry . My age is: 12 . : :666 .';
    str.substring( 0, 5 )
    "My na"
    var str = 'My name is: Jerry . My age is: 12 . : :666 .';
    str.substring( 5, 0 )
    "My na"
  • substr 和 slice 如果遇到負數 會 和 length 相加
    javascript
    var str = 'My name is: Jerry . My age is: 12 . : :666 .';
    str.slice( 0, 5 )
    "My na"
    var str = 'My name is: Jerry . My age is: 12 . : :666 .';
    str.slice( 0, -5 )
    "My name is: Jerry . My age is: 12 . : :"
    str.length
    44
    str.slice(0, 39)
    "My name is: Jerry . My age is: 12 . : :"

    • 從定義上看: substring和slice是同類的,引數都是字串的某個{開始}位置到某個{結束}位置(但{結束}位置的字元不包括在結果中);而substr則是字串的某個{開始}位置起,數length個長度的字元才結束。-- 共性:從start開始,如果沒有第2個引數,都是直到字串末尾
    • substring和slice的區別則是,slice可以接受“負數”,表示從字串尾部開始計數; 而substring則把負數或其它無效的數,當作0。
    • substr的start也可接受負數,也表示從字串尾部計數,這點和slice相同;但substr的length則不能小於1,否則返回空字串。

14. 通過promise判斷滾動事件是scrollTo觸發的還是滑鼠滾動觸發的

    status = false;
    function timeout( long ){
        return new Promise( function( resolve, reject ){
            window.scrollTo( 0, long )
            setTimeout( resolve, 0 )
        } )
    };

    $(document).click( function(){
        status = true;
        timeout( 200 ).then( function(){
            status = false;
        } )
    } );

    $(window).scroll( function(){
        if( status == 'true' ){
            console.log( '點選事件觸發的' )
        }else if( status == 'false' ){
            console.log( '滾動事件觸發的' )
        }
    } )

15. 函式的引數可以是個表示式(任意型別)

function timeout( a ){
    console.log(a)
};
timeout( window.scrollTo(0, 200), 6 ) // undefined

16. window.status
- 全域性定義status是不行的 它是個保留字 定義任何都是字串
- status屬性在IE,火狐,Chrome,和Safari預設配置是不能正常工作。要允許指令碼來改變狀態列文字,使用者必須把配置螢幕首選項設定為false dom.disable_window_status_change。

17. 一個有趣的JS面試題目

    function a(xxx){
        this.x=xxx
        return this
    }
    x = a(5)
    y = a(6); 
    console.log(x.x);
    console.log(y.x);

    // 輸出什麼 ?
    // undefined 和 6 
    /**
     * 非嚴格模式
     * 首先 函式 a 定義在全域性環境執行 它裡面的this就是指向了window,return 的this也是window
     * 當執行 x = a(5);的時候 this.x = 5,函式返回了this,this是window物件,又被重新賦值給了x, 此時x又是window物件。
     * 如果我們不返回this,就會是這個結果。
     * function a(xxx){
           this.x=xxx
       }
       a(5) 
       console.log(x) //5
     * 但是我們返回了this;
     * 當執行 y = a(6); 這個時候 x 就是 6 了,而返回的this被賦值給了y。
     * 所以當列印x.x時, x是Number型別,基本型別,x.x 所以是undefined
     * 而y.x就是 window.x x是6 所以是 6
     */

18. label for
- 給 label指定for 對應input或者別的form元素 即使label不包裹著for 也會觸發該元素的聚焦 for把兩個不包裹的元素關聯了起來
- 而label包裹上的元素不需要寫for也可以聚焦

19. JS中的Label
- start 在ES5中並沒有建立作用域

start: {
    console.log(1); 
    console.log(2);
    break start
    console.log(3);
}
// 1 2 
還有雙重for迴圈的場景 有興趣可以MDN看一下

20. setTimeout嚴格上來講並不是全域性函式
- setTimeoutwindow的一個方法,如果把window當做全域性物件來看待的話,他就是全域性函式。嚴格來講,它不是。全域性函式與內建物件的屬性或方法不是一個概念。全域性函式他不屬於任何一個內建物件。JS中包含以下7個全域性函式escape()eval()isFinite()isNaN()parseFloat()parseInt()unescape()

21. 一個朋友提的場景需求而引發的Array.sort()方法思考

// 比如一個數組 
let arr = [1, 23, 4, 5, 6, 8, 9, 1,0 ,11, 5, 666, -1, -1, -1 ];
// 需求是 按照升序排列 但是-1必須在最後

arr.sort( (a, b)=>{
    a < 0 && (a = Number.POSITIVE_INFINITY);
    b < 0 && (b = Number.POSITIVE_INFINITY);
    return a - b
} )

arr.sort( (a, b)=>{
  a < 0 || b < 0 && ( a *= -1, b *=-1 );
  return a - b
} )

// 位運算
arr.sort( (a, b) => !~a || !~b ? b : a - b ) 

// sort方法會修改原始陣列 使用之前最好拷貝一下原陣列

22. 如果你要處理多列或多行之間的計算或別的問題
- 如果是資料驅動很好處理
- 如果是JQ 計算的時候最好給每列或每行要用到的起個class類名,根絕這個類名去查詢計算。這樣會避免列或行的增刪帶來的問題。比如你用eq去找的,但是增加了一列,減少了一列。或者你按著input的name名字去找,後端給你改了名字咋搞呢。

23. 一個iframe跨域的場景
- 今天遇到一個場景,父頁面A、Iframe頁面B和Iframe頁面C,B頁面提交後通過A頁面關閉開啟的Iframe
- 父頁面A用來操作關閉當前Iframe和重新整理A頁面中的列表
- Iframe-B中需要上傳圖片,而上傳圖片的元件是公共Iframe-C
- 但是Iframe-C的域名和A、B頁面不同,產生了跨域問題
- 嘗試了window.name, document.domain等方法皆不奏效。
- 後來,想到個笨方法

/*
 * 正常來講 
 *  $.ajax().done( ( response )=> { 
 *      window.parent.modal.hide();
 *      window.parent.IframeB.hide();
 *  }) 
 * 但是到這裡由於Iframe-C的問題產生了跨域
 * 於是換了個思路
 * 請求成功後 給URL加一個hash然後重新整理當前頁面
 * $.ajax().done( ( response )=> { 
 *      window.location.hash = '?active=true';
 *      window.location.reload();
 *  }) 
 * 然後在引起跨域的問題之前 (我是放在了<body>之後) 檢查url的hash值
 * var body = document.body || document.getElementsByClassName('body')[0];
 * /\?active\=true/.test(window.location.href) && (  window.parent.modal.hide(), window.parent.IframeB.hide() )
 * 這個時候會有一些閃動 你可以給body設定透明或者別的 等載入到JS把透明或別的去掉
*/

24. IE8的非同步上傳檔案方案
- 基本思路很簡單,提交上傳檔案表單時,讓瀏覽器轉移到iframe處理響應資訊,響應資訊嵌入一段js程式碼,這段js程式碼呼叫當前頁面的一個方法就可以實現回撥,類似於xss攻擊。這時就要用到form表單的target屬性,我們這裡只需要用到iframename的值,iframename指的是iframe的name屬性,意思是轉移到iframe處理響應資訊。
- 父頁面

<form action="excel/uploadExcel" target="testForm" method="POST" enctype="multipart/form-data">
    <input type="file" name="file">
    <input type="submit" value="submit">
</form>
<div id="titles"></div>
<iframe name="testForm" style="display: none"></iframe>

<script>
    // 暴露一個全域性方法供Iframe呼叫
    function uploadFileCallback = function( data, errorMsg ){
        if( errorMsg ){
            alert( errorMsg );
            return
        }
        document.getElementById('titles').innerHTML = data;
    }
</script>
  • iframe頁面
<script>
    window.parent.uploadFileCallback( '資料1''資料2' )
</script>

25. isNaN( ) 會把被檢測的內容先轉成數字型別

MDN-isNaN

var str = '';
isNaN( str ); // false
Number( str ); // 0

var num = 0;
isNaN( num ); // false

//isNaN() 底層會將字串先轉成數字型別 
isNaN( '666' ) // false

26. 物件拍平方法

我自己寫的只能拍平2層 最下面是大貓大牛寫的

  • 三層的情況
var m = { "a": 1, "b": { "c": 2, "d": [3, 4] }, "e": { f: { g: "6" } } };
var obj = {};
function planeHouse( m, child ){
    Object.keys(m).forEach( function( v, k ){
        if( Object.prototype.toString.call( m[v] ) === "[object Object]" ){
        // 如果當前還是一個物件 遞迴呼叫 傳入child  v是m[v]的子key  obj[b.c] obj[b.d]
        planeHouse( m[v], v )
        }else{
        child ? obj[child+'.'+v] = m[v]  : obj[v] = m[v]
        }

    } )
}

planeHouse( m ) // {a: 1, b.c: 2, b.d: Array(2), f.g: "6"}
  • 兩層的情況
var m = { "a": 1, "b": { "c": 2, "d": [3, 4] } };
var obj = {};
function planeHouse( m, child ){
    Object.keys(m).forEach( function( v, k ){
        if( Object.prototype.toString.call( m[v] ) === "[object Object]" ){
            // 如果當前還是一個物件 遞迴呼叫 傳入child  v是m[v]的子key  obj[b.c] obj[b.d]
            planeHouse( m[v], v )
        }else{
            child ? obj[child+'.'+v] = m[v]  : obj[v] = m[v]
        }

    } )
}

planeHouse( m ) // {a: 1, b.c: 2, b.d: Array(2)}
  • 大貓大牛寫的方法 支援多層
var m = { "a": 1, "b": { "c": 2, "d": [3, 4] }, "e": { f: { g: "6" } } };
function spreadJSON (result, json, parentKey) {
      const keys = Object.keys(json);
      keys.forEach(key => {
        const value = json[key];
        const concatKey = parentKey + (parentKey ? '.' : '') + key;
        if (Object.prototype.toString.call(value) === '[object Object]'){ 
            spreadJSON (result, value, concatKey)
        }else {
            result[concatKey] = value
        };
      })
      return result;
    }

spreadJSON ({}, m, '')

{a: 1, b.c: 2, b.d: Array(2), e.f.g: "6"}

27. Blob物件

MDN-Blob

  • Blob 物件表示一個不可變、原始資料的類檔案物件。Blob 表示的不一定是JavaScript原生格式的資料。檔案介面基於Blob,繼承了blob的功能並將其擴充套件使其支援使用者系統上的檔案。
  • 要從其他非blob物件和資料構造一個Blob,請使用 Blob() 建構函式。要建立包含另一個blob資料的子集blob,請使用 slice()方法。要獲取使用者檔案系統上的檔案對應的Blob物件,請參閱 檔案文件。

28. Postcss-cli的簡單使用
- 今天把專案中的css單獨使用postcss優化了一下
1. npm i -g|-D postcss-cli 安裝postcss-cli
2. npm i -g autoprefixer 安裝外掛
3. cd node_modules/.bin 一定要進入這個資料夾
4. 在3進入的資料夾中 根據相對路徑找到你要優化的css (我的是放在了根目錄的css檔案中)
5. postcss ../../css/common.css -o ../../css/outcommon.css -u autoprefixer
6. 然後你就拿到了加了相容的新的css樣式
- 還有很多外掛(現在貌似200+)還沒用到 有時間一起研究

29. IOS9和低版本安卓 不支援 let!

30. IOS的時間格式 必須是 2018/03/24 19:11:00
- IOS的時間格式 必須是 2018/03/24 19:11:00
- IOS的時間格式 必須是 2018/03/24 19:11:00
- IOS的時間格式 必須是 2018/03/24 19:11:00

31. Vue開發微信網頁遇到的一些問題
- 前幾天做一個專題,本來有個老的模板,可以直接套,但是我想用VUE,於是給自己找了個大麻煩,熬到凌晨,還加了班,最後因為微信的配置問題和甲方比較催促,不得不放棄了。講一下期間遇到的問題,等這個專案忙完了,接著研究,不能遺留這些問題。
- 1. Vue bus的狀態沒有實時更改,不得不採用props傳值的麻煩方式,具體在彈窗的那裡 —— 待解決。
2. 微信wx.config 簽名無效一直報錯(這是我最後放棄使用vue的原因,實在是來不及了)所以你使用Vue開發微信的時候一定要提前做個demo測試一下,貌似通過URL.split('#')[0]這樣傳遞給後端去做簽名就行了
3. 如何使用內網穿透除錯微信。
4. 桌面右下角的網路檢視屬性IPV4的ip並不準確 還是要用cmd-ipconfig
5. Vue beforeEach的全域性路由導航中 如果沒有進入登入頁面(獲取使用者資訊儲存到Store中) 你在這個週期裡用Store取值是拿不到的,所以要做個路由名稱的判斷(當時腦子確實寫漿糊了 幾近崩潰 很難受)
6. IOS的時間格式 必須是 2018/03/24 19:11:00
7. IOS<=9和老版本的安卓的系統 不支援let
8. 關於微信的授權:
+ 我們的後端是通過URL的hash值將使用者授權後的資訊傳遞給我
+ 首先你要做個Vue的登入頁
+ 思路是 這個登入頁中,mouted或者creadted週期中判斷url中是否有後端傳來的hash,我們定的是data,如果取到了這個hash並且有內容,就儲存到store
+ 在這個元件中首先要跳轉到後端的授權連結假如是http://www.shou.com
+ 然後後端接到這個請求連結後,將引數附加在URL中 http://www.shou.com?data={"user": {"openid": "ssxffqfsf"}}
+ 然後後端還會重定向回我們的頁面,然後我們再次進入這個登入元件,這個時候就取到了後端給我們的hash然後就可以進入我們的專案了

32. table表格中的表格如果使用relative定位會導致表格邊框消失
- 在IE、火狐中都測出該問題
- 解決思路: 給td中套一個div,給這個div設定相對定位,內容寫在相對定位的DIV中。
- 可看帖子 解決IE瀏覽器下:td標籤上有position: relative;與background-color屬性時td邊框消失
- 另一種思路,看場景。 不要給td背景色 給tr背景色 當:hover當前行的時候就不會出現邊框問題了

33. 一個橫著鋪開的ul列表的樣式

.tabs .tabs-navbar .tabs-nav{
    height: 30px;
    white-space: nowrap;
    overflow: hidden;
}   
.tabs .tabs-navbar .nav-tabs>li{
    display: inline-block;
    float: none;
} 

34. table使用 table-layout: fixed;帶來的問題
- table使用table-layout: fixed;然後使用colspan rowspan 會使子表格中設定寬度失效,這個時候可以用

<colgroup>
    <col style="width: 60px">
    <col style="width: 100px">
    <col style="width: 209px">
    <col style="width: 50px">
    <col style="width: 60px">
    <col style="width: 209px">
    <col style="width: 209px">
    <col style="width: 80px">
    <col style="width: 110px">
    <col style="width: 60px">
</colgroup>
  • 來分隔表格寬度

35. JQ $(dom).html()儲存的html中沒有input和textarea的值
- 監聽textarea和input的input事件 賦值在DOM結構上即可

saveDom()
function saveDom(){
    var changeInputTimeId = null,
        changeTextTimeId = null;
    $('input').on( 'input', function(e){
        clearTimeout( changeInputTimeId )
        var $this = $(this);
        setTimeout( function(){
            $this.attr( 'value', $this.val() )
        }, 200 )
    } )

    $('textarea').on( 'input', function(e){
        clearTimeout( changeTextTimeId )
        var $this = $(this);
        setTimeout( function(){
            // TEXTAREA必須是val和text
            $this.text( $this.val() )
        }, 200 )
    } )
}

36. 迴流(reflow)和重繪(repaint)

傳送門 DOM操作成本到底高在哪兒?

  • reflow(迴流): 根據Render Tree佈局(幾何屬性),意味著元素的內容、結構、位置或尺寸發生了變化,需要重新計算樣式和渲染樹;
  • repaint(重繪): 意味著元素髮生的改變隻影響了節點的一些樣式(背景色,邊框顏色,文字顏色等),只需要應用新樣式繪製這個元素就可以了;
  • reflow迴流的成本開銷要高於repaint重繪,一個節點的迴流往往回導致子節點以及同級節點的迴流;
  • 引起reflow迴流

    1. 頁面第一次渲染(初始化)
    2. DOM樹變化(如:增刪節點)
    3. Render樹變化(如:padding改變)
    4. 瀏覽器視窗resize
    5. 獲取元素的某些屬性:
    6. 瀏覽器為了獲得正確的值也會提前觸發迴流,這樣就使得瀏覽器的優化失效了,這些屬性包括offsetLeft、offsetTop、offsetWidth、offsetHeight、 scrollTop/Left/Width/Height、clientTop/Left/Width/Height、呼叫了getComputedStyle()或者IE的currentStyle
  • 引起repaint重繪

    1. reflow迴流必定引起repaint重繪,重繪可以單獨觸發
    2. 背景色、顏色、字型改變(注意:字型大小發生變化時,會觸發迴流)
  • 優化方式

    1. 避免逐個修改節點樣式,儘量一次性修改
    2. 使用DocumentFragment將需要多次修改的DOM元素快取,最後一次性append到真實DOM中渲染
    3. 可以將需要多次修改的DOM元素設定display: none,操作完再顯示。(因為隱藏元素不在render樹內,因此修改隱藏元素不會觸發迴流重繪)
    4. 避免多次讀取某些屬性(見上)
    5. 將複雜的節點元素脫離文件流,降低迴流成本
  • 為什麼一再強調將css放在頭部,將js檔案放在尾部

    • DOMContentLoaded 和 load
      1. DOMContentLoaded 事件觸發時,僅當DOM載入完成,不包括樣式表,圖片…
      2. load 事件觸發時,頁面上所有的DOM,樣式表,指令碼,圖片都已載入完成
    • CSS 資源阻塞渲染
      1. 構建Render樹需要DOM和CSSOM,所以HTML和CSS都會阻塞渲染。所以需要讓CSS儘早載入(如:放在頭部),以縮短首次渲染的時間。
    • JS 資源
      1. 阻塞瀏覽器的解析,也就是說發現一個外鏈指令碼時,需等待指令碼下載完成並執行後才會繼續解析HTML
      2. 普通的指令碼會阻塞瀏覽器解析,加上defer或async屬性,指令碼就變成非同步,可等到解析完畢再執行
        • async非同步執行,非同步下載完畢後就會執行,不確保執行順序,一定在onload前,但不確定在DOMContentLoaded事件的前後
        • defer延遲執行,相對於放在body最後(理論上在DOMContentLoaded事件前)

37. 滾動的圖片視差效果demo

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>滾動的圖片視差效果demo</title>
    <style>
        body{
            margin: 0;
            background: url('../images/timg.jpg')no-repeat;
            background-size: 100% 100%;
        /*這個屬性把背景圖固定住*/
            background-attachment: fixed;
        }
        header, footer{
            height: 800px;
            background: #fff;
        }
        section{
            height: 100px;
        /*背景色透明*/
            background: transparent;
        }

    </style>
</head>
<body>
    <header></header>
    <section>66</section>
    <footer></footer>
</body>
</html>

38. NVM安裝後,以前裝的Node的環境變數沒了
- 自己沒好好看安裝的提示文字吧
- 第一個安裝路徑是NVM的安裝路徑
- 第二個安裝路徑是你已經安裝的Node環境的安裝路徑,比如我的node在安裝NVM之前裝在D:\node, NVM安裝的第二個路徑就要是這個路徑下,這樣你的Node環境變數就不會丟失了

39. 移動端web頁面上使用軟鍵盤時如何讓其顯示“前往”(GO)而不是換行?
- 用一個 form 表單包裹住就會顯示前往,單獨的一個 input 就會提示換行。

40. vue使用bootstrap的響應式佈局
1. http://v3.bootcss.com/customize/ 到這裡去定製bootstrap 所有的都不勾選 只勾選 grid
2. 下載下來 引入到你的vue專案用
3. 和使用bootstarp一樣佈局吧 親測好用

41. VUE許可權控制——動態路由的方案
- https://github.com/tianxiadaluan/vue-checkAuth 王大哥總結的 可以學習一波

42. Vue ElementUI 的導航欄重新整理後預設選擇的沒了

    <el-aside width="200px" style="background-color: rgb(238, 241, 246)">
        <!-- 設定成route模式 然後設定預設選擇的路由 -->
        <el-menu :default-openeds="openIndex" :router="true" :default-active="this.$route.path">
            <el-submenu index="1">
                <template slot="title"><i class="el-icon-message"></i>導航一</template>
                <el-menu-item-group>
                <template slot="title">分組一</template>
                <!-- 設定成route模式 index設定成路由 -->
                <el-menu-item index="/table" >表格1
                </el-menu-item>
                <el-menu-item index="/table2">表格2
                </el-menu-item>
                </el-menu-item-group>
                <el-menu-item-group title="分組2">
                <el-menu-item index="1-3">選項3</el-menu-item>
                </el-menu-item-group>
                <el-submenu index="1-4">
                <template slot="title">選項4</template>
                <el-menu-item index="1-4-1">選項4-1</el-menu-item>
                </el-submenu>
            </el-submenu>
        </el-menu>
    </el-aside>

43. 關於toString和valueof的面試題

var a = {};
var b = {key: 'b'};
var c = {key: 'c'};
var d = [3,5,6];
a[b] = 123;
a[c] = 345;
a[d] = 333;
console.log(a[b]);
console.log(a[c]);
console.log(a[d]);
// 345
// 345
// 333
????? 沒錯 沒有搞錯  why
/*
Object內建toString 和 valueOf 方法;
這種情況a[b] = 123 會預設呼叫物件的toString()
所以就是 a['object Objcet'] = 123;
        a['object Object'] = 345;
        a['object Array'] = 333;
        */
/*
如果是多個物件
var g = [ {name: 666}, {age: 666}, {job: 777} ]
g.toString()
"[object Object],[object Object],[object Object]"
a[g]  其實就是 a["[object Object],[object Object],[object Object]"]
*/      

44. ‘false‘如何轉成布林後仍然是false

const str = 'false';
Boolean(str) // true
JSON.parse(str) // false  
// 使用JSON.parse()最好try-catch 避免報錯

45. 字串不會隱式轉換的

'0' == ''
false
'0' == false
true
'' == false
true

46. 一個數組交叉合併題

const arr = [ ["1", "2", "3"], [ "a", "b" ] ];
for( let i = 0, l = arr[0].length; i < l ; i++ ){
    newArr.push( arr[0][i] + arr[1][0], arr[0][i] + arr[1][1] )
}
// ["1a", "1b", "2a", "2b", "3a", "3b"]

47. 函式柯里化

// 固定引數實現
const result = x => y => z => x * y * z;
result(3)(4)(4) // 48;
// 柯里化實現
function curry(fn) {
    const len = fn.length;
    return function curried() {
        const args = Array.prototype.slice.call(arguments);
        if (args.length >= len) {
            return fn.apply(this, args);
        }            
        return function () {
            return curried.apply(this, args.concat(Array.prototype.slice.call(arguments)));
        };
    };
}
const result = curry(function (a, b, c) {
    return a * b * c;
});
result(3)(4)(4); // 48

48. 空陣列迴圈的問題

let arr = [];
arr[4] = 'a';
console.log(arr) // [ emptyx3, 4 ]
// 如果要迴圈出來 需要用for 使用forEach 等一些高階函式 會過濾空的陣列

49. Array.sort()
- Array.sort()預設是按照字符集排序的

let arr = [6, 1,0, 8, 9, 10, 15, 20, 9];
// 字符集
arr.sort() // (9) [0, 1, 10, 15, 20, 6, 8, 9, 9];

// 老老實實回撥不能少
arr.sort((a, b) => {
    return a - b;
})

50. 陣列含有[0001]這種型別的問題

var arr = [['0001', '0010'], ['0020', '0300'], ['0301', '0400']]
console.log(JSON.stringify(arr)) // [["0001","0010"],["0020","0300"],["0301","0400"]]

var arr = [[0001, 0010], [0020, 0300], [0301, 0400]]
console.log( JSON.stringify( arr ) ) // [[1,8],[16,192],[193,256]]

51. 正則g的問題

原文

-在建立正則表示式物件時如果使用了“g”識別符號或者設定它了的global屬性值為ture時,那麼新建立的正則表示式物件將使用模式對要將要匹配的字串進行全域性匹配。在全域性匹配模式下可以對指定要查詢的字串執行多次匹配。每次匹配使用當前正則物件的lastIndex屬性的值作為在目標字串中開始查詢的起始位置。 lastIndex屬性的初始值為0,找到匹配的項後lastIndex的值被重置為匹配內容的下一個字元在字串中的位置索引,用來標識下次執行匹配時開始查詢的位置。如果找不到匹配的項lastIndex的值會被設定為0。 當沒有設定正則物件的全域性匹配標誌時lastIndex屬性的值始終為0,每次執行匹配僅查詢字串中第一個匹配的項。可以通過regex.lastIndex來訪問在執行匹配相應的lastIndex 屬性的值。
- 多次使用同一個g正則匹配同個字串可能會出現問題

52. animation-fill-mode: forwards 影響z-index關係
- #content > .modal #content包裹著modal彈窗
- modal的遮罩層和#content是兄弟節點關係
- #content 是absolute定位 但是沒有設定z-index modalz-index是1050 遮罩層是1040
- 當使用animation-fill-mode: forwards時 modal的z-index失效 或是受#content的無z-index影響 導致遮罩擋著彈窗 具體原因 還不清楚

53. 傳統a連結跳轉的網頁也可以加過度動畫的
- 具體動畫看你怎麼加嘍 體驗也很棒

<head>
    <style>
        .content{
            opacity: 0;
            transition: opacity 0.5s;
        }
        .content.active{
            opacity: 1;
        }
    </style>
</head>
<body>
    <nav>
        <ul>
            <li><a href="/">首頁</a