JavaScript 重點補全

SRIGT發表於2024-07-16

✅ 資料型別

  • typeof:一元運算子
    • 檢測基本資料型別和函式型別
    • 無法區分 nullundefinedtypeof null 返回 "object"
    • 通常較快,因為它是內建的型別檢查機制
  • instanceof:二元運算子
    • 檢測物件是否為特定建構函式的例項
    • 不能檢測基本資料型別,只適用於引用型別
    • 可能較慢,因為涉及原型鏈的搜尋
  • Object.protptype.toString.call():方法
    • 檢測所有資料型別
    • 可以區分基本資料型別和引用型別
    • 可能較慢,因為涉及內部 [[Class]] 屬性的獲取
分類 型別 說明 示例 typeof instanceof Object.prototype.toString.call()
基本資料型別 undefined 未賦值 var a; undefined [object Undefined]
string 字串 var a = 'hello'; string [object String]
number 數字 var a = 1; number [object Number]
boolean 布林值 var a = true; boolean [object Boolean]
null 空值 var a = null; null [object Null]
symbol 符號(ES6) var a = Symbol(); symbol [object Symbol]
bigint 大整數(ES2020) var a = 1n; bigint [object BigInt]
引用資料型別 object 物件 var a = {}; object Object [object Object]
array 陣列 var a = []; object Array [object Array]
function 函式 var a = function(){}; function Function [object Function]
date 日期 var a = new Date(); object Date [object Date]
regexp 正則 var a = /d{6}/g; object RegExp [object RegExp]
自定義資料型別 my 自定義類 class My {};
var a = new My();
object My [object Object]

✅ 引用型別資料的內建方法

Object

  • Object.assign(target, source1, source2, ...)複製所有可列舉屬性的值從一個或多個源物件到目標物件

    var Alex = { name: "Alex", age: 20 };
    var Bob = { name: "Bob", age: 20, gender: "male" };
    var result = Object.assign(Bob, Alex);
    console.log(result);	// { name: 'Alex', age: 20, gender: 'male' }
    
  • Object.create(proto)建立一個新物件,使用指定的 proto 物件作為原型

    var proto = {
      func: function () {
        console.log(this.value);
      },
    };
    var obj = Object.create(proto);
    obj.value = "hello world";
    obj.func();		// hello world
    
  • Object.entries(obj):返回一個給定物件自身的所有可列舉屬性的鍵值對陣列

    var Alex = { name: "Alex", age: 20 };
    console.log(Object.entries(Alex));	// [ [ 'name', 'Alex' ], [ 'age', 20 ] ]
    
  • Object.fromEntries(iterable):將可迭代的鍵值對陣列或 Map 物件轉換為一個新的物件

    var a = [['a', 'b'], [1, 2]]
    console.log(Object.fromEntries(a));	// { '1': 2, a: 'b' }
    
  • Object.keys(obj):返回一個給定物件自身的所有可列舉屬性鍵的陣列

    var Alex = { name: "Alex", age: 20 };
    console.log(Object.keys(Alex));		// [ 'name', 'age' ]
    
  • Object.values(obj):返回一個給定物件自身的所有可列舉屬性值的陣列

    var Alex = { name: "Alex", age: 20 };
    console.log(Object.values(Alex));	// [ 'Alex', 20 ]
    
  • Object.freeze(obj):凍結一個物件的屬性,使得屬性不可配置,也不可寫

  • Object.isFrozen(obj):判斷一個物件是否被凍結

    var obj = { a: 1 };
    Object.freeze(obj);
    obj.a = 2;
    console.log(obj);					// { a: 1 }
    obj.b = 2;
    console.log(obj);					// { a: 1 }
    console.log(Object.isFrozen(obj));	// true
    
  • Object.seal(obj):防止物件新增新屬性或修改現有屬性的可配置性

  • Object.isSealed(obj):判斷一個物件是否被密封

    var obj = { a: 1 };
    Object.seal(obj);
    obj.a = 2;
    console.log(obj);					// { a: 2 }
    obj.b = 2;
    console.log(obj);					// { a: 2 }
    console.log(Object.isSealed(obj));	// true
    
  • Object.defineProperty(obj, prop, descriptor):定義一個物件的新屬性或修改現有屬性的特性

    var obj = {};
    Object.defineProperty(obj, 'a', {
      value: 1,
      writable: false,			// 可寫性
      enumerable: true,			// 可列舉性
      configurable: false,		// 可配置性
    });
    console.log(obj);			// { a: 1 }
    obj.a = 2;
    console.log(obj);			// { a: 1 }
    
  • Object.defineProperties(obj, props):定義一個物件的多個新屬性或修改現有屬性的特性

    var obj = {};
    Object.defineProperties(obj, {
      a: {
        value: 1,
        writable: false,
      },
      b: {
        value: 2,
        writable: true,
      },
    });
    console.log(obj.a, obj.b);	// 1 2
    obj.a = 2;
    obj.b = 3;
    console.log(obj.a, obj.b);	// 1 3
    
  • Object.is(value1, value2):比較兩個值是否相同,類似於===運算子

    console.log(Object.is(123, 123));	// true
    console.log(Object.is(123, "123"));	// false
    

Array

基本操作方法
  • concat(Array)連線兩個或多個陣列,返回一個新陣列

    var a = [1, 2 ,3];
    var b = [4, 5, 6];
    var c = a.concat(b);
    console.log(c);		// [ 1, 2, 3, 4, 5, 6 ]
    
  • copyWithin(target[, start[, end]]):在陣列內,將指定位置的成員複製到其他位置,並返回修改後的陣列

    • target:從該位置開始替換資料;如果為負數,則表示倒數(下同)
    • start:從該位置開始讀取資料,預設為 0
    • end:到該位置前停止讀取資料(不包括該位置),預設為 this.length
    var a = [0, 1, 2, 3, 4, 5];
    a.copyWithin(3, 0, 3);
    console.log(a);		// [ 0, 1, 2, 0, 1, 2 ]
    
  • entries():返回陣列的可迭代物件,包含陣列中每個索引的鍵值對

    var a = ["a", "b", "c"];
    for (var [key, value] of a.entries()) console.log(key, value);
    /*
     * 0 a
     * 1 b
     * 2 c
     */
    
  • fill(value[, start[, end]])填充,用一個固定值填充陣列中從開始索引到結束索引內的全部元素

    var arr1 = new Array(5);
    arr1.fill(0);
    console.log(arr1);	// [ 0, 0, 0, 0, 0 ]
    
    var arr2 = [1, 1, 1, 1];
    arr2.fill(0, 1, 3);
    console.log(arr2);	// [ 1, 0, 0, 1 ]
    
  • filter(fn)過濾器,建立一個新陣列,包含透過所提供函式實現的測試的所有元素

    var a = [1, 2, 3];
    console.log(a.filter((value) => value >= 2));	// [ 2, 3 ]
    
  • find(fn)查詢值,返回陣列中滿足提供的測試函式的第一個元素的值

    var a = [1, 2, 3];
    console.log(a.find((value) => value >= 2));		// 2
    
  • findIndex(fn)查詢索引,返回陣列中滿足提供的測試函式的第一個元素的索引

    var a = [1, 2, 3];
    console.log(a.findIndex((value) => value >= 2));	// 1
    
  • forEach(fn)遍歷,對陣列的每個元素執行一次給定的函式

    var a = [1, 2, 3];
    a.forEach((value) => console.log(value));
    /*
     * 1
     * 2
     * 3
     */
    
  • includes(item):判斷一個陣列是否包含一個指定的值

    var a = [1, 2, 3];
    console.log(a.includes(2), a.includes(4));	// true false
    
  • indexOf(item):返回在陣列中可以找到一個給定元素的第一個索引

    var a = [1, 1, 1];
    console.log(a.indexOf(1));		// 0
    
  • join(separator):將陣列的所有元素連線為字串

    var a = [1, 2, 3];
    console.log(a.join(','));		// 1,2,3
    
  • keys():返回陣列的可迭代物件,包含原始陣列的

    var a = ["a", "b", "c"];
    for (let key of a.keys()) console.log(key);
    /*
     * 0
     * 1
     * 2
     */
    
  • lastIndexOf(item):返回在陣列中可以找到一個給定元素的最後一個索引

    var a = [1, 1, 1];
    console.log(a.lastIndexOf(1));		// 2
    
  • map((currentValue[, index[, array]]) => {}[, thisArg])對映,建立一個新陣列,其結果是該陣列中的每個元素是呼叫一次提供的函式後的返回值

    var a = ['a', 'b', 'c'];
    var b = a.map((value, index) => value + ' ' + index);
    console.log(b);		// [ 'a 0', 'b 1', 'c 2' ]
    
  • pop()尾出棧,刪除陣列的最後一個元素並返回該元素的值

    var a = [1, 2, 3];
    console.log(a.pop());		// 3
    console.log(a);				// [ 1, 2 ]
    
  • push(...items)尾壓棧,向陣列的末尾新增一個或多元素,並返回陣列新的長度

    var a = [0, 1];
    console.log(a.push(2));		// 3
    console.log(a);				// [ 0, 1, 2 ]
    
  • reduce((accumulator, currentValue[, index[, array]]) => {}[, initialValue]):將陣列元素計算為一個值(從左到右)

    var a = ["a", "b", "c"];
    console.log(a.reduce((acc, cur) => acc + cur, ""));		// abc
    
  • reduceRight((accumulator, currentValue[, index[, array]]) => {}[, initialValue]):將陣列元素計算為一個值(從右到左)

    var a = ["a", "b", "c"];
    console.log(a.reduceRight((acc, cur) => acc + cur, ""));	// cba
    
  • reverse()反轉陣列的元素順序

    var a = [1, 2, 3];
    console.log(a.reverse());	// [ 3, 2, 1 ]
    
  • shift()頭出棧,刪除並返回陣列的第一個元素

    var a = [1, 2, 3];
    console.log(a.shift()); 	// 1
    console.log(a);				// [ 2, 3 ]
    
  • slice([start[, end]])切片,選取陣列的一部分,並返回一個新陣列

    var a = [1, 2, 3];
    console.log(a.slice(1, 3));		// [ 2, 3 ]
    
  • some(fn):檢測陣列元素中是否有元素符合指定條件

    var a = [1, 2, 3];
    console.log(a.some((value) => value < 3));	// true
    console.log(a.some((value) => value > 3));	// false
    
  • sort(fn):對陣列的元素進行排序

    var a = [2, 1, 3];
    console.log(a.sort((a, b) => a - b));	// [ 1, 2, 3 ]
    console.log(a.sort((a, b) => b - a));	// [ 3, 2, 1 ]
    
  • splice(start, deleteCount[, item])從陣列中新增或刪除元素

    var a = [1, 2, 3];
    a.splice(1, 1, 1);		// [ 1, 1, 3 ]
    console.log(a);
    
  • toLocaleString():返回一個字串表示陣列中的元素

    var a = [1000, 2, 3];
    console.log(a.toLocaleString());	// 1,000,2,3
    
  • toString():返回一個字串,表示指定的陣列及其元素

    var a = [1000, 2, 3];
    console.log(a.toString());			// 1000,2,3
    
  • unshift(...items)頭壓棧,向陣列的開頭新增一個或多元素,並返回新的長度

    var a = [2, 3];
    console.log(a.unshift(1));	// 3
    console.log(a);				// [ 1, 2, 3 ]
    
  • values():返回一個新的 Array Iterator 物件,該物件包含陣列每個索引的

    var a = ["a", "b", "c"];
    for (let value of a.values()) console.log(value);
    /*
     * a
     * b
     * c
     */
    
靜態方法
  • Array.from(arrayLike[, mapFn[, thisArg]]):從類似陣列或可迭代物件建立一個新的陣列例項

    console.log(
      Array.from({
        1: "a",
        0: "b",
        2: "c",
        length: 3,
      })
    );	// [ 'b', 'a', 'c' ]
    
    console.log(Array.from("abc"));		// [ 'a', 'b', 'c' ]
    
    console.log(Array.from([1, 2, 3], (x) => x + x));	// [ 2, 4, 6 ]
    
  • Array.isArray():檢查一個值是否為陣列

    console.log(Array.isArray([1, 2, 3]));			// true
    console.log(Array.isArray([]));					// true
    console.log(Array.isArray(new Array()));		// true
    console.log(Array.isArray(Array.prototype));	// true
    console.log(Array.isArray("abc"));				// false
    console.log(Array.isArray(null));				// false
    console.log(Array.isArray(undefined));			// false
    
  • Array.of():建立一個具有可變數量引數的新陣列例項

    console.log(Array.of(undefined, null, 1, "a"));	// [ undefined, null, 1, 'a' ]
    
案例:陣列去重
  1. 使用雙重迴圈

    var array = [1, 2, 2, 3, 4, 4, 5];
    for (var i = 0; i < array.length; i++) {
      for (var j = i + 1; j < array.length; j++) {
        if (array[i] === array[j]) {
          array.splice(j, 1);
          j--;
        }
      }
    }
    console.log(array);
    
  2. 使用 Set 集合資料結構

    var array = [1, 2, 2, 3, 4, 4, 5];
    var result = [...new Set(array)];
    console.log(result);
    
  3. 使用 Map 雜湊資料結構

    var array = [
      { name: "Alex", age: 20 },
      { name: "Bob", age: 25 },
      { name: "Charlie", age: 20 },
    ];
    
    let map = new Map();
    array.forEach((item) => {
      if (!map.has(item.age)) {
        map.set(item.age, [item]);
      } else {
        map.get(item.age).push(item);
      }
    });
    let result = {};
    for (let [key, value] of map) {
      result[key] = value;
    }
    console.log(result);
    
  4. 使用 filter 方法

    var array = [
      { name: "Alex", age: 20 },
      { name: "Bob", age: 25 },
      { name: "Charlie", age: 20 },
    ];
    var result = array.filter((item, index, self) => {
      return self.findIndex((temp) => temp.name === item.name) === index;
    });
    console.log(result);
    
  5. 使用 reduce 方法

    var array = [
      { name: "Alex", age: 20 },
      { name: "Bob", age: 25 },
      { name: "Charlie", age: 20 },
    ];
    var result = array.reduce(function (acc, curr) {
      if (!acc[curr.age]) acc[curr.age] = [];
      acc[curr.age].push(curr);
      return acc;
    }, {});
    console.log(result);
    

Date

  • getDate(): 返回月份中的第幾天(從 1 到 31)
  • getDay(): 返回星期幾(0 表示週日,1 表示週一)
  • getFullYear(): 返回年份
  • getHours(): 返回小時(從 0 到 23 )
  • getMilliseconds(): 返回毫秒(0 到 999)
  • getMinutes(): 返回分鐘(從 0 到 59)
  • getMonth(): 返回月份(從 0 到 11)
  • getSeconds(): 返回秒數(從 0 到 59)
  • getTime(): 返回自1970年1月1日午夜以來的毫秒數
  • getTimezoneOffset(): 返回UTC時間與本地時間之間的時差,以分鐘為單位
  • now(): 返回自 1970 年 1 月 1 日午夜以來的毫秒數
  • parse(): 解析日期字串並返回自 1970 年 1 月 1 日以來的毫秒數
  • setDate(): 設定月份中的某一天
  • setFullYear(): 設定日期物件的年份
  • setHours(): 設定日期物件的小時
  • setMilliseconds(): 設定日期物件的毫秒數
  • setMinutes(): 設定日期物件的分鐘數
  • setMonth(): 設定日期物件的月份
  • setSeconds(): 設定日期物件的秒數
  • setTime(): 將日期設定為 1970 年 1 月 1 日之後(之前)的指定毫秒數
  • toDateString(): 將日期物件的日期部分轉換為可讀字串
  • toISOString(): 使用 ISO 標準將日期作為字串返回
  • toJSON(): 以字串形式返回日期,格式為 JSON 日期
  • toLocaleDateString(): 使用區域設定約定將 Date 物件的日期部分作為字串返回
  • toLocaleTimeString(): 使用區域設定約定將 Date 物件的時間部分作為字串返回
  • toLocaleString(): 使用區域設定約定將 Date 物件轉換為字串
  • toString(): 將 Date 物件轉換為字串
  • toTimeString(): 將 Date 物件的時間部分轉換為字串
  • valueOf(): 返回 Date 物件的原始值

RegExp

  • compile():(ES6 開始廢棄)重新編譯正規表示式

  • exec():執行正規表示式的搜尋,返回第一個匹配結果的陣列

    var a = 123
    console.log(/^\d+$/.exec(a));	// [ '123', index: 0, input: '123', groups: undefined ]
    
  • test():執行正規表示式的搜尋,返回一個布林值,指示是否找到匹配

    var a = 123
    var b = "123"
    console.log (/^\d+$/.test(a))	// true
    console.log (/^\d+$/.test(b))	// true
    
  • toString():返回表示正規表示式的字串

    console.log(/^\d+$/.toString());	// /^\d+$/
    
  • flags:返回正規表示式的標誌(修飾符)的字串

  • source:返回正規表示式的模式文字

  • lastIndex:設定或返回正規表示式下次匹配的起始索引

    console.log(/^\d+$/g.flags);		// g
    console.log(/^\d+$/g.source);		// ^\d+$
    console.log(/^\d+$/g.lastIndex);	// 0
    

✅ 事件物件

  • event.preventDefault()阻止事件的預設行為
  • event.stopPropagation()阻止事件繼續在 DOM 樹上冒泡或捕獲,使事件不會傳播到其他層級的元素
  • event.stopImmediatePropagation():阻止同一元素上的其他事件處理程式被觸發,即使它們在事件佇列中等待執行
  • event.target:返回觸發事件的元素,即事件的目標
  • event.currentTarget:返回繫結事件處理程式的元素,在事件處理程式內部,this 關鍵字通常指向 event.currentTarget
  • event.timeStamp:返回事件被觸發的時間戳
  • event.isTrusted:返回一個布林值,指示事件是否由使用者而非指令碼觸發
  • event.type:返回事件的型別,如 clickkeypress
  • event.bubbles:返回一個布林值,指示事件是否會冒泡
  • event.cancelable:返回一個布林值,指示事件是否可以被取消
  • event.eventPhase:返回一個整數,表示事件當前處於事件流的哪個階段(捕獲、目標、冒泡)

✅ 原型(proto)與原型鏈

  • 原型分隱式原型顯示原型

    var array = new Array();
    var implicit = array.__proto__;		// 隱式原型
    var explicit = Array.prototype;		// 顯式原型
    console.log(implicit, explicit);
    console.log(implicit === explicit);	// true
    
    • 此時,類例項的隱式原型全等於類的顯式原型
    • 隱式原型指向顯式原型
  • 原型鏈是指原型中巢狀著原型

    var array = new Array();
    console.log(array.__proto__);
    console.log(array.__proto__.__proto__);
    
  • 可以透過 hasOwnProperty() 方法檢視當前例項本身是否具有某個屬性,而非去原型鏈上尋找

    class Calc {
      constructor(x, y) {
        this.x = x;
        this.y = y;
      }
    }
    
    var calc = new Calc(1, 2);
    console.log(calc.hasOwnProperty('x'));	// true
    console.log(calc.hasOwnProperty('z'));	// false
    

✅ 函式

函式型別

  1. 具名函式:透過 function 關鍵字宣告的,具有函式名,可以在其作用域內的任何地方呼叫

    function func() {}
    
  2. 匿名函式:通常賦值給變數或者作為引數傳遞給其他函式

    var func = function () {};
    
  3. 箭頭函式:ES6 引入的一種新的函式定義方式,提供了更簡潔的語法,並且在處理 this 時有所不同

    var func = () => {};
    
  4. 生成器函式:可以暫停執行並在之後恢復執行,透過 function* 關鍵字定義,並使用 yield 關鍵字返回值

    function* generator() {
      yield 1;
      yield 2;
      yield 3;
    }
    let g = generator();
    console.log(g.next().value);	// 1
    console.log(g.next().value);	// 2
    console.log(g.next().value);	// 3
    
  5. 建構函式:用於建立物件例項,通常首字母大寫,並透過new關鍵字呼叫

    function Person(name, age) {
      this.name = name;
      this.age = age;
    }
    var person = new Person("Alex", 20);
    console.log(person.name, person.age);	// Alex 20
    
  6. 立即執行函式:定義後立即執行,常用於建立私有作用域或避免全域性名稱空間汙染

    (function (name) {
      console.log(name);	// Alex
    })("Alex");
    
  7. 回撥函式:回撥函式是作為引數傳遞給另一個函式的函式,通常在某個非同步操作完成後被呼叫

    function func(callback) {
      callback("func");
    }
    func((value) => console.log(value));	// func
    
  8. 閉包函式:閉包是指有權訪問另一個函式作用域中變數的函式,通常由一個內部函式引用外部函式的變數形成

    function outer(outerValue) {
      return function inner(innerValue) {
        console.log("outerValue:", outerValue);			// outerValue: out
        console.log("innerValue:", innerValue);			// innerValue: in
      };
    }
    outer("out")("in");
    

箭頭函式與普通函式的區別

箭頭函式 普通函式
this 繫結 不繫結,捕獲上下文的 this 動態繫結,取決於函式的呼叫方式
arguments 物件 沒有自身的 arguments 物件
可以使用剩餘引數語法 ...args
有自身的 arguments 物件
包含函式呼叫時傳入的所有引數
建構函式 不可用,不支援 new 可以,支援 new
返回值 單個表示式時可以直接返回表示式結果 必須使用 return 返回
supernew.target 不支援 支援
this.arguments 不支援 支援
特點 快速、簡潔 複雜,多場景適用

閉包

  • 閉包 = 內層函式 + 引用外層函式的變數

  • 閉包不一定要有 return

    • 當外層函式需要適使用閉包中的變數時,需要 return

    • 閉包應用:實現資料私有(舉例:計數)

      function func() {
        let count = 0;
        return function closure() {
          count++;
          console.log("count", count);
        };
      }
      const res = func();
      res();		// count 1
      res();		// count 2
      
      • 節流與防抖函式
      • Vue 和 React 的 Hooks 鉤子函式
  • 閉包不一定有記憶體洩漏

    • 在上述計數示例中,count 變數在 closure() 中被引用,無法被作為垃圾回收,從而引起記憶體洩漏
    • 並非所有記憶體洩漏都需要手動回收

✅ 作用域

全域性作用域

  • 可以被區域性作用域遮蔽
  • 在瀏覽器環境中,全域性作用域中的變數通常會成為 window 物件的屬性

區域性作用域

  • 變數或函式只能在特定的程式碼塊內部被訪問

塊級作用域

  • ES6 引入
  • 允許在程式碼塊內部宣告變數和函式,這些變數和函式的作用域被限制在相應的程式碼塊內
  • 透過 letconst 關鍵字實現

作用域鏈

  • 用於變數解析的機制
  • 當在函式內部嘗試訪問一個變數時,如果該變數在當前函式的區域性作用域中沒有找到,直譯器會沿著作用域鏈向上查詢,直至找到該變數或達到全域性作用域
  • 確保了變數的私有性封裝性,防止了變數名稱的衝突

變數提升

  • 變數和函式宣告在程式碼執行之前就被移至其作用域的頂部,但變數的初始化操作保留在原位置

  • 表現為:在變數宣告之前引用了變數,會得到 undefined 的值,而非報錯

    function func() {
      console.log(a);	// undefined
      var a = 1;
      console.log(a);	// 1
    
      console.log(b);	// 報錯:ReferenceError
      let b = 2;
      console.log(b);	// 2
    
      console.log(c);	// 報錯:ReferenceError
      const c = 3;
      console.log(c);	// 3
    }
    func();
    

-End-

相關文章