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

  1. 函式呼叫: 在一般情況下的函式呼叫,this通常都指向global物件。這也是預設情況。
  2. 建構式(constructor)呼叫: 透過new運算符建立物件實體,等於呼叫類型的建構式,this會指向新建立的物件實例
  3. 物件中的方法呼叫: this指向呼叫這個方法的物件實體

Constructor 建構式

  1. 類別(class)的定義時,物件的屬性都只能在建構式中定義,這與用物件字面的定義方式不同
  2. 如果物件在初始化時不需要任何語句,那麼就不要寫出這個建構式
  3. 建構式或物件方法的多形(polymorphism)或覆蓋(Overriding),在JavaScript中沒有這種特性
  4. 建構式是會被限制只能有一個,而在物件中的方法(函式)也沒這個特性
  5. 如果你需要定義不同的建構式在物件中,因應不同的物件實體的情況,只能用函式的不定傳入參數方式,或是加上傳入參數的預設值來想辦法改寫

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 繼承

  1. extends關鍵字可以作類別的繼承
  2. 繼承的子類別中的建構式,super()需要放在第一行
  3. super也可以用於指向上層父母類別,呼叫其中的方法或存取屬性
  4. 沒有多重繼承,可以用合併多個物件產生新物件(合成 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()

  1. Object.assign()是ES6中的新方法,在ES6前都是用迴圈語句,或其他的方式來進行物件拷貝的工作
  2. 除了拷貝之外,也可以作物件的合併,合併時成員有重覆名稱以愈後面的物件中的成員為主進行覆蓋
//物件拷貝
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 解構賦值

  1. 解構賦值是用在”陣列指定陣列”或是”物件指定物件”的情況
  2. 這個時候會根據陣列原本的結構,以類似”鏡子”對映樣式(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

  1. http://www.infoq.com/cn/articles/es6-in-depth-rest-parameters-and-defaults

  2. https://eyesofkids.gitbooks.io/javascript-start-from-es6/content/part3/object.html

  3. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments

  4. https://medium.com/ecmascript-2015/default-rest-spread-f3ab0d2e0a5e

  5. https://medium.com/ecmascript-2015/es6-destructuring-13ca399f993a

  6. https://eyesofkids.gitbooks.io/javascript-start-from-es6/content/part4/rest_spread.html