FE 讀書會(三): Object
Object
物件定義中的”鍵-值”,如果是一般的值的情況,稱為”屬性(property, prop)“,如果是一個函式,稱之為”方法(method)“。屬性與方法我們通常合稱為物件中的成員(member)。
const mObj = {
jedi: true,
age: 28,
}
mObj.age=29
console.log('mObj.jedi='+mObj.age)
雖然 mObj 是 const (read only), 但還是可以修改裡面屬性的值
console.log('mObj.name='+mObj.name) //undefined
mObj.name='HelloJay'
console.log('mObj.name='+mObj.name) //HelloJay
而且就算原本裡面沒有某個屬性,也會自動擴充,並且預設會給 undefined
Class
class Player {
constructor(name, age) {
this.mName=name
this.mAge=age
}
toString() {
return 'name='+this.mName+', age='+this.mAge
}
}
let player = new Player('HelloJay', 18)
console.log(player.toString()) //name=HelloJay, age=18
console.log(player.mName) //你猜猜
QA: 想想看,上面跟下面的宣告方式有何差別?
class Player {
constructor(name, age) {
mName=name
mAge=age
}
toString() {
return 'name='+mName+', age='+mAge
}
}
let player = new Player('HelloJay', 18)
console.log(player.mName) //你猜猜
console.log(mName) //你猜猜
this & scope
- 函式呼叫: 在一般情況下的函式呼叫,
this
通常都指向global物件。這也是預設情況。 - 建構式(constructor)呼叫: 透過
new
運算符建立物件實體,等於呼叫類型的建構式,this
會指向新建立的物件實例 - 物件中的方法呼叫:
this
指向呼叫這個方法的物件實體
Constructor 建構式
- 類別(class)的定義時,物件的屬性都只能在建構式中定義,這與用物件字面的定義方式不同
- 如果物件在初始化時不需要任何語句,那麼就不要寫出這個建構式
- 建構式或物件方法的多形(polymorphism)或覆蓋(Overriding),在JavaScript中沒有這種特性
- 建構式是會被限制只能有一個,而在物件中的方法(函式)也沒這個特性
- 如果你需要定義不同的建構式在物件中,因應不同的物件實體的情況,只能用函式的不定傳入參數方式,或是加上傳入參數的預設值來想辦法改寫
QA: 想想看,如果有兩個 constructor 會發生什麼事?
class Player {
constructor(name) {
this.mName=name
this.mAge=age
}
constructor(name, age) {
this.mName=name
this.mAge=age
}
toString() {
return 'name='+this.mName+', age='+this.mAge
}
}
let player = new Player('HelloJay', 18)
console.log(player.mName) //?
QA: 想想看,如果需要傳入多個參數怎辦?
//方法一: ES5 傳統方式 arguments object
//arguments object is an Array-like object
//The arguments object is not an Array. It is similar to an Array, but does not have any Array properties except length.
function checkName(name) {
console.log('name=' + name) //'name=a'
console.log(typeof arguments); // 'object'
for (var i = 1; i < arguments.length; i++) {
console.log('i='+i+', arg='+arguments[i])
}
console.log(arguments.length);
console.log(arguments.pop);
//如果我想把 arguments 轉為 array 呢?
let argsA = Array.prototype.slice.call(arguments);
let argsB = [].slice.call(arguments);
// ES2015
let argsC = Array.from(arguments);
let argsD = [...arguments];
console.log(typeof argsD[3]); // "number"
console.log(argsD.length); // 4
console.log(argsD.pop);
}
checkName('a', 'b', 'c', 5)
//方法二: ES6 不定參數 or 其餘參數(Rest parameters)
//(Rest parameters)有一個限制,就是這個參數一定是函式的"最後一個"
function checkName(...args) {
console.log(args)
}
checkName('a', 'b', 'c', 5) // ["a", "b", "c", 5]
checkName() // []
//方法三: ES6 默認參數
function animalSentence(animals1="tigers", animals2="bears") {
console.log(`Lions and ${animals1} and ${animals2}! Oh my!`)
}
animalSentence() // Lions and tigers and bears! Oh my!
animalSentence("elephants") // Lions and elephants and bears! Oh my!
animalSentence("elephants", "pigs") // Lions and elephants and pigs! Oh my!
animalSentence("elephants", "pigs", 'zebra') //你猜猜
function sum(a, b = 2, c) {
return a + b + c;
}
console.log(sum(1, 5, 10)); // 16
console.log(sum(1, undefined, 10)); // 13
Inheritance 繼承
- extends關鍵字可以作類別的繼承
- 繼承的子類別中的建構式,
super()
需要放在第一行 super
也可以用於指向上層父母類別,呼叫其中的方法或存取屬性- 沒有多重繼承,可以用合併多個物件產生新物件(合成 composition)、Mixins(混合)樣式,或是用ES6標準的Proxy物件,來達到類似多重繼承的需求
class Point {
constructor(x, y) {
this.x = x
this.y = y
}
toString() {
return '(' + this.x + ', ' + this.y + ')'
}
}
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y)
this.color = color
}
toString() {
return super.toString() + ' in ' + this.color
}
}
let pointObj = new ColorPoint(1, 2, 'white')
console.log(pointObj.toString())
console.log(pointObj instanceof Point)
Object.assign()
Object.assign()
是ES6中的新方法,在ES6前都是用迴圈語句,或其他的方式來進行物件拷貝的工作- 除了拷貝之外,也可以作物件的合併,合併時成員有重覆名稱以愈後面的物件中的成員為主進行覆蓋
//物件拷貝
const aObj = { a: 1, b: 'Test' }
const copy = Object.assign({}, aObj)
console.log(copy) // {a: 1, b: "Test"}
//物件合併
const bObj = { a: 2, c: 'boo' }
const newObj = Object.assign(aObj, bObj) //null或undefined在拷貝過程中會被無視
console.log(newObj) //{a: 2, b: "Test", c: "boo"}
Spread Operator 展開運算子
//ex1
let tmp = ['a','b','c']
let result=['1','2'].concat(tmp) //before ES6
console.log(result) // ["1", "2", "a", "b", "c"]
//使用 ES6 Spread Operator
result = ['1', '2', ...tmp]
console.log(result) // ["1", "2", "a", "b", "c"]
result = ['1', ...tmp, '2']
console.log(result) // 你猜猜
//ex2
let tmp = 'abc'
let result = tmp.split("")//before ES6
console.log(result) // ["a", "b", "c"]
//使用 ES6 Spread Operator
result = [...tmp]
console.log(result) // ["a", "b", "c"]
//ex3
function sum(a, b, c) {
return a + b + c;
}
var args = [1, 2];
console.log(sum(...args, 3)); // 6
Destructure 解構賦值
- 解構賦值是用在”陣列指定陣列”或是”物件指定物件”的情況
- 這個時候會根據陣列原本的結構,以類似”鏡子”對映樣式(pattern)來進行賦值
//ex1
const [x, y, z] = [1, 2, 3]
console.log(x) // 1
//ex2
const [x, y, ...z] = [1]
console.log(x) // 1
console.log(y) // ??
console.log(z) // ??
//ex3
let nested = [1, [2, 3], 4]
let [a, [b], d] = nested
console.log(a) // 1
console.log(b) // 2
console.log(d) // 4
//ex4
const [ d, e = 1 ] = [ 6 ]
console.log(d)
//ex5
let point = {
x: 1,
y: 2
}
let { x: a, y: b } = point
console.log(a) // 1
console.log(b) // 2
//ex6
let point = {
x: 1
}
let { x: a, y: b } = point
console.log(a) // 1
console.log(b) // 你猜猜
Ref
http://www.infoq.com/cn/articles/es6-in-depth-rest-parameters-and-defaults
https://eyesofkids.gitbooks.io/javascript-start-from-es6/content/part3/object.html
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments
https://medium.com/ecmascript-2015/default-rest-spread-f3ab0d2e0a5e
https://medium.com/ecmascript-2015/es6-destructuring-13ca399f993a
https://eyesofkids.gitbooks.io/javascript-start-from-es6/content/part4/rest_spread.html
Author Hello Jay
LastMod 2018-05-21