---
# System prepended metadata

title: JavaScript is Hard as Rock

---


# JavaScript is Hard as Rock

## Basic Scope: let v.s var

What are the printing results?

- Scope

```javascript
if(true){
  var x = 3;
}
console.log(x);
```

函數作用域（function-scoped）
在函數外宣告，則是全域作用域（globally-scoped）

thus, x = 3 (global)

```javascript
if(true){
  let x = 3;
}
console.log(x);
```
區塊作用域（block-scoped），只在其宣告的區塊（如 if、for 等）內有效
導致一個引用錯誤（ReferenceError），因為 x 在這個作用域中並不存在。所以，該程式碼執行時將會報錯，而不會列印出任何數值。

## Hoisting: var 宣告在前

var 會被提升，但只有宣告。

```javascript
console.log(x);
var x = 3;
```
undefined
```javascript
console.log(x);
let x = 3;
```
（ReferenceError），因為 x 在這個時刻還未被宣告。

```javascript
var x = 3;
function test(){
  x = 10;
  var x = 5;
}
test();
console.log(x);
```

在 test 函數內部，var x = 5; 的宣告會因 hoisting 被提升至函數作用域的頂部。
當執行 x = 10; 時，實際上是修改了函數作用域內的 x，而不是全域作用域的 x。

因此，當 test() 函數執行完畢後，全域變數 x 的值仍然是 3。

## CallBack

```javascript
let text = "hey!";

console.log(1);

function useless(tryCallback) {
  console.log(2);
  return tryCallback();
}

function getText() {
  console.log(3);
  return text;
}

console.log(4);

if(useless(getText) === text){
    console.log(5 + text);
}
  
console.log(6);
```

接下來，console.log(4); 列印 4。注意到這時 function useless 和 function getText 都已經被定義，但尚未被調用。

if(useless(getText) === text) 條件句開始執行。

首先，useless(getText) 被調用：

在 useless 函數內部，console.log(2); 列印 2。
然後調用 tryCallback()，即 getText() 函數：
在 getText 函數內部，console.log(3); 列印 3。
getText 返回 text 的值，即 "hey!"。
useless 函數返回 "hey!"。
接著，檢查 if 條件 "hey!" === "hey!"，該條件為真。

因此，console.log(5 + text); 列印 5hey!。

最後，console.log(6); 列印 6。



### Defines a callback function directly as an argument
#### Click, API Call
```javascript
if(useless(function(){return text;})===text){
    console.log(5 + text);
}
```
Use for: executing code on a button click, call api...

---

### Event-Driven
#### Callbacks can also be called by the browser
```javascript
document.body.addEventListener("mousemove", function(){
    let second = document.getElementById("second"); 
    addMessage(second, "Event: mousemove");
});
```
That’s defined as an event handler to the mousemove event, and that will be called by the browser when that event occurs.


### SORT
升序排序
```javascript
let values = [0, 3, 2, 5, 7, 4, 8, 1];
values.sort(function(value1, value2){ // comparator
    return value1 - value2;
});
```
We provide a callback that the JavaScript engine will call every time it needs to compare two items.


## Promise

The Promise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value. Executor passed to Promise constructor executes synchronously.

```javascript
new Promise((resolve)=>{
    console.log(10)
    resolve(3);
}).then((data)=>{
    console.log(data);
}).catch((error)=>{
    console.log(error);
});
console.log("end");
```

```javascript
async function example() {
    console.log(10);
    const data = await Promise.resolve(3);
    console.log(data);
}

example().catch((error) => {
    console.log(error);
});
console.log("end");
// What is the printing result?
```

當新的 Promise 被創建時，傳遞給它的執行器函數（executor function）立即同步執行。因此，console.log(10) 會首先被執行，列印 10。

resolve(3) 被調用，標記 Promise 為成功（fulfilled）狀態，並將值 3 作為結果。

.then((data) => { console.log(data); }) 排定了一個當 Promise 解決時執行的回調函數。然而，這個回調函數不會立即執行，而是會在當前執行棧清空後的微任務隊列（microtask queue）中被排定。

console.log("end") 接著被執行，列印 "end"。

最後，當前執行棧清空後，微任務隊列中的 .then 回調被執行，列印出 3。

### Promise.all()
Promise.all() 方法接受一個 Promise 對象的數組作為參數。
它返回一個新的 Promise，這個 Promise 在所有傳入的 Promise 都成功解決（fulfilled）時解決，並將每個 Promise 的結果組成一個數組作為結果。

### Promise.any()
Promise.any() 接受一個 Promise 對象的數組作為參數。
它返回一個 Promise，這個 Promise 在任何一個傳入的 Promise 解決時解決，並將第一個解決的 Promise 的結果作為結果。
如果所有傳入的 Promise 都失敗，則返回的 Promise 會以一個 AggregateError 為原因而失敗。

### Promise.race()
Promise.race() 同樣接受一個 Promise 對象的數組作為參數。
它返回一個 Promise，這個 Promise 會採用第一個解決或失敗的傳入 Promise 的狀態和結果作為自己的狀態和結果。
換句話說，無論是解決還是失敗，第一個改變狀態的 Promise 決定了 Promise.race() 返回的 Promise 的結果。

## Arrow Function

Arrow functions are a simplification of function expressions.

```javascript
let func=function(name){
  return "Greetings "+name;
};
let arrowFunc1=(name)=>{
  return "Greetings " + name;
};
let arrowFunc2=(name)=>("Greetings "+name);
let arrowFunc3=name=>"Greetings "+name;
```

Arrow functions cannot be used as constructors.

```javascript
let Point=(x,y)=>{
    this.x=x;
    this.y=y;
};
let p=new Point(3,4); // What will happen to this?
```

在這個例子中：

箭頭函數不能用作構造函數，這意味著您不能使用 new 關鍵字來創建箭頭函數的實例。
如果您嘗試這樣做（像上面的代碼那樣），JavaScript 將會拋出一個錯誤，提示說箭頭函數不能作為構造函數使用。
not a constructor

```javascript
document.addEventListener("click", function(){
    console.log(this); // What is "this" here?
});
document.addEventListener("click", ()=>{
    console.log(this); // What is "this" here?
});
```
在使用普通函數表達式的第一個 addEventListener 調用中，this 會被設置為觸發事件的元素，即 document。
在使用箭頭函數的第二個 addEventListener 調用中，箭頭函數不綁定自己的 this，它會捕獲其包含作用域中的 this 值。在這個例子中，如果這段代碼是在全域作用域中運行的，箭頭函數中的 this 很可能指向全域對象（在瀏覽器中是 window）；如果這段代碼是在某個封閉的作用域或模塊中運行，this 的值將依該作用域而定。


# What are the printing results?

1.
```javascript
let result=subtract(1,2);
function subtract(a, b){ // declaration
    return a-b;
};
console.log(result);
```
-1

⭕️ 函數宣告會被提升（hoisting），因此在代碼執行時，函數 subtract 已經可用。

2.
```javascript
let result=subtract(1,2);
let subtract=function(a, b){ // expression
    return a-b;
};
console.log(result);
```

這裡會發生錯誤。因為在嘗試調用 subtract 函數時，它還沒有被定義。函數表達式不會被提升。Referrence Error

3.
```javascript
let a=1;
function b(){
    a=10;
    return;
    function a(){}
}
b();
console.log(a);
```
10

❌ 您的答案是 10，但實際上結果應該是 1。在函數 b 中，function a(){} 創建了一個同名局部變數 a，並且由於函數提升，它覆蓋了外部作用域的變數 a。因此，a = 10; 實際上修改的是局部變數 a，而不是外部的 a。

4.
```javascript
let test=a=>b=>a+b*3;
// How to call test correctly?
```
❌ test(a)(b)，如 test(1)(2)

5.
```javascript
function test(a, b, ...c){
    console.log(a, b, c);
}
test(1, 2, 3, 4, 5, 6);
test(1, 2, 3, 4);
test();
```
⭕️ 1,2,[3,4,5,6]
⭕️ 1,2,[3,4]
❌
不正確。實際上會列印 undefined, undefined, []，因為 a 和 b 都是 undefined，而 c 是一個空數組。


6.
```javascript
function test(){
    console.log("1");
}
window.setTimeout(test, 0);
console.log("2");
```
先 2 再 1 因為 setTimeout 機制
⭕️ 即使 setTimeout 的延遲時間設置為 0，回調函數 test 也會在當前執行棧清空後的任務隊列中排隊

https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/EventLoop

7.
```jsx
<button id="btn">Click</button>
<script>
// Is this code snippet do what it wants to do?

btn.addEventListener("click", function(){
    // Want to delay 1 second and do something
    window.setTimeout(function(){
        // Want to change button text color to red
        this.style.color="red";
    }, 1000);
});
</script>
```

`const btn = documument.getElementById("btn")`

change 'this' to 'btn'

⭕️ setTimeout 內的匿名函數中的 this 不會指向按鈕元素，而是指向全域對象（在瀏覽器中是 window）

## 閉包解決

```javascript
// 获取按钮元素

btn.addEventListener("click", function(){
    // 延迟 1 秒后执行
    window.setTimeout(function(){
        // 修改按钮文字颜色为红色
        this.style.color = "red";
    }, 1000);
});
```


⭕️ 或是使用箭頭函數：

```javascript
btn.addEventListener("click", function() {
    window.setTimeout(() => {
        this.style.color = "red";
    }, 1000);
});
```

⭕️ 或是直接重新賦予 this:
```javascript
btn.addEventListener("click", function() {
    
    const button = this;
    
    window.setTimeout(function() {
        button.style.color = "red";
    }, 1000);
});
```

⭕️⭕️ 甚至使用 bind：

```javascript
btn.addEventListener("click", function() {
    window.setTimeout(function() {
        this.style.color = "red";
    }.bind(this), 1000);
});
```

👉 btn 變量實際上已經是按鈕元素的引用，因為它是按鈕的 id 屬性。在大多數現代瀏覽器中，具有 id 屬性的 HTML 元素會自動成為全域變量。

然而，這種自動創建全域變量的做法並不是最佳實踐，因為它依賴於瀏覽器的特定行為，並且可能會導致代碼難以理解和維護。更好的做法是明確地獲取元素引用，如使用 document.getElementById('btn')。這樣做提高了代碼的清晰度和可移植性。

＝