# JS(6/10) Day 20 五倍紅寶石(第八屆共筆)(2021/7/23) 物件: 用 Object.keys (變數名稱)、Object.values (變數名稱)可印出所有 key 跟 value。 在物件裡面的函式會稱作方法,在外面就叫函式。 ## 閉包(closure): 一種特性,當使用高階函式時,可以將外層函式裡宣告的變數&值保留到內層函式使用。 ```=js // 外層函式 = a function a() { var x = 123 // closure 閉包 // 內層函式 = setTimeout setTimeout(() => { console.log(x) }, 1000) } a() ``` 結論:x 本來會隨著外層的泡泡破掉而消失,但 JS 卻能把 x 一起帶到 WebAPI 排隊。 過程:(網路上統整的結果,如有寫錯還請同學更正,感謝) 一開始 JS 會先建構全域執行環境、分配記憶體給函式及變數、建構函式及宣告變數&賦值。 => function a & setTimeout 、 var x = 123 及 console.log(x) 的 x 都會被建立起來。(console.log(x) 的 x 會因為 scope chain 的關係先在內層找有沒有宣告 x ,因為找不到所以往外層找,最後找到 var x = 123,所以內層的 x 會指向外層的 123) 再來會建構 function 的執行環境以及執行程式碼。 => 呼叫 function a ,建構 a 的執行環境,將 x 儲存在 a 的執行環境。 => 準備執行 setTimeout ,外層 a 的泡泡破掉,此時 var x = 123 因為一開始有被分配到記憶體,所以還會留在原地,不會跟著泡泡破掉而消失。 => 執行 setTimeout,x 能夠指向外層的 123,不會變成not defined。 --- ### 高階函式(HOF): 很好用,= 函式製造機,可以生出函式出來。 ex:3D列印機可以印出自己出來。 ## 物件導向(Object-oriented): 其實就是生物分類法,希望把共同的特性寫在前端,讓後端的物件能直接使用前端的特性。 => 我只要在前端寫一次,後端的東西就能夠使用,相當方便,不然如果後端的東西有100個的話就要寫100次,太累。 物件 = 屬性 + 行為 屬性:名詞 行為:動詞(function) 繼承 = 分類:可以讓子類別的東西使用上層類別(Super Class、Parent Class)的東西 建構函數: function 如同{ }可以用來建構物件,把他當成一般物件來用可以做到以下的事。 ```=js function heroCreater(name, action) { const attack = () => { console.log(`${name}使用絕招${action}`) } return { name, action, attack // 當參數和屬性同名時,可省略值不寫 // 原本應該長成 name: name, } } const h = heroCreater("小傑", "剪刀石頭石頭") h.attack() // "小傑使用絕招剪刀石頭石頭" console.log(h) // [object Object] { // action: "剪刀石頭石頭", // attack: () => { // window.runnerWindow.proxyConsole.log(`${name}使用絕招${action}`) // }, // name: "小傑" // } console.log(h.action) // "剪刀石頭石頭" ``` ### 產生空物件: 有兩種方法可用。 1. const a = { } 2. const b = Object.create(null) => 利用指定的原型(prototype)把東西灌進去。 重要觀念: 1. 所有的物件都有__proto__屬性 使用後可以找到他的原型。 2. 所有的 function 都有 prototype 屬性 預設的 prototype = { }。 .map 方法其實是 prototype 裡面的屬性,後面的值是 function,用來做事。 ```=js // 對 prototype 物件新增 attack 屬性,值是後面的 function Hero.prototype.attack = function() { console.log("attack") // } ``` 原型鏈(prototype chain):連續使用__proto__可以一層一層往上找到原型。 正常來說印出沒有的屬性應該要會得到 Error,但在 JS 會得到 undefined, 而印出沒有的屬性並呼叫會得到 TypeError,因為呼叫 undefined, undefined 不是 function,會出錯。 ```=js // 建構函數,通常會把函數名稱的首字改成大寫 hero => Hero ,方便辨識這是物件不是函數。 // 這邊的 Hero 稱為建構子,函式前面有 new 的都叫建構子。 function Hero(name, action) { // new 做的事情 // 1. 建立一個空物件 // 2. 把 this -> {} // 3. 把 this.__proto__ -> Hero.prototype ( 預設值: { } ) // 4. 自動 return this // 不要自己多寫 return,如果是物件會覆蓋掉 this 值 this.name = name this.action = action } ``` ```=js function hello() { console.log("hi") } const h = new hello() console.log(h) // hello { } ,裡面的 this = 函式名稱 + 空物件 console.log(this) // { } ,外面的 this 空物件 --------------------------- const h = hello() console.log(h) // undefined,沒有 return 值 ``` .map的來源: list.map 一開始會找不到,因為 list 沒有 map 方法可用 => 往.__proto__找,但因為 new 的關係會自動轉向到.prototype => prototype 裡面有 map 方法 => 最後會找到 list.prototype.map ```=js const list = [1, 2, 3, 4, 5] // 左邊跟 list = new Array(1, 2, 3, 4, 5) 是一樣的東西 list.__proto__ === Array.prototype // true list.__proto__.map === Array.prototype.map // true ``` 所有的 function 都有回傳值,當沒有寫 return 時會有兩種預設值: 1. 一般的 function 會回傳 undefined 2. 使用 new 建立的 function 會回傳 this 的值 ## class ES6才有的語法糖衣,本質上還是使用__proto__去做事。 class => new => instance(實體) (烤盤) (透過) (雞蛋糕) 如果 new Hero() 想帶引數進去,一定要搭配建構子constructor()使用。 constructor 裡面只放屬性,行為請寫在外面。 ```=js class Hero { constructor(name) { this.name = name } } let h = new Hero("奇犽") console.log(h) ``` ```=js class 靈長類 { squash() { console.log("!!!!"); } } // 黑猩猩是一種靈長類 class 黑猩猩 extends 靈長類 { } class 人 extends 靈長類 { } me = new 人() me.squash() you = new 黑猩猩() you.squash() ``` --- ###### tags: `JavaScript`