linche0859
8/2/2019 - 9:50 AM

繼承機制

封裝

function Dog(name) {
  this.name = name
}
// 共用的屬性或方法
Dog.prototype.species = '貓科'

let dog1 = new Dog('dog1')
let dog2 = new Dog('dog2')

console.log(dog1.species)
console.log(dog2.species)
// 是否指向同一個記憶體位置
console.log(dog1.species === dog2.species) // true
// 判斷constructor和instance的關係
console.log('isPrototypeOf', Dog.prototype.isPrototypeOf(dog1)) // true
// 判斷某一個屬性是不是本地屬性
console.log('hasOwnProperty', dog1.hasOwnProperty('species')) // false
// 遍遞所有的property
for (let prop in dog1) {
  // prop是string
  console.log(`dog1[${prop}]=`, dog1[prop])
}

建構函數的繼承

重點 : 如果使用建構函數,__prop__(prototype)中一定會有一個constructor,且對應正確的建構函數

方法ㄧ - prototype 模式

function Animal() {
  this.species = '動物'
}

function Cat(name, color) {
  this.name = name
  this.color = color
}
// 指向並繼承Animal instance
Cat.prototype = new Animal()
// ㄧ定要加上這段,不然建構子會找不到建構函數
Cat.prototype.constructor = Cat

let cat1 = new Cat('大毛', '黄色')

// 結果
// Cat {name: "大毛", color: "黄色"}
//  color: "黄色"
//  name: "大毛"
//  __proto__: Animal
//    constructor: ƒ Cat(name,color)
//    species: "动物"
//    __proto__:
//      constructor: ƒ Animal()
//      __proto__: Object

方法二 - 直接繼承 prototype

function Animal() {}
Animal.prototype.species = '动物'
// 重要 : 現在的Animal prop和Cat prop都指向記憶體的同一個位置
// 如: Cat.prototype.species = '貓科',Animal.prototype.species也被改為'貓科'
Cat.prototype = Animal.prototype
// Animal的constructor也被指向Cat的Constructor
Cat.prototype.constructor = Cat

方法三 - 利用空的建構函數為中介,並封裝為一個函數

function Animal() {}
Animal.prototype.species = '动物'

function extend(Child, Parent) {
  let F = function() {}
  F.prototype = Parent.prototype
  Child.prototype = new F()
  Child.prototype.constructor = Child
}
extend.call(null, Cat, Animal)

方法四 - 拷貝繼承

把父類別的所有屬性和方法,拷貝至子類別

function Animal() {}
Animal.prototype.species = '动物'

function extend2(Child, Parent) {
  var p = Parent.prototype
  var c = Child.prototype
  for (var i in p) {
    c[i] = p[i]
  }
}

extend.call(null, Cat, Animal)
var cat1 = new Cat('大毛', '黄色')
console.log(cat1.species) // 動物

非建構函数的繼承 - 物件的繼承

淺拷貝

把父物件的属性,全部拷貝给子物件

function extendCopy(p) {
  var c = {}

  for (var i in p) {
    c[i] = p[i]
  }
  return c
}

var Doctor = extendCopy(Chinese)

// 但改Doctor會去影響Chinese

深拷貝

和另一物件不共用記憶體

方法ㄧ
var Chinese = {
  nation: '中国'
}

var Doctor = {
  career: '医生'
}

function object(o) {
  function F() {}

  F.prototype = o

  return new F()
}

var Doctor = object(Chinese)

// 結果
// F {}
//   __proto__:
//     nation: "中国"
//       __proto__: Object
方法二
var Chinese = {
  nation: '中国'
}

var Doctor = {
  career: '医生'
}

/**
 * 深拷貝
 * @param {Object} parent 父物件
 * @param {Object} child 子物件
 * @returns
 */
function deepCopy(parent, child) {
  var parent = parent || {}
  // 一開始 i為key name
  for (var i in parent) {
    // typeof string = string,typeof array = object
    if (typeof parent[i] === 'object') {
      child[i] = parent[i].constructor === Array ? [] : {}

      // 進去後的parent為array或object,child為產生新物件的屬性
      // 最後把結果丟回給child[property name]
      deepCopy(parent[i], child[i])
    } else {
      child[i] = parent[i]
    }
  }
  return child
}

Chinese.birthPlaces = ['北京', '上海', '香港']
var Doctor = deepCopy(Chinese)

// Doctor.birthPlaces.push('厦门')不會去影響Chinese.birthPlaces