# 讀書會 ## 1. Const Let Var 差異 ```jsx= { let name = ‘tina’ let name = ‘wang’ } ``` <span style="color:salmon">不可能會console.log 出 wang(不能重複宣告)</span> block scope Const Let function scope Var ## 2. Settimeout ```javascript= for (var i = 0; i < 5; i++){ setTimeout(function(){ console.log(i); },1000); } ``` **解法 * 先改成let就可以了,因為是block scope** * IIFE把settimeout 包起來 ```javascript= for (var i = 1; i <= 5; i++) { ((i) => { setTimeout(() => { console.log(i); }, 1000); })(i); } ``` ## 3. JS Question ![](https://i.imgur.com/Q1OTUKu.png) ```javascript= { (function(){ var a = b = 3; })() console.log('a', typeof a !== 'undefined') // console.log('b', typeof b !== 'undefined') } ``` 解答: `var a = b = 3 ` 會被拆解成 ``` b = 3 var a = 3 變成全局變數(實務上可能會被禁止,但瀏覽器可以實踐) ``` 而因為 var a 是 functional scope 所以離開 scope 後該變數就會消失,因此下方印出會是 not defined ## 4. (0.1 + 0.2) === 0.3 false 因為JS是二進位,所以會有誤差 1. 先做倍化 2. 浮點數陷阱 toPrecision是轉文字,再透過 parseFloat((0.1+0.2).toPrecision(12)) 3. 用套件 math.js **重點語法:parseFloat / toPrecision** * **parseFloat:** 將字串轉化成數字,會忽略前後空白。當遇到無法解析的字元,會忽略該字元及其後的所有字元,停止解析於此字元,並回傳目前為止的結果。若第一個字元就無法被解析,會回傳 NaN。 * parseInt 類似處理,但可以選擇要幾進位,但只會處理整數 * toPrecision:把數字格式化為指定的長度,若是沒有放參數,則會直接把數字轉為字串回傳。 ```javascript= var num = 13.3714; var a = num.toPrecision(); //13.3714 var b = num.toPrecision(2); //13 var c = num.toPrecision(3); //13.4 var d = num.toPrecision(10); //13.37140000 ``` ## 5. hoist * 提升 hoist JS var function會經過提升 * let const 也會提升,在賦值前不能取用 * 暫時性死區 temporary dead zone ## 6. undefined / null / not defined 1. not defined: 沒宣告也沒賦值 2. undefined: 有宣告但沒有賦值 3. null: DOM 選擇的節點不存在就會有null,有宣告但是抓不到對應的值,被給了一個空值(有宣告有賦值) ## 7. var foo = function(){} / function bar(){} 差異 兩種宣告函數的方法 題目跟hoist有關 變數提升只有變數本身上去,賦值行為在下面 Function Statements: function bar(){} ```javascript= console.log(foo()) foo是unfined // foo is not a function console.log(bar()) var foo = function(){} function bar(){} ``` ## 8. 強制轉型 ```javascript= true + 0 //1 true + 'xyz' truexyz true + true //2 true + false //1 ``` 以下會強制轉型 * \+ * == * if() * while() mdn js 嚴格相等 1. '+' 是否有自串 轉為字串相加 2. 變為數字相加 ## 9. IIFE 立即執行函數 在我們建立函式的同時,這段函式就會立即被執行了! 在 IIFEs 內所定義的變數並不會跑出去這個函式之外而干擾到程式其他的部分 - 會馬上執行,才不會被其他變數影響 - 不會造成變數污染 - 作用域分開來 ## 10. Closure **回答:function 本身和他 reference 的環境(),透過閉包可以避免變數污染以及創造function內的私有變數。** 補充:是function以及該function被宣告時所在的作用域環境(lexical environment)的組合。 出來的function ruturn 出來的function 一個 function 裡面包了另一個 function,同時會 return 裡面的 function 讓我們可以在外面使用到它。 scoped chain call stack heap memoty --> js garbage collection 大概念:函數和函數所reference的環境 用意:避免變數污染以及創造function內的私有變數 補充: 1. **詞彙環境 (Lexical Environment)** 直白一點理解,詞彙環境就是程式碼實際上、物理上,到底寫在哪裡, 2. **scope chain** 往外找的變數機制,就是所謂的範圍鏈 (Scope Chain)。 3. **Lexical Environment 和 Lexical Scope 之間最大的差異** 就是 Lexical Environment 是在程式執行中存放環境資訊的地方,而 Lexical Scope 則為在程式編譯時就已經決定好的作用域。 4. Javascript 為靜態範疇 (Lexical Scope),所以在程式執行的時候就已經決定了所有變數的 Scope。 5. **Execution Context** Javascript 共會建立兩種執行環境: 1. 全域執行環境 (Global Execution Context) 在執行任何程式之前,預設會建立的一個全域環境。 2. 函式執行環境 (Function Execution Context) 在函式執行 (invoke) 的時候會各別為函式建立專屬的執行環境, 執行一個函式就會建立一個該函式的執行環境,所以有可能同時會有多個函式執行環境。 順序如下: 1. 建立 global execution context 2. 執行 greet function 3. 建立 greet execution context 4. 存取 whattosay variable and 賦值 Hi 5. greet execution context 執行完畢結束消失,但 whattosay variable 仍存在記憶體中 6. 建立 sayHi execution context 7. 存取 name variable and 賦值 Tony 8. 利用 scope chain 去找 whattosay variable ![](https://i.imgur.com/AmQK0VB.jpg) 執行環境可以把的外部變數關住、包住那些他應該要參考到的變數,即使執行環境已經沒了。 這個包住所有可取用的變數的現象就稱為避包 ## 11. complete mul(2)(3)(4) ``` 方法一 function mul(x){ return function(y){ return function(z){ return x * y * z } } } console.log(mul(2)(3)(4)) //方法二 const mul = x => y => z => x * y * z ``` ## 12. 檢查變數型別,如何判斷[] {} null型別 ```javascript= //如果用typeof來檢查的話,答案會都是 object typeof[] typeof null typeof{} ``` 解法: 1. `Array.isArray`(一個方法) // Object 沒有 2. toString():`Object.prototype.toString.call(obj)` 3. `([]) instanceof Array` ## 13. call by reference / call by value * Keyword: Primitive type * Primitive type 是 by value,而 Object 和 Function 則是 by reference ### **by value:** * 存在stack memory * 建立變數時,會在記憶體中存在一個自己的位置,若此時將另一個變數等於元變數,則新變數會存在於兩個不同的記憶體位置,因此彼此並不會乎相干擾影響 * 原始型別是不可變的(immutable) ```javascript= let str = 'hello' str.toUpperCase() console.log(str) // hello ``` ### **by reference:** * 存在 heap memory * 一樣會在記憶體中給它一個位置,但若此時將它等同於另一個變數時,不會在給新變數一個新位置,而是指向同一個位址,因此當值改變的時候新變數的值也會改變,因為它們實際上是指稱到相同的位置。 > 如果是用 object literal 的方式指定物件的值,那麼就會是 by value ![](https://i.imgur.com/rFPsfz8.png) * call by sharing ```javascript= let y = [] function update(arr){ arr = [1,2,3] } update(y) console.log(y) //[] ``` ## 14. 拷貝物件或是陣列 ``` for in for of for(let [k, v] of Object.entries(obj)) test.slice() 也可以 JSON strinfy / parser ``` > 深複製,淺複製 => 裡面的reference有沒有被複製 > 淺複製:是指向同一個記憶體位置,會參考到同一個物件,並沒有將此物件拷貝到並建立出新的關聯 深複製:會獨立出來不共用同一個記憶體位置 1. for loop 2. slice 3. JSON 深複製 4. spread operator 5. array.concat 6. array.from * 複製陣列方法 Object.assign(target, sources) ```javascript= var obj = { a: 1 }; var copy = Object.assign({}, obj); console.log(copy); // { a: 1 } ``` **但要注意這個還是淺拷貝** ```javascript= var o1 = { a: 1 }; var o2 = { b: 2 }; var o3 = { c: 3 }; var obj = Object.assign(o1, o2, o3); console.log(obj); // { a: 1, b: 2, c: 3 } console.log(o1); // { a: 1, b: 2, c: 3 }, 目標物件本身也被改變。 //有相同屬性時合併物件 var o1 = { a: 1, b: 1, c: 1 }; var o2 = { b: 2, c: 2 }; var o3 = { c: 3 }; var obj = Object.assign({}, o1, o2, o3); console.log(obj); // { a: 1, b: 2, c: 3 },屬性c為o3.c的值,最後一個出現的屬性c。 ``` ## 15. 什麼是 this 1. 目前 指向的呼叫他的物件 execution context 2. this 是JavaScript程式在執行時的綁定(runtime binding),與函式在何處宣告無關,而是取決於函式被呼叫的方式以及地點。 3. arrow function 不在乎誰呼叫他,綁定到其定義時所在的物件,我們要了解一般函式在建立時是在 window 下,所以在 window 下使用箭頭函式自然會指向 window,要確實將箭頭函式宣告在物件內部,這樣 this 才會指向該物件。 use strict arrow function 不在乎誰呼叫他,而是當時在的環境 ## 16. call bind apply差異 call bind apply 改變 this call apply 第一個放指向的物件,第二個放參數,call要一個放,apply用陣列傳入 bind 回傳是函數 ## 17. this 觀念 箭頭函示 ES6 this 指向位置 執行環境中 不能被更改綁定的位置 this 指向不同 指向執行環境不一定是指向全域,無法透過 call bind apply 改變指向 arguments不能使用 ```javascript= function add(a,b){ console.log(arguments) } add(1,2) ``` function expression ## 18. const result = 0 && 1 || 1 && 2 * 答案 2 * 重點:運算子優先序列 && || 和 相依性 * && 若左邊能被轉為真,就回傳右邊,若左邊不能就回傳左邊 <div style="background: #F7CBCB; line-height:30px ; font-weight: 500; padding-left: 10px"> expr1 && expr2 <br> If expr1 can be converted to true, returns expr2; else, returns expr1.</div> ```javascript= 0 && true // 0 ``` 補充: 常見的 false value:null、NaN、0、empty string ("" or '' or \`\`)、undefined. * || 若左邊能被轉為錯,就回傳右邊,若左邊回傳真,則回傳左邊 <div style="background: #F7CBCB; line-height:30px ; font-weight: 500; padding-left: 10px; margin-bottom:8px"> expr1 || expr2 <br>If expr1 can be converted to true, returns expr1; else, returns expr2. </div> ## 19. JS如何處理非同步 **關鍵字:** 單執行緒(避免處理太複雜的情況)、web API 處理非同步(建議講第三方 API)、event loop 、 quene ## 20. explain give a example why use promise **非同步範例:** 1. 非同步傳回來 Promise 2. 操作 DOM 非同步 ## 21. PROMISE 組成為何? Function params 有 resolve reject,其中有判斷函示 ![](https://i.imgur.com/jvZK21Q.png) ## 22. 回傳值為何? ( fail ) ![](https://i.imgur.com/LVJ1fvm.png) ## 23. 回傳值為何? ( 12453 ) **重點: Promise 本身只是物件,是同步的** ![](https://i.imgur.com/kc7GU99.png) ## 24. 回傳值為何? ```javascript= setTimeout(()=>alert('timeout')) Promise.resolve() .then(()=> alert('promise')) alert('global ex. context') ``` 1. **答案:global -> promise -> timeout** 2. **重點:micro task vs marco task** ![](https://i.imgur.com/xpu27xK.png) **micro task:** 小任務,在主線任務結束後會見縫插針(如果有很多就一次噴完),常見的有promise、dom mutations **marco task:** Event、webAPI、HTML parse 順序:quene empty have micro? -> run -> marco task -> run ## 25. promise inorder 照順序 ![](https://i.imgur.com/xhxxUZc.png) **答案:**1475236 **流程:** 1. 先找同步的: 1 -> 4 -> 7 2. 非同步進佇列:settimeout promise settimeout 3. 先處理 micro task -> promise 4. -> 5 5. 處理第一個 settimeout 6. -> 2 7. 第一個 settimeout 中碰到 promise,先放到非同步佇列中等待 8. 此時主線清空 9. 還是先處理 micro task 10. -> 3 11. 最後處理第二個 settimeout 12. -> 6 **promise 本身是物件所以沒有同步或非同步** ## 26. 做出判斷是否為整數的函式 > 先思考有沒有可以自己寫,再想看看有沒有JS原生方式 答案: 1. **Math.floor 和原本自己比較** Math.floor:無條件捨去 Math.ceil:無條件進位 Math.round:四捨五入 ```javascript= let num = 2.6 Math.floor(num) //2 Math.ceil(num) //3 Math.round(num) //3 let num = 2.4 Math.floor(num) //2 Math.ceil(num) //3 Math.round(num) //2 ``` 2. **Number.isInteger()** JS 原生方法 3. x - Math.floor(x) 但是過大的時候會出事 4. parseInt(x, 10)//sometime false 可以將數字轉換成幾進位的數 5. Number.isInteger **Hint !** * 先和面試官確定題目回傳,傳進來得值有限制嗎? * 若數字太大的話 JS 會把他變成 1e+26 ## 27. duplicate([1,2,3,4,5]) 如果要問這個問題的話,可以先由簡單的方法開始說 push return array.length **解法:** 1. [...arr, ...arr] 2. for loop 3. arr.concat(arr) * 4. return [].concat.apply(arr, arr) 5. repeat + join + split* ## 28. create 1~10 into array 1. Map(Number) 2. for loop 3. const result = Array.from({length:10}, (_, i) => i+1) also Array.from(Array(10), (_, i) => i+1) 5. Array.from(Array.keys()) //few see 6. Array(10) => [empty, ].map 8. Array(10) => [empty, ].fill((_, i) => i+1) **Array(10).fill().map((, i) => i+1);** 補充: * Array.from ```javascript= Array.from('foo'); // [ "f", "o", "o" ] const set = new Set(['foo', 'bar', 'baz', 'foo']); Array.from(set); // [ "foo", "bar", "baz" ] Array.from([1, 2, 3], x => x + x); // [2, 4, 6] ``` * Array.prototype.fill() 會修改原陣列 ```javascript= [1, 2, 3].fill(4) // [4, 4, 4] [1, 2, 3].fill(4, 1) // [1, 4, 4] [1, 2, 3].fill(4, 1, 2) // [1, 4, 3] [1, 2, 3].fill(4, 1, 1) // [1, 2, 3] [1, 2, 3].fill(4, 3, 3) // [1, 2, 3] [1, 2, 3].fill(4, -3, -2) // [4, 2, 3] [1, 2, 3].fill(4, NaN, NaN) // [1, 2, 3] [1, 2, 3].fill(4, 3, 5) // [1, 2, 3] Array(3).fill(4) // [4, 4, 4] [].fill.call({ length: 3 }, 4) // {0: 4, 1: 4, 2: 4, length: 3} ``` ## 29. 2 unique array find the duplicate element ask the return value what is unique array?? 1. hash table =>{1:1, 2:2} 2. double for loop 3. arr.filter(item => arr2.indexOf(item) !== -1) O(n*n) 4. arr.filter(item => arr2.includes(item) !== -1) O(n*n) 5. [arr1, arr2] => sort => for loop ## 30. explain array.map 1. for loop 2. callback 3. callback(item, index, array) 4. create new map ```javascript= Array.prototype.myMap = function (callback) { const result = [] for(let i = 0;i <this.length; i++){ result.push(callback(arr[i], i, arr)) } } ``` ## 31. PALIMDROME 1. str.replace(/\W/g, "") 2. split.reverse.join(only for array) 3. for loop