前端知識點總結——JS高階(持續更新中)

楷楷發表於2019-02-16

前端知識點總結——JS高階(持續更新中)

1.字串

什麼是: 連續儲存多個字元的字元陣列
相同: 1. 下標 2. .length 3. 遍歷

  4. 選取: slice(starti[, endi])

不同: 型別不同 API不通用
API: 所有字串API都無權修改原字串,總是返回新字串

  1. 大小寫轉換:
    統一轉大寫: str=str.toUpperCase()
    統一轉小寫: str=str.toLowerCase()
    何時: 不區分大小寫時,都需要先轉為一致的大小寫,再比較。

說明: 驗證碼本不該客戶端做,應該由伺服器端完成

2.獲取指定位置的字元:

str.charAt(i) => str[i]
獲取指定位置字元的unicode號
str.charCodeAt(i)

  將unicode號轉為漢字: String.fromCharCode(unicode)

3.獲取子字串:

str.slice(starti,endi+1)
強調: 如果一個API,兩個引數都是下標,則後一個引數+1(含頭不含尾)
str.substring(starti,endi+1) 用法和slice完全一樣
強調: 不支援負數引數
str.subStr(starti,n) 從starti開始,取n個
強調: 第二個引數不是下標,所以,不用考慮含頭不含尾

4.查詢: 4種:

  1. 查詢一個固定的關鍵詞出現的位置:
    var i=str.indexOf(“關鍵詞”[,fromi])
    在str中,fromi位置後,找下一個”關鍵詞”出現的位置
    如果找到,返回關鍵詞第一個字的下標位置
    如果沒找到,返回-1
    說明: fromi可省略,預設從0開始

    var i=str.lastIndexOf(“關鍵詞”);
    在str中,查詢”關鍵詞”最後出現的位置

    問題: 只能查詢一個固定的關鍵詞
    臥我草/操/艹/槽
    微 信 w x wei xin
    解決: 用正則查詢:

  2. 判斷是否包含關鍵詞:
    var i=str.search(/正則/)
    返回值: 如果找到,返回關鍵詞的位置

         如果沒找到,返回-1

    問題: 預設,所有正則都區分大小寫
    解決: 在第二個/後加i ignore 忽略
    問題: 只能獲得位置,無法獲得本次找到的敏感詞的內容

  3. 獲得關鍵詞的內容:
    var arr=str.match(/正則/i);
    2種情況:

    1. 不加g的情況: 只能返回第一個找到的關鍵詞內容和位置: [ 0: “關鍵詞內容”, index: 位置 ]
    2. 加g: 返回所有找到的敏感詞的內容,儲存在陣列中。g: global

強調: 如果找不到,返回null

 警告: 凡是一個函式可能返回null!都要先判斷不是null,才能用!

問題: 只能獲得關鍵詞內容,無法獲得位置

  1. 即找每個關鍵詞內容,又找每個關鍵詞位置:
    reg.exec()

5.替換:

什麼是: 將找到的關鍵詞替換為指定的內容
如何: 2種:

  1. 簡單替換: 將所有敏感詞無差別的替換為統一的新值
    str=str.replace(/正則/,”替換值”)
  2. 高階替換: 根據每個敏感詞的不同,分別替換不同的值
    str=str.replace(/正則/,function(kw){

     //kw: 會自動獲得本次找到的一個關鍵詞
     return  根據kw的不同,動態生成不同的替換值

    })

衍生: 刪除關鍵詞:
str=str.replace(/正則/,””)

6.正規表示式: Regular Expression

什麼是: 描述一個字串中字元出現規律的規則的表示式
何時: 2種:

  1. 查詢關鍵詞:
  2. 驗證:

如何: 正規表示式語法:

  1. 最簡單的正則其實是關鍵詞原文:

7.字符集:

什麼是: 規定一位字元,備選字元列表的集合
何時: 只要一位字元,有多種備選字時
如何: [備選字元列表]
強調: 一個[]只能匹配一位字元
簡寫: 如果備選字元列表中部分字元連續

可簡寫為: [x-x]  用-省略中間字元
 比如: [0-9] 一位數字
      [a-z] 一位小寫字元
      [A-Z] 一位大寫字母
      [A-Za-z] 一位字元
      [0-9A-Za-z] 一位字母或數字
      [u4e00-u9fa5] 一位漢字

反選: 1 除了4和7都行

8.預定義字符集: 4種:

d 一位數字 [0-9]
w 一位數字,字母或下劃線 [0-9A-Za-z_]
強調: 只有100%匹配時,才使用w,如果不允許有_,則使用自定義字符集
s 一位空字元,比如: 空格,Tab,…
. 萬用字元
問題: 字符集只能規定字元的內容,無法靈活規定字元的個數

9.量詞:

什麼是: 專門規定一個字符集出現次數的規則
何時: 只要規定字符集出現的次數,都用量詞
如何: 字符集量詞
強調: 量詞預設只修飾相鄰的前一個字符集
包括: 2大類:

1. 有明確數量邊界:
 {6,8}  最少6次,最多8次
 {6,}   最少6次,多了不限
 {6}    必須6次,不能多也不能少
2. 沒有明確數量邊界:
 ?     可有可無,最多1次
 *     可有可無,多了不限
 +     至少1次,多了不限

10.選擇和分組:

  1. 選擇: 或
    規則1|規則2
    何時: 只要在兩個規則中任選其一匹配
  2. 分組: (規則1規則2…)
    何時: 如果希望一個量詞同時修飾多個規則時,都要先將多個規則分為一組,再用量詞修飾分組。

    比如: 車牌號: [u4e00-u9fa5][A-Z]•[0-9A-Z]{5}
    比如: 手機號規則: 
    +86或0086  可有可無,最多1次
    空字元      可有可無,多了不限
     1
     在3,4,5,7,8中選一個

    9位數字
    (+86|0086)?s*1[34578]d{9}

     比如: 身份證號:

    15位數字 2位數字 一位數字或X

    可有可無,最多一次
    d{15}(d{2}[0-9X])?
    比如: 電子郵件: 鄙視

    /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/
    比如: url:
    (https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]

11.匹配特殊位置: 3個:

  1. 字串開頭: ^
  2. 字串結尾: $
    比如: 開頭的空字元: ^s+

     結尾的空字元: s+$
     開頭或結尾的空字元: ^s+|s+$

    3.單詞邊界: b 包括開頭,結尾,空字元,標點符號

    比如: 單詞首字母: [a-z]
    匹配單詞: xxx
    

12.String:

替換: 2種: 如果關鍵詞是固定的:
str=str.replace(“關鍵詞”,”替換值”);
如果關鍵詞變化
str=str.replace(/正則/ig,”替換值”);
切割: 2種: 如果分隔符是固定的:
var substrs=str.split(“分隔符”)
如果分隔符不是固定的
var substrs=str.split(/正則/i)
固定套路: 將字串打散為字元陣列

 var chars=str.split("")

13.RegExp:

什麼是: 儲存一條正規表示式,並提供用正規表示式執行驗證和查詢的API
何時: 只要用正則查詢關鍵詞或驗證字串格式時
如何:
建立: 2種:

  1. 直接量: var reg=/正則/ig
    何時: 只要正規表示式的規則是固定不變的。
    問題: 正規表示式時固定不變的,不支援動態生成
  2. new: var reg=new RegExp(“正則”,”ig”);
    何時: 只要需要動態生成正規表示式

API: 2個:

  1. 驗證: var bool=reg.test(str)
    問題: 預設,只要找到匹配的內容,就返回true,不要求完整匹配!
    解決: 今後,凡是驗證必須前加^,後加$
  2. 查詢: 即找每個關鍵詞位置,又獲得每個關鍵詞內容
    var arr=reg.exec(str)
    在str中查詢下一個關鍵詞的位置和內容
    返回值: arr:[ 0: 內容, index: 位置 ]
    如果找不到,返回null
    如果找所有: 只要用while反覆呼叫reg.exec即可,exec可自動跳到下一個查詢位置

14.Math

什麼是: 儲存數學計算的常量和API的物件
何時: 進行算術計算
如何:
建立: 不用建立,所有API都用Math直接呼叫
API:

    1. 取整:
      上取整: Math.ceil(num)
      下取整:
      Math.floor(num)
      parseInt(str) 去掉字串結尾非數字字元(單位)
      四捨五入取整:
      Math.round(num)
      優: 返回數字型別,可直接計算
      缺: 不能隨意指定小數位數
      n.toFixed(d)
      優: 可隨意指定小數位數
      缺: 返回字串型別,不能直接做加法
      自定義round
    1. 乘方和開平方:
      Math.pow(底數,冪)
      Math.sqrt(num)
    2. 最大值和最小值
      Math.max(值1, 值2,…)
      Math.min(值1, 值2,…)
      問題: 不支援陣列
      解決: Math.max(…arr)
    3. 隨機數:
      Math.random() 0~1 隨機小數
      公式: 在min到max之間取一個隨機整數
      parseInt(Math.random()*(max-min+1)+min)
      簡寫: 在0~max之間取一個隨機整數
      parseInt(Math.random()*(max+1))
    4. 三角函式:
      已知角度,求邊長,用三角函式: sin cos tan
      已知邊長,求角度,用反三角函式: asin acos atan
      僅以atan:
      var 弧度=Math.atan(對邊長/鄰邊長)

      360角度=2π弧度

      問題: atan無法區分角度的象限
      解決: Math.atan2(對邊長, 鄰邊長);

    15.Date:

    什麼是: 儲存一個時間,提供操作時間的API
    何時: 只要在程式中儲存時間或操作時間,都用date
    如何:
    建立: 4種:

    1. 建立日期物件,並自動獲得客戶端當前系統時間
      var now=new Date();
    2. 建立日期物件,儲存自定義時間
      var now=new Date(“yyyy/MM/dd hh:mm:ss”);
    3. 用毫秒數建立日期物件:
      var date=new Date(ms)
    4. 複製一個日期物件:
      問題: 日期的計算都是直接修改原日期物件
      解決: 如果希望同時保留計算前後的開始和結束時間,都要先複製開始時間,再用副本計算結束時間
      var date2=new Date(date1)

    本質: 起始日期物件內部儲存的是一個巨大的毫秒數:

    1970年1月1日至今的毫秒數

    文字儲存日期的問題:

    1. 有時區問題:
    2. 不便於計算:

    毫秒數儲存日期:

    1. 不受時區的干擾: 
    2. 便於計算: 

    總結: 將來在網路中傳輸或在資料庫中儲存時間,都用毫秒數

    16.API:

    1. 8個單位:
      FullYear Month Date Day
      Hours Minutes Seconds Milliseconds
    2. 每個單位上都有一對兒get/set方法:
      getXXX() 負責獲得單位的值
      setXXX() 負責修改單位的值
      特例: Day 不能修改,沒有setDay()
    3. 取值範圍:
      Month: 0~11 計算機中的月份總是比現實中小1
      Date: 1~31
      Day: 0~6
      Hours: 0~23
      Minutes/Seconds: 0~59

    日期計算:

    1. 兩日期相減: 得到的是毫秒差
      何時: 計算時間段或計算倒數計時
    2. 對任意單位做加減: 3步:

      1. 獲得單位的當前值
      2. 做加減
      3. 將計算後的結果set回去
        setXXX()可自動調整時間進位制

    可簡化為: date.setXXX(date.getXXX()+n)
    問題: setXXX()直接修改原日期
    解決: 如果同時儲存計算前後的開始和結束時間,應該先複製副本,再用副本計算。

    17.Date:

    日期格式化:
    date.toString() 預設當地時間的完整版格式
    date.toLocaleString() 轉為當地時間的簡化版格式
    date.toLocaleDateString() 僅保留日期部分
    date.toLocaleTimeString() 僅保留時間部分

    18.Error:

    什麼是錯誤: 程式執行過程中,遇到的無法繼續執行的異常情況
    程式出錯,都會強行中斷退出。
    什麼是錯誤處理: 即使程式出錯!也保證不會中斷退出
    何時: 如果希望程式,即使出錯,也不會強行中斷退出
    如何:
    try{
    可能出錯的正常程式碼
    }catch(err){
    //err: 錯誤物件, 自動儲存了錯誤的資訊
    只有出錯才執行的錯誤處理程式碼:
    提示錯誤資訊, 記錄日誌, 釋放資源
    }
    問題: 效率略低
    解決: 多數try catch,都能用if…else代替

    主動丟擲錯誤:
    throw new Error(“錯誤資訊”)
    鄙視: js中共有幾種錯誤型別: 6種:
    SyntaxError 語法錯誤
    ReferenceError 引用錯誤
    TypeError 型別錯誤
    RangeError 範圍錯誤 引數超範圍

    EvalError URIError

    19.Function:

    什麼是函式: 儲存一段程式碼段的物件,再起一個名字。
    為什麼: 程式碼重用
    何時: 只要一段程式碼可能被重複使用時!
    如何:
    建立: 3種:

    1. 宣告: function 函式名(引數列表){

            函式體;
            return 返回值;
          }

      引數: 呼叫函式時,接收傳入函式的資料的變數
      何時: 如果函式自身必須某些資料才能正常執行時,就必須定義引數,從外部接收必須的資料
      返回值: 函式的執行結果
      何時: 如果呼叫者需要獲得函式的執行結果時
      呼叫: var 返回值=函式名(引數值列表);
      問題: 宣告提前: 在程式開始執行前,先將var宣告的變數和function宣告的函式,提前到當前作用域的頂部集中建立。賦值留在原地。
      解決:

    2. 直接量: var 函式名=function (引數列表){
      特點: 不會被宣告提前
      揭示: 函式名其實只是一個變數

        函式其實是一個儲存程式碼段的物件
        函式名通過物件地址引用函式物件
    3. new :
      var 函式名=
      new Function(“引數1″,”引數2″,…,”函式體”)

    20.過載overload:

    什麼是: 多個相同函式名,不同引數列表的函式,在呼叫時,可根據傳入的引數不同,自動執行不同的操作。
    為什麼: 為了減少API的數量,減輕呼叫者的負擔
    何時: 只要一項任務,可能根據傳入引數的不同,執行不同的流程時。
    如何: js語法預設不支援過載!

     因為: js中不允許多個同名函式,同時存在。最後一個函式會覆蓋之前的。
    變通實現: arguments
     什麼是: 每個函式中,自動包含的,接收所有傳入函式的引數值的類陣列物件
       類陣列物件: 長得像陣列的物件
         vs 陣列: 相同: 1. 下標, 2. .length, 3. 遍歷
                 不同: 型別不同, API不通用
    

    21.匿名函式:

    什麼是: 定義函式時,不指定函式名
    為什麼: 節約記憶體 或 劃分臨時作用域
    何時:

    1. 只要一個函式,希望呼叫後,立刻自動釋放!
    2. 劃分臨時作用域:

    如何:

    1. 回撥: 定義函式後,自己不呼叫,而是傳遞給另一個函式去呼叫
    2. 自調: 定義函式後,立刻呼叫自己。
      何時: 今後所有js程式碼必須都放在匿名函式自調中,避免全域性汙染。

    22.垃圾回收:

    什麼是垃圾: 一個不再被任何變數使用的物件
    什麼是垃圾回收: js引擎會自動回收不再被使用的物件的空間。
    為什麼: 記憶體空間都是有限的!
    垃圾回收器: 專門負責回收垃圾物件的小程式——js引擎自帶
    如何:

    1. 程式執行時,垃圾回收器伴隨主程式執行而執行。
    2. 每建立一個物件,垃圾回收器就會記錄物件被幾個變數引用著.
    3. 如果發現一個物件不再被任何變數應用,則自動回收該物件的儲存空間。

    好的習慣: 只要一個物件不再使用,就要賦值為null

    23.作用域和作用域鏈

    作用域(scope): 一個變數的可用範圍
    為什麼: 避免內部的變數影響外部
    本質: 是一個儲存變數的物件
    包括: 2種:

    1. 全域性作用域: window
      儲存全域性變數: 隨處可用,可反覆使用
    2. 函式作用域: ?
      儲存區域性變數: 僅在函式內可用,且不可重用!

    24.函式生命週期:

    1. 程式開始執行前
      在記憶體中建立執行環境棧(陣列): 用於儲存正在呼叫的函式任務。
      在執行環境站中新增第一條記錄: 呼叫瀏覽器主程式
      建立全域性作用域物件window: 2個作用:

      1. 儲存瀏覽器自己需要的資料和物件
      2. 作為程式的全域性作用域物件,儲存全域性變數
    2. 定義函式時:
      在window中定義函式名變數
      建立函式物件儲存函式定義
      函式名變數引用函式物件
      函式物件的scope屬性,又指回了函式建立時的作用域
    3. 呼叫函式時
      在執行環境棧中新增了本次函式呼叫的記錄
      建立本次函式呼叫的函式作用域物件AO
      在AO中新增函式的區域性變數
      設定AO的parent指向函式的scope
      執行環境棧中的函式呼叫記錄,引用AO
      變數的使用順序: 先用區域性,再用全域性
    4. 函式呼叫後
      本次函式呼叫的記錄從執行環境棧中出棧
      導致AO被釋放, 導致所有區域性變數都釋放

    25.作用域鏈:

    什麼是: 由多級作用域物件,逐級引用形成的鏈式結構
    2個作用:

    1. 儲存所有變數
    2. 控制著變數的使用順序!

    26.閉包closure:

    什麼是: 即重用一個變數,又保護變數不被汙染的一種機制
    為什麼: 全域性變數和區域性變數都具有不可兼得的優缺點:
    全域性變數: 優: 可重用, 缺: 易被汙染
    區域性變數: 優: 僅函式內可用,不會被汙染

           缺: 不可重用!

    何時: 只要一個變數,可能被重用,又不想被篡改
    如何: 3步:

    1. 用外層函式包裹要保護的變數和內層函式
    2. 外層函式將內層函式返回到外部
    3. 呼叫外層函式,獲得內層函式的物件,儲存在外部的變數中——形成了閉包

    閉包形成的原因: 外層函式呼叫後,外層函式的函式作用域物件無法釋放
    主動使用閉包: 為一個函式繫結一個專屬的變數
    鄙視: 畫簡圖

    1. 找受保護的變數,並確定其最終值
    2. 找內層函式物件
      外層函式向外返回內層函式物件: 3種:

      1. return function(){}
      2. 全域性變數=function(){}
      3. return arr/obj{function(){…}}

    27.OOP

    什麼是物件: 記憶體中儲存多個資料的獨立儲存空間都稱為一個物件。
    什麼是物件導向: 程式中都是用物件結構來描述現實中一個具體事物。
    為什麼: 為了便於大量資料的維護和查詢
    何時: 幾乎所有js程式,都使用物件導向的方式開發
    如何: 三大特點: 封裝,繼承,多型
    封裝: 用物件來集中描述現實中一個具體事物的屬性和功能
    為什麼: 便於維護和查詢
    何時: 今後只要使用物件導向的方式開發,都要先封裝物件,再按需使用物件的屬性和功能。
    如何: 3種:

    1. 用{}:
      var obj={
      屬性名:值,

        ... : ... ,

      //方法名:function(){…},
      方法名 (){…},
      }
      其中: 事物的屬性值會成為物件的屬性

          物件的屬性本質是儲存在物件中的一個變數
       事物的功能會成為物件的方法!
          方法的本質是儲存在物件中的一個函式

      如何訪問物件的成員:
      訪問物件的屬性: 物件.屬性名
      呼叫物件的方法: 物件.方法名()
      問題: 物件自己的方法中要使用物件自己的屬性
      錯誤: 直接用屬性名,報錯: 找不到變數

      為什麼: 預設,不加.就使用的變數,只能在作用域鏈中查詢,無法自動進入物件中

      解決一: 物件名.屬性名

      問題: 物件名僅是一個普通的變數名,可能發生變化。

      正確解決: this.屬性名
      this: 自動指正在呼叫當前方法的.前的物件

      為什麼: 不受物件名變數的影響
      何時: 只要物件自己的方法向訪問物件自己的屬性時,都必須加this.

    js中物件的本質,其實就是一個關聯陣列

    1. 用new:
      var obj=new Object(); //建立空物件 等效於{}
      obj.屬性名=值;
      obj.方法名=function(){
      … this.屬性名 …
      }

    和關聯陣列一樣,js中的物件也可隨時新增新屬性和方法。
    問題: 反覆建立多個相同結構的物件時,重複程式碼太多,導致不便於維護
    解決:

    1. 用建構函式:
      建構函式: 描述一類物件統一結構的函式
      為什麼: 為了重用結構程式碼!
      何時: 只要反覆建立相同結構的多個物件時,都用建構函式
      如何: 2步:

      1. 定義建構函式
        function 型別名(屬性引數列表){
        this.屬性名=屬性引數;
        this. … = 屬性引數;
        this.方法名=function(){

        this.xxx

        }
        }

      2. 呼叫建構函式建立新物件
        var obj=new 型別名(屬性值列表)
        new: 1. 建立新的空物件

         2. 設定新物件繼承建構函式的原型物件
         3. 用新物件呼叫建構函式
           將建構函式中的this都指向新物件
         4. 返回新物件的地址

    問題: 建構函式只能重用程式碼,無法節約記憶體!
    解決: 繼承:

    28.繼承:

    什麼是: 父物件的成員,子物件無需建立,就可直接使用
    為什麼: 程式碼重用,節約記憶體
    何時: 只要多個子物件,擁有相同的成員時,都應只在父物件中定義一份,所有子物件共用即可!
    如何: js中繼承都是通過原型物件實現的

    什麼是原型物件: 集中儲存同一型別的所有子物件,共用成員的父物件
    何時: 只要繼承,必然原型物件
    如何: 
     建立: 不用建立,買一贈一
       每建立一個建構函式,都附贈一個原型物件 
     繼承: 在建立子物件時,new的第2步自動設定子物件繼承建構函式的原型物件
     訪問成員: 優先訪問自有成員
              自己沒有,就去父物件(原型物件)中查詢
     將成員新增到原型物件中: 
      建構函式.prototype.成員=值

    自有屬性和共有屬性:
    自有屬性: 儲存在當前物件本地,僅歸當前物件獨有的屬性
    共有屬性: 儲存在父物件中,所有子物件共有的屬性
    讀取屬性值: 子物件.屬性
    修改屬性值: 自有屬性,必須通過子物件自己修改

               共有屬性,只能用原型物件修改!

    內建物件的原型物件:
    鄙視: 內建物件: 11個:

    String Number Boolean ——包裝型別物件
    Array Date RegExp Math
    Error
    Function  Object
    Global (在瀏覽器中,被window代替)

    鄙視: 包裝型別的理解

    什麼是: 儲存一個原始型別的值,並提供操作原始型別值的API
    為什麼: 原始型別的值本身不具有任何功能
    何時: 只要試圖對原始型別的值呼叫API時,都會自動使用包裝型別物件來幫助原始型別的值執行操作。
    如何: 
     1. 記憶體中已經預置了三大包裝型別的物件:
       String  Number  Boolean
     2. 在試圖對原始型別的值呼叫API時,自動檢測原始型別的值的型別名
       var n=345.678;
         typeof n => number
     3. 根據型別名例項化對應的包裝型別物件,呼叫其API
       new Number(n).toFixed(2) => 345.68
     4. 執行後,包裝型別物件自動釋放
       new Number釋放!
    

    29.OOP

    物件導向三大特點: 封裝,繼承,多型
    繼承:
    原型物件:
    內建型別的原型物件:
    一種型別: 包含兩部分:

    1. 建構函式: 建立該型別的子物件
    2. 原型物件: 儲存所有子物件的共有成員

    解決瀏覽器相容性問題: 舊瀏覽器無法使用新API

    1. 判斷當前瀏覽器對應型別的原型物件中是否包含該API
    2. 如果不包含,則自定義該API,新增到對應型別的原型物件中
    

    30.原型鏈:

    什麼是: 由多級父物件,逐級繼承形成的鏈式結構
    儲存著: 所有物件的屬性
    控制著: 物件屬性的使用順序:

    先自有,再共有

    鄙視: 如何判斷一個物件是陣列型別? 有幾種方法
    錯誤: typeof : 只能區分原始型別,函式,無法進一步區分引用型別物件的具體型別名
    正確: 4種:

    1. 判斷原型物件:
      obj.__proto__==Array.prototype
      Array.prototype.isPrototypeOf(obj)
    2. 判斷建構函式:
      obj.constructor==Array
      obj instanceof Array

    問題: 不嚴格, 不但檢查直接父物件,且檢查整個原型鏈

    1. 判斷物件內部的class屬性

    class屬性: 物件內部的專門記錄物件建立時的型別名的屬性
    問題1: class屬性是內部屬性,無法用.直接訪問
    解決: 唯一的辦法: Object.prototype.toString()
    問題2: 每種型別的原型物件都重寫了各自不同的toString()方法,子物件無法呼叫到Object.prototype.toString()
    解決: fun.call(obj) 讓obj強行呼叫任何一個fun

    Object.prototype.toString.call(obj)
      在執行的一瞬間: obj.toString()
      結果:"[object Class]"
    

    鄙視: 何時將方法定義在原型物件中,何時將方法定義在建構函式上
    例項方法和靜態方法:
    例項方法: 必須該型別的子物件才能呼叫的方法
    比如: arr.sort() arr.push()
    何時: 只要要求必須該型別的子物件才能呼叫
    如何: 所有放在原型物件中的方法都是例項方法
    靜態方法: 不需要建立該型別的子物件,任何物件都可使用的方法。
    比如: Array.isArray(fun)

        Array.isArray(date)
        Array.isArray(obj)

    何時: 不確定將來呼叫該函式的物件型別時
    如何: 新增到建構函式物件上的方法都是靜態方法。可通過建構函式.靜態方法方式直接呼叫!

    31.多型:

    什麼是: 一個方法在不同情況下表現出不同的狀態
    包括:

    1. 過載overload:
    2. 重寫override:
      什麼是: 如果子物件覺得從父物件繼承來的成員不好用,可在本地定義同名的自有成員,覆蓋父物件的成員
      為什麼: 覺得從父物件繼承來的成員不好用
      何時: 只要覺得從父物件繼承來的成員不好用
      如何: 在本地定義同名的自有成員

    32.自定義繼承:

    1. 只修改一個物件的父物件
      obj.__proto__=father
      Object.setPrototypeOf(obj,father)
    2. 修改所有子物件的父物件:
      建構函式.prototype=father
      時機: 在建立子物件之前換!
    3. 兩種型別間的繼承:
      何時: 發現多個型別之間擁有部分相同的屬性結構和方法定義時,都要抽象父型別出來
      如何: 2步:

      1. 定義抽象父型別: 2步:

        1. 定義建構函式儲存公共的屬性結構
        2. 定義原型物件儲存公共的方法
      2. 讓子型別繼承父型別: 2步:

        1. 在子型別建構函式中借用父型別建構函式
          錯誤: 直接呼叫: Flyer(fname,speed)
          原因: Flyer不用.不用new呼叫,其中的this預設指window,Flyer中所有屬性洩露到全域性
          正確: 用call將正確的this注入到Flyer中,代替錯誤的this
          如何: Flyer.call(正確的this, fname,speed)
        2. 讓子型別原型物件繼承父型別原型物件
          Object.setPrototypeOf(子型別原型,父型別原型)

    33.ECMAScript6

    1. 嚴格模式:
      什麼是: 比普通js執行機制要求更嚴格的模式
      為什麼: 普通的js執行機制有很多廣受詬病的缺陷
      何時: 今後所有專案必須執行在嚴格模式下

      1. 如果新專案,整個js檔案啟用嚴格模式
      2. 舊專案,逐個函式啟用嚴格模式

    如何:

    1. 在script或整個js檔案頂部,新增”use strict”;
    2. 在函式內頂部,新增”use strict”;

    規則: 4個:

    1. 禁止給未宣告的變數賦值
    2. 將靜默失敗升級為錯誤
    3. 普通函式或匿名函式自調中的this預設不再指向window,而是undefined
    4. 禁止使用arguments, arguments.callee,…

    補: arguments.callee 自動獲得當前正在呼叫的函式本身
    禁用,說明強烈不推薦使用遞迴!

    34.保護物件:

    保護物件的屬性:
    ES5將物件屬性分為:
    命名屬性: 可用.直接訪問到的屬性
    資料屬性: 直接儲存屬性值的屬性
    保護資料屬性: 4大特性:

    一個屬性包含四大特性:{
      value: 實際儲存屬性值,
      writable: true/false, //只讀
      enumerable: true/false, //不可遍歷
        //不是徹底隱藏,用.依然可訪問!
      configurable:true/false //1. 禁止刪除
                         //2. 禁止修改其它特性
                         //一旦改為false,不可逆
    }
    獲取一個屬性的四大特性:
    var attrs=Object.getOwnPropertyDescriptor(obj,"屬性")
    修改四大特性:
    Object.defineProperty(obj,"屬性",{
      四大特性:值
    })
    簡寫: Object.defineProperties(obj,{
           屬性名:{
             特性:值,
             特性:值,
           },
           屬性名:{
             ... : ...
           }
         })

    訪問器屬性: 不直接儲存屬性值,僅提供對另一個資料屬性的保護
    何時: 只要對一個屬性提供自定義規則的保護
    如何:

    新增: 只能用Object.defineProperty和defineProperties新增
    四大特性: {
      get(){ return this.資料屬性 }
      set(val){ 
        如果驗證val通過
          this.資料屬性=val
        否則
          報錯
      }
      enumerable:
      configurable:
    }

    如何使用: 同普通的資料屬性用法一樣!

     在取值時,自動呼叫訪問器屬性內部的get
     在賦值時,自動呼叫訪問器屬性內部的set方法,同時將等號右邊的新值,交給val引數

    問題: enumerable只能防住for in,防不住.,依然可用.直接修改被保護的資料屬性
    解決:
    內部屬性: 不能用.直接訪問到的屬性
    比如: class proto

    保護物件的結構: 3種

    1. 防擴充套件: 禁止給物件新增新屬性
      Object.preventExtensions(obj)
      原理: 內部屬性: extensible:true

        preventExtensions將extensible改為false
    2. 密封: 在防擴充套件同時,禁止刪除現有屬性
      Object.seal(obj)
      原理: 1. 將extensible改為false,禁止擴充套件

       2. 自動將所有屬性的configurable都改為false
    3. 凍結: 在密封的同時,禁止修改一切屬性值
      Object.freeze(obj)
      原理: 1. 兼具密封的所有功能

       2. 又將每個屬性的writable自動改為false!
      
    1. Object.create()
      僅用父物件,就可建立子物件,
      同時還可為子物件擴充套件自有屬性
      var child=Object.create(father,{
      //Object.defineProperties
      屬性名:{

      特性:值,
      特性:值,

      }
      })
      鄙視: 描述Object.create的執行原理

      1. 建立空物件child
      2. 自動設定child的__proto__為father
      3. 為child擴充套件新的自有屬性

    35.call/apply/bind

    替換函式中不想要的this!
    call/apply: 立刻呼叫函式,並臨時替換中的this為指定物件
    何時: 只要呼叫函式時,函式中的this不是想要的就用call換成想要的
    如果傳入函式的引數,是以陣列形式,整體傳入
    就用.apply(obj,arr)
    bind: 基於原函式,建立一個新函式,並永久繫結this為指定物件
    何時: 不會立刻呼叫的函式(回撥函式)中的this,不是想要的,就可用bind建立一個新函式,並永久繫結this!

    36.陣列API:

    判斷:

    1. 判斷陣列中所有元素是否都符合條件
      arr.every(function(elem,i,arr){

      //elem: 當前元素值
      //i: 當前位置 
      //arr: 當前陣列物件
      return 判斷條件

      })

    2. 判斷陣列中是否包含符合條件的元素
      arr.some(function(elem,i,arr){

      return 判斷條件

      })

    遍歷:

    1. forEach: 對原陣列中每個元素執行相同的操作
      arr.forEach(function(elem,i,arr){
      arr[i]=新值
      })
    2. map: 依次取出原陣列中每個元素執行相同操作後,放入新陣列。原陣列不變
      arr.map(function(elem,i,arr){
      return 新值
      })

    過濾和彙總:

    1. 過濾: 複製出原陣列中符合條件的元素,放入新陣列返回
      var subs=arr.filter(function(elem,i,arr){
      return 判斷條件
      })
    2. 彙總: 將原陣列中所有值統計出一個最終結論
      var result=arr.reduce(function(prev,elem,i,arr){
      //prev: 截止到目前,之前的臨時彙總值
      return prev+elem;
      })

    37.let: 代替var

    為什麼
    問題1: 宣告提前, 破壞程式原有執行順序
    解決: let禁止在宣告之前,提前使用該變數
    問題2: js沒有塊級作用域, 塊內的變數,會汙染到塊外
    解決: let會將當前所在if/for/while…(){}變成塊級作用域

      後果: 塊內的let出的變數不會影響外部!

    原理: 其實let就是匿名函式自調!
    let與for迴圈,可形成閉包的效果
    強調: 原來塊內外都可使用的變數,出了塊,就不能用了!

    38.引數增強:

    預設值: function fun(引數1, 引數2,…,引數n=預設值)
    強調: 帶預設值的引數必須定義在列表末尾
    原理: 引數n=引數n||預設值;
    rest: 代替了arguments
    何時: 當函式,不確定引數個數時——過載
    為什麼: arguments的缺點:

    1. 類陣列物件,不是陣列
    2. 只能後去全部,不能有選擇的分段獲取

    如何: 定義函式時: function fun(引數1,引數2,…, …陣列名)
    陣列名, 是一個純正的陣列,且可有選擇的分段獲取
    原理: var arr=[].slice.call(arguments[,starti]);//將類陣列物件轉為陣列
    spread: 代替apply
    為什麼: apply雖然可打散陣列型別引數為單個值,但是必須和替換this的操作捆綁使用
    何時: 只要僅需要打散陣列型別引數為單個值時
    如何: 呼叫時: fun(引數值1,引數值2,…陣列)

    1. 箭頭函式: 代替回撥函式中的function

    何時: 只要回撥函式,都不再使用function,而是使用箭頭函式
    如何:

    1. 去function改=>
    2. 如果只有一個引數,可省略()
    3. 如果函式體只有一句話,則{}可省略

      更簡化: 如果僅有的一句話還是return,可省略return
      

    特點: 內外共用同一個this ——代替bind
    問題: 如果反而希望內外this不通用時,就不能用箭頭函式

    40.模板字串: 代替+號拼接字串

    ESLint規定,不允許使用+拼接字串
    如何:

    1. 定義模板: 左右模板內容都必須放在“中
    2. 在模板中嵌入變數或表示式,動態生成內容:
      模板內,可用${…}嵌入任何合法的js變數或語句

    41.解構: 簡化批量賦值

    什麼是: 將一個物件/陣列中的成員和元素,分別提取出來,單獨使用。
    為什麼: 避免反覆使用物件名/陣列名
    何時: 只要希望將一個大的物件或陣列中的每個成員單獨取出使用時
    如何: 3種:

    1. 陣列解構: 下標對下標
    2. 物件解構: 屬性對屬性
    3. 引數解構: 屬性對屬性
      定義函式時:
      問題: 普通函式的引數列表的順序和個數是固定的
      解決: 使用物件語法定義引數列表
      優點: 將來傳入的引數個數,順序與物件列表無關
      呼叫函式: 也用物件語法傳入引數
      賦值過程中,採用物件結構的方式,為引數變數賦值

    42.for…of 在特定情況下,代替for迴圈

    什麼是: 依次遍歷陣列/類陣列物件中每個元素的值
    vs for…in: 依次遍歷關聯陣列/物件中每個成員的屬性名
    何時: 如果希望從頭到尾遍歷整個陣列或類陣列物件
    如何:

    for(var elem of arr){
            elem
          }
    

    侷限: 無法獲得當前位置; 無法控制遍歷的進度/順序; 無法有選擇的遍歷部分

    43.class: 代替傳統的封裝,繼承,多型的語法

    封裝:

      class Student {
          constructor(sname,sage){
            ... ...
          }
          intr (){//Student.prototype.intr
            
          } 
          fun (){
        
          }
      }
    

    繼承:

      class Flyer {
        constructor(fname,speed){
          ... ...
        }
        fly (){
          ... ...
        }
      }
      class Plane extends Flyer{
        constructor(fname,speed,score){
          //super指向父型別建構函式,且自動替換this
          super(fname,speed)
          ... ...
        }
        getScore (){
          ... ...
        }
     }
    

    靜態方法:

     class User{
        constructor(uname,upwd){
          this.uname=uname;
          this.upwd=upwd;
        }
        save(){//儲存在User.prototype中的例項方法
          console.log("儲存當前物件");
        }
        static  findOne(){//靜態方法,定義在建構函式上
          return new User();
        }
      }
      var user=new User(...);
      user.save();//呼叫例項方法
      User.findOne();//呼叫靜態方法
    

    44.Promise: 解決: 回撥地獄

    什麼是callback hell: 由於使用引數傳遞迴調函式,導致步驟多時,引數的巢狀層級很深。
    何時: 只要非同步呼叫,可能發生延遲時,都要用Promise代替傳統引數callback
    如何: 定義時

      function 第一件事(){
        return new Promise(fn=>{
          第一件事的內容
          fn()
        })
      }
      function 第二件事(){
        return new Promise(fn=>{
          第二件事的內容
          fn()
        })
      }
      function 第三件事(){
        第三件事的內容
      }

    呼叫時:

    第一件事()//return Promise(fn)
      .then(第二件事)//return Promise(fn)
      .then(第三件事)
    

    鄙視題:

    1. 將類陣列物件複製為陣列:
      var arr2=Array.prototype.slice.call(arguments)
      將類陣列物件複製為陣列,並選取指定位置的剩餘元素
      var arr2= Array.prototype.slice.call(arguments,starti)

           相當於arguments.slice(starti)

      其實更簡單的: var arr2= [].slice.call(arguments,starti)

    2. promise中的錯誤處理:
      其實: new Promise(可接收2件事)

             .then(   )  .catch(    )

      new Promise((正常函式,出錯函式)=>{

      如果順利執行:
        呼叫正常()
      否則
        呼叫出錯()

      })

    3. 等待多個任務完成
      前提: 每個任務都必須都返回Promise
      如何: Promise.all([

         task1(), task2(),...
       ]).then(()=>{所有任務完成後才執行的任務})
      

    1. 47

    相關文章