# ⚪️ JavaScript 深い理解と実践

# 🔶 Reference vs Primitive

In JavaScript, objects and arrays are examples of reference types, which are distinct from primitive types like stringsand Boolean, numbers, undefined, Null.

heapandstack

# ▫️ Reference Types in JavaScript:

  • Objects and Arrays:

    • Objects, like the person object, are reference types.
    • They can hold properties with primitive values or other reference types.
    • Nesting of objects or arrays inside a reference type doesn't change its reference type nature.
  • Hobbies Array:

    • The hobbies array, though holding strings (primitive values), is a reference type.
    • Arrays in JavaScript are always considered reference types.

Difference in Memory Management:

  • Memory Types:

    • JavaScript uses two types of memory: The Stack and the Heap.
  • Stack Memory:

    • The stack is easy-to-access memory, managing items in a stack-like structure.
    • Suitable for items with known sizes in advance (numbers, strings, booleans).
  • Heap Memory:

    • The heap is used for items with dynamic · s and structures, like objects and arrays.
    • Objects and arrays go into the heap since they can be mutated and change at runtime.
  • Pointer Mechanism:

    • Each heap item has an exact address stored in a pointer.
    • The pointer, pointing at the item in the heap, is stored on the stack.

# ▫️ Strange Behavior of "Reference Types"

  • What's actually stored in the person variable in the following snippet? b. var person = { name: 'Max' }

It's b. A pointer to the person object is stored in the variable. The same would be the case for the hobbies array.

  • What does the following code spit out then?
var person = { name: "Max" };
var newPerson = person;
newPerson.name = "Anna";
console.log(person.name); // What does this line print? 'Anna'
  • Why?

    • Because you never copied the person object itself to newPerson.
    • You only copied the pointer!
    • It still points at the same address in memory though. Hence changing newPerson.name also changes person.name because newPerson points at the exactly same object!

# ▫️ How to Copy Objects and Arrays:

Now that we know that we only copy the pointer - how can we actually copy the value behind the pointer? The actual object or array?

You basically need to construct a new object or array and immediately fill it with the properties or elements of the old object or array.

You got multiple ways of doing this - also depending on which kind of JavaScript version you're using (during development).

Here are the two most popular approaches for arrays:

  1. Use the slice() is a standard array method provided by JavaScript. You can check out its full documentation here.

    var hobbies = ["Sports", "Cooking"];
    var copiedHobbies = hobbies.slice();
    

It basically returns a new array which contains all elements of the old element, starting at the starting index you passed (and then up to the max number of elements you defined). If you just call slice(), without arguments, you get a new array with all elements of the old array.

  1. Use the spread operator If you're using ES6+, you can use the spread operator.

    var hobbies = ["Sports", "Cooking"];
    var copiedHobbies = [...hobbies];
    

Here, you also create a new array (manually, by using []) and you then use the spread operator (...)to "pull all elements of the old array out" and add them to the new array.

For objects

  1. You can use the Object.assign() syntax which is explained in greater detail here.

    var person = { name: "Max" };
    var copiedPerson = Object.assign({}, person);
    

This syntax creates a new object (the {} part) and assigns all properties of the old object (the second argument) to that newly created one. This creates a copy.

  1. Just as with arrays, you can also use the spread operator on objects.

    var person = { name: "Max" };
    var copiedPerson = { ...person };
    

This will also create a new object (because you used { }) and will then pull all properties of person out of it, into the brand-new objects.

# 🔶 Deconstruction Assignment

ES6 Practical Deep Deconstruction Assignment Methods for Deconstructed Objects, Arrays, Hybrid Deconstruction, and Continuous Deconstruction

# ▫️ Deconstructing Objects

Without destructuring assignment, accessing the properties of an object or the elements of an array requires dot notation or bracket notation:

1.1 basic deconstruction format

let object = { name: "XXX", age: 22 };
let name = object.name;
let age = object.age;
console.log(name, age); // XXX 22

1.2 variable alias

if you want to use a different variable name than the property name, you can use the following syntax:

let object = { name: "XXX", age: 22 };
let { name: myName, age: myAge } = object;
console.log(myName, myAge); // XXX 22

1.3 default value

if 目標對象屬性中沒有要解構的屬性,不指定默認值, 那麼將會是 undefined, 此時可以給要解構變量寫 default 值.

let { name, age, gender = "female" } = { name: "XXX", age: 22 };

此種為變量設置默認值的方法,常用與函數參數中,加入調用函數時沒傳遞參數,有可能會對函數運行造成錯誤,那就可以形參位置進行對象解構, 對變量指定默認值。

function fn({name:'xxx', age = 30 }={}){
  console.log(name, age);// xxx 30
}

# ▫️ Deconstructing Arrays

解構數組和對象有些不同, 解構對象的時候屬性前後位置不影響, 但是解構數組,需要按照 index 順序解構

2.1 basic deconstruction format without destructuring assignment, accessing the properties of an object or the elements of an array requires dot notation or bracket notation:

let array = ["XXX", 22];
let name = array[0];
let age = array[1];
console.log(name, age); // XXX 22

if with deconstructing assignment, you can use the following syntax:

let array = ["XXX", 22];
let [name, age] = array;
console.log(name, age); // XXX 22

🔻 Scenario 01: To destructure the nested data object within the main data property of a response object to get to the inner data array.

// Assuming the response object is named `response`
const {
  data: {
    data: [innerData],
  },
} = response;

console.log(innerData); // This will log the inner object represented by the {...} in your screenshot

This snippet assumes that response is the name of the variable holding the entire object you've shown in your screenshot. The destructuring pattern { data: { data: [innerData] } } navigates through the object structure to the inner data array and grabs the first item from that array, assigning it to the innerData variable. If you want to access other properties at the same level, you can add them to the destructuring pattern accordingly.

destrcturing

async function banner() {
  // Simulating the structure from your screenshot
  const response = await db
    .collection("banner-list")
    .orderBy("sort", "desc")
    .limit(1)
    .get();

  // Correct destructuring pattern based on the screenshot you provided
  let {
    data: {
      data: [result],
    },
  } = response;

  console.log(result); // This will log the first object from the data array
}

In this updated code, response represents the object returned from the database query. The destructuring pattern { data: { data: [result] } } navigates through the first data object and then the data array to extract the first item of that array into the result variable.

Please make sure that the db.collection(...).get() method indeed returns an object with a structure similar to the one in your screenshot, with nested data properties. If the structure is different, you'll need to adjust the destructuring pattern accordingly.

# 🔶 Arrow function の理解

  1. 只有一個參數的函數 (Arrow Function with a Single Parameter):
(參數) => 返回值;
// 例如:
(x) => x * 2;
  1. 沒有參數或多個參數的函數 (Arrow Function with No or Multiple Parameters):

    // 沒有參數時使用空的小括號
    () => 返回值
    
    // 多個參數時使用小括號括起來
    (參數1, 參數2) => 返回值
    // 例如:
    (a, b) => a + b
    
  2. 箭頭後面的值就是返回值 (Arrow Function Expression):

    • 返回值必須是一個表達式。什麼叫表達式?有返回值的語句就是表達式。
  3. 如果返回值是一個對象,需要用括號包起來 (Object as Return Value):

    () => ({ key: "value" });
    
  4. 如果需要在 arrow function 裡定義邏輯, 可以直接在箭頭後跟一個代碼塊,代碼塊中的語法和普通函數沒有區別。

    (參數1, 參數2) => {
      // 邏輯
      return 返回值;
    };
    

# 🔶 キーワード "this "の理解

# アロー関数で書く理由 ① 関数を短く書きたい

//アロー関数
const arrowAddFunc = (a, b) => {
  return a + b;
};

//1個しか評価項目が無い場合は以下のようにも書ける
//中括弧とreturnの省略が出来る
const arrowAddFunc = (a, b) => a + b;

//評価項目が1個だけ、かつ引数が1個しか無い場合はこのようにも書ける
//括弧も省略できる
const arrowDoubleFunc = (a) => 2 * a;

省略して、1行で書ける事も出来ますが、アロー関数を書き慣れていない人がパッと見た時に、やや分かりづらいかもしれません。必ずしも1行で書かなければいけない理由はありません。いずれにしろ、 function をいちいち書く必要がなく、短く書く事が出来ます。しかしアロー関数の真価は this を束縛しないという点にあるでしょう。

# アロー関数で書く理由 ② this を束縛しない

アロー関数で短く書けます!と説明されても、へーそうなんだ。。。で終わると思いますが、アロー関数を使う上でちゃんと理解しておきたいのは、こちらの方かと思います。

それは、this の値は関数定義時に決まる(=this を束縛しない) というルールです。 this を束縛しない、という説明はやや分かりづらいので、ここでは、アロー関数を使えば、this の値は関数定義時に決める事ができる、という理解で大丈夫でしょう。

JavaScript で this を扱う時、最初は分かりづらい...直感的でない...という事は、JavaScript を書いてる方なら、分かって頂けるかなと思います。this の呼び出しパターンにはいくつかあるのですが、ここでは、アロー関数を使うメリットを説明する上で、メソッド呼び出しパターンと関数呼び出しパターンを事前に例として挙げておきます。this を理解する上で大切なのは、呼び出し元が何であるかという事です。まずはメソッド呼び出しパターンから説明します。

# メソッド呼び出しパターン

以下のコードを実際に打ち込んで、console.log で確認してみましょう。

const object = {
  value: "test",
  method1: function () {
    return this; //①何が返されるでしょう?
  },
  method2: function () {
    return this.value; //②何が返されるでしょう?
  },
};

//① thisはobject自身を参照している
console.log(object.method1()); //{ value: 'test', method: [Function: method] }
//② thisはobject自身を参照しているので、valueプロパティにもアクセスできる
console.log(object.method2()); // test

# 🔶 キーワード "class"の理解

# Class の 基本概念

A class is a template for an object that defines its properties and methods. In JavaScript, classes can be defined using the class keyword.

class MyClass {
  // class of constructor functions that are called when the instance is created.
  constructor() {
    console.log(this); // In a constructor function, this points to the instance object.
  }

  // 類的實例方法
  myMethod() {
    console.log(this); // In the method, this still points to the instance object.
  }

  // 類的靜態方法
  static myStaticMethod() {
    console.log(this); // In static methods, this points to the class itself.
  }
}

// Examples of Creation
const myInstance = new MyClass();

// Calling Example Methods
myInstance.myMethod();

// Calling the static method
MyClass.myStaticMethod();

# Strict mode && Class

  • All code in the class is executed in strict mode
  • In strict mode, when a function is called in the global context, this is no longer a global object (usually a window), but is undefined.
"use strict";

class MyClass {
  constructor() {
    console.log(this); // 在構造函數中,this 仍然指向實例對象
  }

  myMethod() {
    console.log(this); // 在方法中,this 仍然指向實例對象
  }

  static myStaticMethod() {
    console.log(this); // 在靜態方法中,this 是指向類本身
  }
}

// 創建類的實例
const myInstance = new MyClass();

// 調用實例方法
myInstance.myMethod();

// 調用靜態方法
MyClass.myStaticMethod();

// 在全局上下文中,調用函數(不是在類中)
function globalFunction() {
  console.log(this); // 在嚴格模式下,全局上下文中的 this 將是 undefined
}

// 在嚴格模式下,全局上下文中的 this 將是 undefined
globalFunction();

# 箭頭函數在 Class 中的影響

  • 如果類中的方法是以箭頭函數定義的,則方法中的 this 將指向類的實例對象。
class MyClass {
  myArrowMethod = () => {
    console.log(this); // 在箭頭函數中,this 指向類的實例對象
  };
}

const myInstance = new MyClass();
myInstance.myArrowMethod(); // 調用箭頭函數方法

この形式の書き方は、アロー関数内の this が常にクラスのインスタンスオブジェクトを指すようにし、関数の呼び出し方法の影響を受けないように確保します。

# クラスの継承

  • 継承を使用すると、クラス内のプロパティやメソッドを利用でき、さらに新しいプロパティやメソッドをサブクラスに追加できます。
  • extends を使用してクラスを継承し、継承後はまるでそのクラスのコードが現在のクラスにコピーされたかのようになります。
  • 親クラスのメソッドを上書きすることができますが、親クラスのメソッドは上書きされず、サブクラスに同じ名前のメソッドが追加されます。

サブクラスで親クラスのコンストラクタを上書きする場合、サブクラスのコンストラクタ内で super() を呼び出さなければなりません。そうしないとエラーが発生します。

# 🔶 Static Properties and Static Methods in Classes

...待補充

# 🔷 Array 主なメソッド

Standard built-in objects-Array (opens new window)

# ▫️ Array.prototype.push()

# ▫️Array.prototype.from()

  • 用途:從具有長度屬性的對象或可迭代對象創建數組。
  • 示例
let filledArray = Array.from({ length: 10 }, () => ({ hello: "goodbye" }));

# ▫️Array.prototype.fill()

  • 用途:填充數組的每個元素為相同的值。
  • 示例
    let filledArray = new Array(10).fill("hello");
    
  • 特點:適用於不可變值(如數字、字符串、布爾值)。
  • 問題:用於對象時,填充的是對同一對象的引用。
    let filledArray = new Array(10).fill({ hello: "goodbye" });
    

# 填充唯一對象

  • 方法:結合 fillmap
    let filledArray = new Array(10).fill(null).map(() => ({ hello: "goodbye" }));
    
  • 特點:創建具有唯一對象引用的數組。
  • 注意map 方法可能對大數據集來說代價昂貴。

# 使用 for 循環

  • 方法:傳統的 for 循環。
    let filledArray = new Array(10);
    for (let i = 0; i < 10; i++) {
      filledArray[i] = { hello: "goodbye" };
    }
    
  • 特點:避免了使用 map 方法。

# 使用展開語法 (...)

  • 方法:結合展開語法和 map
    let filledArray = [...new Array(10)].map(() => {'hello':'goodbye'});
    
  • 特點:避免使用 fill,但仍使用 map

# 🔸 Array.prototype.map()

Array.prototype.map() (opens new window)

  • map() メソッドは、新しい配列を作成し、元の配列の各要素をコールバック関数で処理した結果を含みます。
  • コールバック関数が必要で、その戻り値が新しい配列内の各要素になります。
const array = [1, 4, 9, 16];

// マップに関数を渡す
const result = array.map((item) => item * 2);

// 回調函數中會出現三個參數:當前元素、當前索引和原始數組。
result = arr.map(function (currentValue, index, arr) {

}[, thisArg]);

console.log(result);
// 期待される出力: Array [2, 8, 18, 32]

使用シーン:ウェブページで配列を表示し、各配列要素にはラベルが必要な場合。

const fruits = ["apple", "banana", "orange"];

const result = fruits.map((fruit) => `<li>${fruit}</li>`);

🔻 使用シナリオ

例子 1:使用 get 方法获取 Map 中的值

const userPreferences = new Map();

// 添加用户喜好设置
userPreferences.set("theme", "dark");
userPreferences.set("language", "en");

// 获取特定键的值
const theme = userPreferences.get("theme");
console.log(theme); // 输出 'dark'

例子 2:使用 set 方法添加或更新 Map 中的键值对

const userData = new Map();

// 添加用户数据
userData.set("username", "user123");
userData.set("email", "user@example.com");

// 更新用户数据
userData.set("email", "newuser@example.com");

例子 3:使用 has 方法检查 Map 中是否存在键

const userPermissions = new Map();

// 检查是否存在特定权限
userPermissions.set("admin", true);
userPermissions.set("editor", false);

if (userPermissions.has("admin")) {
  console.log("用户是管理员");
} else {
  console.log("用户不是管理员");
}

例子 4:使用 delete 方法从 Map 中删除键值对

const dataStore = new Map();

// 添加数据
dataStore.set("key1", "value1");
dataStore.set("key2", "value2");

// 删除数据
dataStore.delete("key1");

例子 5:使用 size 属性获取 Map 的大小

const cartItems = new Map();

// 添加购物车商品
cartItems.set("item1", { name: "Product A", price: 10 });
cartItems.set("item2", { name: "Product B", price: 15 });

// 获取购物车商品数量
const itemCount = cartItems.size;
console.log(`购物车中有 ${itemCount} 件商品`);

例子 6:使用 forEach 方法迭代 Map 中的键值对

const userRoles = new Map();

// 添加用户角色
userRoles.set("user123", "user");
userRoles.set("admin456", "admin");

// 遍历用户角色
userRoles.forEach((role, username) => {
  console.log(`${username} 的角色是 ${role}`);
});

例子 7:使用 keys 方法获取 Map 中的所有键

const menuItems = new Map();

// 添加菜单项
menuItems.set("home", "Home Page");
menuItems.set("about", "About Us");
menuItems.set("contact", "Contact Us");

// 获取所有菜单项的键
const menuItemKeys = Array.from(menuItems.keys());
console.log(menuItemKeys); // 输出 ['home', 'about', 'contact']

# ▫️ Array.prototype.filter()

  • filter() メソッドは、指定された関数によって評価された元の配列の要素から構成される新しい配列を作成します。
    • 配列から条件に合致する要素を取得できます。
    • コールバック関数の結果に基づいて、要素の保持または削除が決まります。結果が true なら保持し、false なら削除します。
const array = [1, 2, 3, 4];

const result = array.filter((item) => item % 2 === 0);

//代碼處理邏輯: number%2 === 0 ? true : false 如果返回true,就留,否則就不留
console.log(result);
// expected output: Array [2, 4]

# ▫️ Array.prototype.find()

  • find() メソッドは、与えられたテスト関数を満たす配列内の最初の要素の値を返します。そうでなければ、undefined を返します。
const array = [5, 12, 8, 130, 44];
const found = array.find((element) => element > 10);

console.log(found);
// expected output: 12

# ▫️ Array.prototype.reduce() (opens new window)

  • reduce() メソッドは、アキュムレータと配列の各要素(左から右へ)をコールバック関数に渡し、配列を単一の値にまとめます。

    • 配列の各要素に対して、コールバック関数を実行します。
    • コールバック関数の戻り値をアキュムレータに代入します。
    • reduce() メソッドは、関数をアキュムレータとして受け取り、配列の各値を(左から右へ)最終値まで縮小していきます。
    • 配列の要素が結合され、値が返されます。
const array1 = [1, 2, 3, 4];

// 0 + 1 + 2 + 3 + 4
const initialValue = 0;
const sumWithInitial = array1.reduce(
  (accumulator, currentValue) => accumulator + currentValue,
  initialValue
);

console.log(sumWithInitial);
// Expected output: 10

🔺 応用シナリオ

  1. Calculating Total Amount in a Shopping Cart Suppose you have a shopping cart application, where each item has a price and quantity, and you want to calculate the total amount.
const cart = [
  { item: "Apple", price: 1.2, quantity: 2 },
  { item: "Banana", price: 0.5, quantity: 5 },
  { item: "Orange", price: 0.8, quantity: 3 },
];

# ▫️ Array.prototype.forEach()

  • forEach() メソッドは、与えられた関数を配列の各要素に対して一度ずつ実行します。
const array = [1, 2, 3, 4];

array.forEach((item) => {
  console.log(item);
  // expected output: 1
});

# 🔷 Object method

# ▫️Object.defineProperties

//使用 Object.defineProperties
//創建和配置對象屬性: Object.defineProperties 被用來在 obj 對象上定義兩個屬性 a 和 b。這些屬性被配置為有特定值(分別為 1 和 2),並且是可枚舉的(enumerable: true)。

//屬性值和可枚舉性: 設置 value 來指定屬性的值,並通過 enumerable 標記屬性是否應該出現在對象的枚舉屬性中。

const obj = {};

Object.defineProperties(Obj, {
  a: {
    value: 1,
    enumerable: true,
  },
  b: {
    value: 2,
    enumerable: true,
  },
});

# ▫️ Object.entries()

  • 使用 Object.entries(obj) 將 obj 對象轉換為一個鍵值對的數組(二維數組)。每個子數組包含一個鍵和對應的值。
  • 返回值:给定对象自己的可枚举字符串键控属性键值对的数组。每个键值对都是一个包含两个元素的数组:第一个元素是属性键(始终是字符串),第二个元素是属性值。
const object = {
  a: "someString",
  b: 42,
};
const testArr = Object.entries(object);
console.log(testArr);

//遍歷鍵值對列表: 使用 for...of 循環遍歷 Object.entries(obj) 返回的鍵值對列表,並打印每個鍵和對應的值。 `new Map([["a", 1], ["b", 2]])` 創建了一個包含兩個鍵值對的 Map 對象。
for (let [k, v] of testArr) {
  console.log(k, v);
}

//用map方法將鍵值對列表轉換成map類型的數據: 通過將鍵值對的數組傳遞給 Map 的構造函數來創建一個新的 Map 對象。

const m = new Map([
  ["a", 1],
  ["b", 2],
]);
  • 應用 1: Converting an Object to a Map:
    • The Map() constructor accepts an iterable of entries. With Object.entries, you can easily convert from Object to Map:
const obj = { foo: "bar", baz: 42 };
const map = new Map(Object.entries(obj));
console.log(map); // Map(2) {"foo" => "bar", "baz" => 42}
  • 應用 2: Iterating through an Object
// Using for...of loop
const obj = { a: 5, b: 7, c: 9 };
for (const [key, value] of Object.entries(obj)) {
  console.log(`${key} ${value}`); // "a 5", "b 7", "c 9"
}

// Using array methods
Object.entries(obj).forEach(([key, value]) => {
  console.log(`${key} ${value}`); // "a 5", "b 7", "c 9"
});

# ▫️ Object.fromEntries()

  • Object.fromEntries() 是 Object.entries() 的逆操作,作用是将一个键值对数组转化为對象,返回的是新對象,不改变原对象
  • 只是 Object.entries() 只返回字符串键属性,而 Object.fromEntries() 还可以创建符号键属性。
Object.fromEntries([
  ["a", "1"],
  ["b", 2],
]);
  • 也可以传入一个 Map 将其转为对象
const map = new Map().set("a", 1).set("b", 2);

Object.fromEntries(map);

Object.fromEntries()實際應用:

  1. 過濾屬性
function foo(obj, ...keys) {
  return Object.fromEntries(
    Object.entries(obj).filter(([key]) => keys.includes(key))
  );
}

console.table(foo({ name: "oli", age: "12" }, "name"));

這個 foo 函數接受一個對象和一系列鍵作為參數,然後返回一個新的對象,其中只包含指定的鍵及其相應的值。具體來說:

  1. Object.entries(obj) 會將 obj 轉換為一個二維數組,每個內部數組包含一對鍵值(例如 [['name', 'oli'], ['age', '12']])。

  2. .filter(([key]) => keys.includes(key)) 會過濾這個數組,只保留那些鍵存在於 keys 參數中的鍵值對。

  3. Object.fromEntries() 將過濾後的二維數組轉回為對象。

在您的例子中,調用 foo({ name: "oli", age: "12" }, "name") 會返回一個只包含 name 鍵和其對應值的新對象。因此,這個函數調用的輸出將是:

{ "name": "oli" }

console.table 會以表格的形式在控制台輸出這個對象,顯示 name 這一列及其對應的值 oli

  1. 将 url 查询字符串转为对象
query = Object.fromEntries(new URLSearchParams("foo=bar&baz=qux"));

🔻 應用 1: 使用 Object.entriesObject.fromEntries 的代碼例子

例子 1:将对象转换为 Map 并操作数据

const obj = { a: 1, b: 2, c: 3 };

// 使用Object.entries将对象转换为Map
const map = new Map(Object.entries(obj));

// 在Map上进行数据操作
map.set("d", 4);
map.delete("b");

// 使用Object.fromEntries将Map转换回对象
const newObj = Object.fromEntries(map);

console.log(newObj); // 输出 { a: 1, c: 3, d: 4 }

例子 2:将路由参数对象转换为 Map 并重新映射键名

const routeParamsObj = { userId: 123, actionType: "edit" };

// 使用Object.entries将路由参数对象转换为Map
const routeParamsMap = new Map(Object.entries(routeParamsObj));

// 重新映射键名
routeParamsMap.set("id", routeParamsMap.get("userId"));
routeParamsMap.delete("userId");

// 使用Object.fromEntries将Map转换回路由参数对象
const newRouteParamsObj = Object.fromEntries(routeParamsMap);

console.log(newRouteParamsObj); // 输出 { id: 123, actionType: 'edit' }

这些示例演示了如何使用 Object.entriesObject.fromEntries 来进行对象和 Map 之间的转换,并对数据进行操作和重映射。

🔻 應用 2: 將對象轉換為 Map

當將对象转换为 Map 时,有许多应用场景,以下是其中一些示例:

  1. API 响应数据解析: 当从 API 获取数据时,通常以 JSON 对象的形式返回。将 JSON 对象转换为 Map 可以更容易地提取和操作所需的数据字段。
const apiResponse = { id: 1, name: "John", age: 30 };
const dataMap = new Map(Object.entries(apiResponse));
console.log(dataMap.get("name")); // 输出 'John'
  1. 表单数据处理: 在 Web 应用程序中,用户提交的表单数据通常作为对象传输。将表单数据转换为 Map 可以更容易地验证、操作和处理这些数据。
const formData = { username: "user123", email: "user@example.com" };
const formDataMap = new Map(Object.entries(formData));
console.log(formDataMap.get("email")); // 输出 'user@example.com'
  1. 路由参数: 在一些 Web 框架中,路由参数可能作为对象传递给控制器或路由处理程序。将路由参数转换为 Map 可以更方便地访问它们。
const routeParams = { id: 123, action: "edit" };
const routeParamsMap = new Map(Object.entries(routeParams));
console.log(routeParamsMap.get("id")); // 输出 123
  1. 配置管理: 在应用程序中,通常需要存储和管理配置数据。将配置对象转换为 Map 可以提供更好的配置管理和访问。
const config = { apiKey: "your-api-key", apiUrl: "https://api.example.com" };
const configMap = new Map(Object.entries(config));
console.log(configMap.get("apiUrl")); // 输出 'https://api.example.com'
  1. 数据转换和处理: 在某些情况下,需要对数据进行转换或处理,例如将对象中的某些字段重新映射到新的键。将对象转换为 Map 可以帮助执行这些操作。
const person = { first_name: "John", last_name: "Doe" };
const personMap = new Map(Object.entries(person));
personMap.set(
  "full_name",
  `${personMap.get("first_name")} ${personMap.get("last_name")}`
);
console.log(personMap.get("full_name")); // 输出 'John Doe'

# ▫️ Object.assign()

# ▫️ Object.create()

Reference: Object.fromEntries (opens new window)

# Data Structure

# Object 和 Map 區別

共同點: Object 和 Map 都允許你按鍵存取一個值、刪除鍵、檢查一個鍵是否存在、以及迭代其餘的鍵。

  1. 構造方法的不同
  • Object 的构造方法:Object可以使用对象字面量的方式创建,也可以使用new Object()Object.create(null)来创建。

    const obj = {
    key1: "value1",
    key2: "value2",
    };
    
    //構造方法
    const obj = new Object();
    const ojb2= Object.create(null);
    
  • Map 的构造方法:Map 需要使用new Map()的方式来创建,也可以通过传入一个包含键值对的数组来初始化。

    const map = new Map()
    const map = new Map([
    ["key1", "value1"],
    ["key2", "value2"],
    ]);
    
  1. value 的類型的不同
  • Object: 鍵類型必須是 String 或者 Symbol,如果是非 String 類型,會進行數據類型轉換,轉換成 String 類型,再進行操作。

    const obj = {
    1: "value1",
    true: "value2",
    undefined: "value3",
    null: "value4",
    [Symbol()]: "value5",
    };
    
    console.log(obj); // {1: "value1", true: "value2", undefined: "value3", null: "value4", Symbol(): "value5"}
    

mapobject

  • Map: 鍵類型可以是任意類型,包括原始類型和引用類型, 不會進行數據類型轉換。 在添加鍵值對時, 會通過 === 來判斷鍵是否相等。

    const map = new Map( );
    map.set(1, "value1");
    map.set('a',1)
    map.set(true, "value2");
    map.set(undefined, "value3");
    map.set(null, "value4");
    map.set(Symbol(), "value5");
    
    console.log(map); // Map(5) {1 => "value1", true => "value2", undefined => "value3", null => "value4", Symbol() => "value5"}
    
  1. 鍵的順序
  • Object

    • key 是無序的, 不會按照添加的順序返回。 1.對於>=0 的整數, 會按照大小排序。對於小數和負數, 會當作字符串處理。
    • 對於字符串, 會按照定義的順序輸出
    • symbol 類型,會直接過濾掉,不會進行輸出。如果想輸出, 需要使用Object.getOwnPropertySymbols方法。
  • Maps

    • key 是有序的, 會按照添加的順序返回。
    • key 可以是任意類型, 包括原始類型和引用類型。
    • key 的比較是===, 即使是兩個看起來一樣的數字, 也會被認為是不同的鍵。
    • size 屬性可以獲取 Map 的大小。
  1. 鍵值對的訪問
  • Object

    • 添加或者修改屬性, 通過 點或者中括號的方式訪問。
    • 判斷屬性是否存在, 通過 in 或者 hasOwnProperty方法。
    • 刪除屬性, 通過 delete 操作符。
  • Map

    • 添加或者修改屬性, 通過set方法。
    • 判斷屬性是否存在, 通過has方法。
    • 刪除屬性, 通過delete方法。
    • get 方法可以獲取屬性值。
    • clear 方法可以清空所有屬性。
    • 通過keys方法可以獲取所有的 key。
    • 通過values方法可以獲取所有的 value。
    • 通過entries方法可以獲取所有的 key value。
  1. 迭代
  • Object

    • 不具備 Iterator 特性,無法使用 for...of 循環迭代。
  • Map

    • 具備 Iterator 特性,可以使用 for...of 循環迭代。
const map = new Map([
  ["key1", "value1"],
  ["key2", "value2"],
]);

for (const [key, value] of map) {
  console.log(`${key} ${`v`alue}`); // "key1 value1", "key2 value2"
}
  1. JSON.stringify() 轉換
  • Map 類型的數據無法使用 JSON.stringify() 方法轉換為 JSON 字符串, 需要先轉換為對象。
const obj = { a: 1, b: 2, c: 3 };
const map = new Map([
  ["key1", "value1"],
  ["key2", "value2"],
]);

console.log(JSON.stringify(obj)); // {"a":1,"b":2,"c":3}
  1. ObjectMap 的性能比較
  2. size-Object
  • Object 沒有 size 屬性, 需要通過 Object.keys(obj).length 來獲取對象的大小。
  • Map 有 size 屬性, 可以直接獲取 Map 的大小。

🔺 Reference: 4 Ways to Populate an Array in JavaScript (opens new window)

# Set 和 Map 區別

以下是 Set 和 Map 之间的一些主要区别和用途:

  1. Set(集合):
    • 用于存储唯一值,不允许重复的元素。
    • 主要用途是存储一组唯一的元素,如集合、去重和检查元素是否存在。
    • 不需要键值对,只存储单个值。
    • 可以通过add方法添加元素,使用has方法检查元素是否存在,使用delete方法删除元素。
const uniqueNumbers = new Set([1, 2, 3, 2, 4, 5]);
console.log(uniqueNumbers); // 输出 Set { 1, 2, 3, 4, 5 }
  1. Map(映射):
    • 用于存储键值对,允许根据键来访问值。
    • 主要用途是创建键值对的映射,可以用于任何需要键值关联的场景。
    • 存储键值对,键可以是任何数据类型,值也可以是任何数据类型。
    • 可以通过set方法添加键值对,使用get方法根据键获取值,使用delete方法删除键值对。
const userPreferences = new Map();
userPreferences.set("theme", "dark");
userPreferences.set("language", "en");
console.log(userPreferences.get("theme")); // 输出 'dark'

Set 是 JavaScript 中的一种数据结构,它具有许多优势,使其在各种应用场景中非常有用。以下是 Set 的一些主要优势:

  1. 唯一性: Set 中的值是唯一的,不会重复。这意味着你可以使用 Set 来存储一组唯一的元素,而不必担心重复值。
const uniqueNumbers = new Set([1, 2, 3, 2, 4, 5]);
console.log(uniqueNumbers); // 输出 Set { 1, 2, 3, 4, 5 }
  1. 查找和去重: 使用 Set 可以轻松查找特定元素是否存在于集合中,并且在添加元素时自动去重。
const fruits = new Set();
fruits.add("apple").add("banana").add("apple");

console.log(fruits.has("apple")); // 输出 true
console.log(fruits.size); // 输出 2,自动去重
  1. 集合操作: Set 提供了各种集合操作方法,如交集、并集和差集,使你能够方便地进行数据操作。
const set1 = new Set([1, 2, 3]);
const set2 = new Set([2, 3, 4]);

// 交集
const intersection = new Set([...set1].filter((value) => set2.has(value)));
console.log(intersection); // 输出 Set { 2, 3 }

// 并集
const union = new Set([...set1, ...set2]);
console.log(union); // 输出 Set { 1, 2, 3, 4 }

// 差集
const difference = new Set([...set1].filter((value) => !set2.has(value)));
console.log(difference); // 输出 Set { 1 }
  1. 队列操作: Set 的特性允许你实现一个简单的队列结构。可以使用 Set 来添加元素到队尾,并从队首删除元素。
const queue = new Set();

queue.add("item1");
queue.add("item2");

const firstItem = queue.values().next().value; // 获取队首元素
queue.delete(firstItem); // 删除队首元素
queue.delete(queue.value.next().value); // 删除队首元素

总之,Set 在 JavaScript 中的使用非常灵活,适用于处理唯一值的情况以及各种集合操作。它可以在查找、去重、集合操作和队列等多种场景中提供便利的解决方案。