# [JS201] 進階 JavaScript:那些你一直搞不懂的地方
###### Date : 2021 Jul.26 - Jul.30
#### Variables
- Javascript 七種原始型態
- null
- nudefined
- Number ( NaN )
- String
- Boolean
- Symblol (since ES6)
- Object,除了以上 6 種其他都是 Object
> Primitive type is not Immutable(不能改變型態)
- 可以用 `typeof` 來判斷型別,但是以下需注意
```javascript
console.log(typeof null);
// Object, 自從有 JavaScript 開始就是這樣了,可能是最原始的 Bug
console.log(typeof [])
// Object, array 的型別還是 object
// 可以用 isArray 來判斷,但也要注意瀏覽器支援問題
console.log(Object.prototype.toString.call(null))
// [Object Null]
console.log(Object.prototype.toString.call([]))
// [Object Array]
// 也可以用這個來判斷 array 有些套件也還是用這個方法
```
- `=` 賦值
- number, string ...
```javascript
var a = 10
var b = a
b = 20
console.log(a, b) // 10, 20
```
- obj, array
```javascript
var obj = { num: 10 }
var obj2 = obj
obj2.num = 20
console.log(obj.num, obj2.num) // 20, 20
```
- `==` 跟 `===` 的差別
- 可參考 : [JavaScript運算符:== VS. ===](https://www.itread01.com/articles/1476355842.html)
- `===` 是型別也要相同
```javascript
console.log(2 == '2') // true
console.log(2 === '2') // false
var a = Number('hello')
console.log(a === a) // false
// a 的型別是 Number 、值是 NaN
// NaN 不等於任何東西,包含自己!
```
- obj, arry 的 `===`
```javascript
var obj = { num: 10 }
var obj2 = obj
obj2.num = 20
console.log(obj === obj2) // true
console.log({} === {}) // false
```
#### Scope
- 變數的生存範圍
- var : 以 function 為準,會一直從 Scope Chain 往上找
- let, const : 以 block `{}` 為準,Ex : for {...} , if {...}
#### Hoisting
- hoisting 是什麼
```javascript
console.log(a) // undefined
var a = 10
```
- hositing 的順序
- Piority ( 同名的會被覆蓋 ): functions > arguments > variables
```javascript
var a = 123
function test() {
// 可看成這裡有一個 var a
console.log(a)
var a = 456
}
test() // undefined
// -----------------------------
function test(a) {
// 可以看成這裡有一個 var a ,但是已經有 argument a 了,所以宣告不會覆蓋過去
console.log(a)
var a = 456
}
test(123) // 123
// ---------------------------
function test(a) {
console.log(a)
function a() {
console.log('hi')
}
}
test(123) // [function a]
```
- hositing 原理
```javascript
var a = 1;
function test(){
console.log('1.', a);
var a = 7;
console.log('2.', a);
a++;
var a;
inner();
console.log('4.', a);
function inner(){
console.log('3.', a);
a = 30;
b = 200;
}
}
test();
console.log('5.', a);
a = 70;
console.log('6.', a);
console.log('7.', b);
// undefined, 7, 8, 30, 1, 70, 200
```
- Execution Context 執行環境
- Stack
- Variable Object 變數物件
- function 執行前會先初始化 var 成 undefined,如果沒有會在 Global 宣告一個
- 如果有重覆的名稱會依照 Pority 覆蓋
- let, const 的怪異行
- 所以 let 會 hositing 嗎
```javascript
let a = 10
function foo() {
console.log(a)
let a = 20
}
foo() // ReferenceError
// 如果會 hositing => undefined
// 如果不會 hositing => 10
```
- TDZ : temporal Dead Zone
- 在 let 賦值以前,如果存取就會 Error
```javascript
let a = 10
function test() {
let a
console.log(a) // undefined, this a TDZ
a = 20
}
test()
```
#### Closure
- Closure 是什麼
```javascript
function foo() {
let a = 0
return () => a++
}
let bar = foo()
console.log(bar())
console.log(bar())
console.log(bar())
// 0, 1, 2
```
- 外面沒辨法存取到 a ,a 被封裝在 foo() 裡了
- 原理,一樣可以從 EC, VO ( AO : Activation Object ) 下去看
```javascript
testEC : {
AO: {
b: 2,
inner: func
},
scpoeChain: [testAO, GlobalEC.VO]
}
Golbal EC: {
VO: {
a: undefined,
test: func
},
scpoeChain: [globalEC.VO]
}
```
- function 的 spcopeChain 如果還會用到,就會一直存在
- closure 說穿了就是可以從 scpoeChain 找到上層的變數,並保留起來
- 日常的 closure
```javascript
var arr = []
for (var i = 0; i < 5; i++) {
arr[i] = function() {
console.log(i)
}
}
arr[0]()
// 5
```
- 方法一 : closure
```javascript
var arr = []
for (var i = 0; i < 5; i++) {
arr[i] = logN(i)
}
function logN(n) {
return function() {
console.log(n)
}
}
arr[0]()
// 0
```
- 方法二 : IIFE ( Immediately Invoked Functions Expression )
```javascript
var arr = []
for(var i = 0; i < 5; i++) {
arr[i] = ( (e) => () => console.log(e) )(i)
}
arr[0]()
// 0
```
- 方法三 : 把 `var` 改成 `let`
```javascript
var arr = []
for (let i = 0; i < 5; i++) {
arr[i] = function() {
console.log(i)
}
}
arr[0]()
// 0
```
#### Prototype
- 什麼是物件導向 ( OOP )
- 用物件的方式去操作
```javascript
function createWallet(initMoney) {
var money = initMoney
return {
add: function(num) {
money += num
},
deduct: function(num) {
money -= (num >= 10) ? 10 : num
},
getMoney() {
return money
}
}
}
var myWallet = createWallen(99)
myWallet.add(1)
myWallet.deduct(100)
console.log(myWallet.getMoney) //90
```
- 範例
```javascript
class Dog {
constructor(name) {
this.name = name
},
getName() {
return this.name
},
sayHello() {
console.log('hello, i\'m ' + this.name)
}
}
var a = new Dog('aaa')
a.sayHello() // hello, i'm aaa
var b = new Dog('bbb')
b.sayHello() // hello, i'm bbb
```
- ES6 以前的 class
- function
```javascript
function Dog(name) {
var myName = name
return {
getName: function() {
return myName
},
sayHello: function() {
console.log(myName)
}
}
}
var a = Dog('aaa')
var b = Dog('bbb')
console.log(a.sayHello === b.sayHello)
// false
// 因為是不同的 function ,但應該要一樣,不然會佔太多資源
```
- 用 new & prototype 改寫
```javascript
function Dog(name) {
this.name = name
}
Dog.prototype.getName = function() {
return this.name
}
Dog.prototype.sayHello = function() {
console.log(this.name)
}
var a = new Dog('aaa')
var b = new Dog('bbb')
console.log(a.sayHello === b.sayHello)
```
- 原型鍊 : `pototype`, `__proto__`
```javascript
function Dog(name) {
this.name = name
}
Dog.prototype.getName = function() {
return this.name
}
var a = new Dog('aaa')
console.log(a.__proto__ === Dog.prototype)
// true
```
- 其中 a 為 Dog 的 instance ( 實例 )
- new
```javascript
function newDog(name) {
var obj = {}
// call 的第一個變數是 this
Dog.call(obj, name)
obj.__proto__ = Dog.prototype
return obj
}
```
- inheritance 繼承
- class
```javascript
class Dog {
constructor(name) {
this.name = name
}
sayHello() {
console.log(this.name)
}
}
// 使用 extends 繼承
class BlackDog extends Dog {
constructor(name) {
// 使用 super 才可以使用到上一層的 constructor
super(name)
// 如果不使用,會出現 Error
this.sayHello()
}
test() {
console.log('test', this.name)
}
}
const b = new BlackDog('bbb')
// bbb
```
#### This
- This 的意義
- 在 class 中
- 在 HTML 的元素的選取
- 在沒有意義的地方呼叫會指向上一層,Ex: windows, node
- `'use strict'` 的時候,this 會指向 undefined,不會指向 windows
- call, apply
- call(this, argument1, argument2, argument3)
- apply(this, [argument1, argument2, argument3])
- 使用 call 跟 apply 時都會改變 `this` 的值,差別是後面參數而已
```javascript
'use strick'
const obj = {
a: 123,
inner: {
test: function() {
console.log(this)
}
}
}
obj.inner.test()
obj.inner.test.call(obj.inner)
// 兩者等價,所以 this 的值都是 obj.inner
const func = obj.inner.test
func()
func().call(undefined)
// 因為 func 前面沒有東西,所以是 undefined
```
- bind : 強制綁定 this
```javascript
'use strick'
function log() {
console.log(this);
}
var a = { a: 1, log: log };
var b = { a: 2, log: log };
log();
a.log();
b.log.apply(a)
// undefined, a, a
```
- bind 用法
```javascript
'use strick'
const obj = {
a: 1,
test: function() {
console.log(this)
}
}
const bindTest = obj.test.bind('aaa')
bindTest.call('hello')
// aaa ,被 bind 的 function 使用 call 也無法改變 this 的值
```
- arrow function 的 this
- 一般 function
```javascript
class Test {
run() {
consoel.log('run this:', this)
setTimeout(function() {
console.log(this)
})
}
}
const t = new Test()
t.run()
// Test{}, windows
```
- 箭頭 function
```javascript
class Test {
run() {
consoel.log('run this:', this)
setTimeout( () => {
console.log(this)
})
}
}
const t = new Test()
t.run()
// Test{}, Test{}
// 因為 arrow function 的 this 跟你怎麼定義 function 有關
```
#### Ref.
- [Javascript typeof](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/typeof)
- [JavaScript運算符:== VS. ===](https://www.itread01.com/articles/1476355842.html)
- [🔰 Huli 胡立老師文章列表整理 🔰](https://hackmd.io/yIt3U-ppTKWkFYgJvMupbA?view)
---
#### 心得 2021.Jul.31
----
#### 以下更新 ( 2021 Jul.31 )
- var, let, const
- initializer
```javascript
var x
console.log(x) // undefined
let y
console.log(y) // undefined
const z
console.log(z) // Error
```
- re-declaration
```javascript
var x = 10
var x = 20
console.log(x) // 20
let y = 10
let y = 20
console.log(y) // Error
const z = 10
const z = 20
console.log(z) // Error
```
- re-assignment
```javascript
var x = 10
x = 20
console.log(x) // 20
let y = 10
y = 20
console.log(y) // 20
const z =10
z = 20
console.log(z) // Error
```
- function declaration / function expression
```javascript
sayHi() // Hi
function sayHi() {
console.log('Hi')
}
sayHello() // Error
const sayHello = () => {
console.log('Hello')
}
```

> 但其中,const & let 的 hoisting 在 function 中還是有的,Ex: TDZ
Ref: https://www.udemy.com/course/html5-css3-z/