js 面向對象 oop、及繼承方式實現、對比

  • 要理解js面向對象,首先得了解原型,對象,構造函數之間的關係,如下面示例爲例
function Person(name) { //構造函數
    this.name = name;
}

Person.prototype.printName = function () { //原型對象
    alert(this.name);
}

var person1 = new Person('Byron'); //實例化對象
console.log(person1.__proto__); //Person
console.log(person1.constructor); //本身試試看會是什麼吧
console.log(Person.prototype); //指向原型對象Person
var person2 = new Person('Frank');
複製代碼
  • 構造函數:用來在建立對象時初始化對象。特色:構造函數名通常爲大寫字母開頭;與new運算符一塊兒使用來實例化對象。
  • 原型:構造函數在建立的過程當中,系統自動建立出來與構造函數相關聯的一個空的對象。能夠由構造函數.prototype來訪問到。
    image
  • 原型:構造函數在建立的過程當中,系統自動建立出來與構造函數相關聯的一個空的對象。能夠由構造函數.prototype來訪問到。
  • 在實例化對象p的過程當中,系統就自動建立出了構造函數的原型,即Person.prototype.
  • 注意:每一個對象的__proto__屬性指向自身構造函數的prototype;
  • constructor屬性是原型對象的屬性,指向這個原型對象所對應的構造函數。
  • 在實例化對象p的過程當中,系統就自動建立出了構造函數的原型,即Person.prototype(每一個對象的__proto__屬性指向自身構造函數的prototype)
  • constructor屬性是原型對象的屬性,指向這個原型對象所對應的構造函數
  • 完整原型鏈圖片示意圖
    完整原型鏈圖片示意圖

new 關鍵字

// 構造函數表示方法1
// 不須要new生成實例
function Animal (name, energy) {
  let animal = Object.create(Animal.prototype)
  animal.name = name
  animal.energy = energy
  return animal
}

// 構造函數表示方法2
// 須要new生成實例
function Animal (name, energy) {
  // const this = Object.create(Animal.prototype)
  this.name = name
  this.energy = energy
  // return this
}
const leo = new Animal('Leo', 7)
const snoop = new Animal('Snoop', 10)

複製代碼
  • 一般咱們使用構造函數2的方法來寫一個構造函數,當new 一個關鍵字時,let this = Object.create(Animal.prototype)、 return this這兩句代碼,會在引擎會隱式調用,而且建立的對象稱爲thisjavascript

  • 調用構造函數,忘記new實例防錯html

// 警告
function Animal (name, energy) {
  if (this instanceof Animal === false) {
    console.warn('Forgot to call Animal with the new keyword')
  }
  this.name = name
  this.energy = energy
}

// 生成實例
function Animal (name, energy) {
  if (this instanceof Animal === false) {
    return new Animal(name, energy)
  }
  this.name = name
  this.energy = energy
}
複製代碼

for ... in

function Animal (name, energy) {
  this.name = name
  this.energy = energy
}
Animal.prototype.eat = function (amount) {
  console.log(`${this.name} is eating.`)
  this.energy += amount
}
Animal.prototype.sleep = function (length) {
  console.log(`${this.name} is sleeping.`)
  this.energy += length
}
Animal.prototype.play = function (length) {
  console.log(`${this.name} is playing.`)
  this.energy -= length
}
const leo = new Animal('Leo', 7)
for(let key in leo) {
  console.log(`Key: ${key}. Value: ${leo[key]}`)
}
複製代碼
  • 遍歷循環:循環遍歷對象自己以及它委託給的原型的全部可枚舉屬性。由於默認狀況下,您添加到函數原型的任何屬性都是可枚舉的,咱們不只會看到名稱和能量,還會看到原型上的全部方法 - 吃,睡,玩等方法。
  • 修改只輸出原型方法
for(let key in leo) {
  if (leo.hasOwnProperty(key)) {
    console.log(`Key: ${key}. Value: ${leo[key]}`)
  }
}

複製代碼

hasOwnProperty

function Animal (name, energy) {
  this.name = name
  this.energy = energy
}
Animal.prototype.eat = function (amount) {
  console.log(`${this.name} is eating.`)
  this.energy += amount
}

Animal.prototype.sleep = function (length) {
  console.log(`${this.name} is sleeping.`)
  this.energy += length
}
Animal.prototype.play = function (length) {
  console.log(`${this.name} is playing.`)
  this.energy -= length
}
const leo = new Animal('Leo', 7)
leo.hasOwnProperty('name') // true
leo.hasOwnProperty('energy') // true
leo.hasOwnProperty('eat') // false
leo.hasOwnProperty('sleep') // false
leo.hasOwnProperty('play') // false
複製代碼

instanceof

function Animal (name, energy) {
  this.name = name
  this.energy = energy
}
function User () {}
const leo = new Animal('Leo', 7)
leo instanceof Animal // true
leo instanceof User // false
複製代碼

從新建立Object.create

  1. 它接受一個對象的參數。
Object.create = function (objToDelegateTo) { }
複製代碼
  1. 它建立一個對象,該對象在失敗的查找中委託給參數對象。
Object.create = function (objToDelegateTo) {
  function Fn(){}
  Fn.prototype = objToDelegateTo
  return new Fn()
}
複製代碼
  • 在Object.create實現的主體內部,咱們將建立一個空函數。而後,咱們將該空函數的原型設置爲等於參數對象。而後,爲了建立一個新對象,咱們將使用new關鍵字調用咱們的空函數。若是咱們返回新建立的對象
  • 注意對於Array、Object等引用類型的數據子實例會共用
var obj = {a: 1, b: ['red', 'blue']}
var objInstance1 = Object.create(obj)
var objInstance2 = Object.create(obj)
objInstance1.b.push('green')
objInstance2.b // ['red', 'blue', 'green']

// 對於基本類型會從新賦值
如:objInstance1.a = 3
console.log(objInstance1) // {a: 3}
複製代碼
  1. 它返回新建立的對象。

繼承實現的方式

  • prototype模式實現繼承
function Animal() {
    this.species = "動物";
}

function Cat(name, color) {
    // this 指向Cat
    Animal.apply(this, arguments);
    this.name = name;
    this.color = color;
}
// 將Cat的prototype對象指向一個Animal的實例,至關於徹底刪除了prototype 對象原先的值,而後賦予一個新值
Cat.prototype = new Animal();
// 任何一個prototype對象都有一個constructor屬性,指向它的構造函數。
// 若是沒有"Cat.prototype = new Animal();"這一行,Cat.prototype.constructor是指向Cat的;
 // 加了這一行之後,Cat.prototype.constructor指向Animal。 
// 顯然會致使繼承鏈的紊亂(cat1明明是用構造函數Cat生成的),
// 所以咱們必須手動糾正,將Cat.prototype對象的constructor值改成Cat。
Cat.prototype.constructor = Cat;
var cat1 = new Cat("大毛", "黃色");
// console.log(cat1.species)
console.log(cat1)
複製代碼
  • 利用空對象做爲中介(寄生組合式繼承)
function extend(Child, Parent) {

    var F = function(){};
  F.prototype = Parent.prototype;

  Child.prototype = new F();

  Child.prototype.constructor = Child;

  Child.uber = Parent.prototype;
}
  
extend(Cat,Animal);

var cat1 = new Cat("大毛","黃色");

alert(cat1.species); // 動物
複製代碼
  • 這種實現繼承的方式與上一種比較效率要高些,避免了new Animal兩次,其添加沒必要要的屬性。

原型鏈

  • 原型鏈:每個對象都有本身的原型對象,原型對象自己也是對象,原型對象也有本身的原型對象,這樣就造成了一個鏈式結構,叫作原型鏈。
  • 以Person舉例:這個例子中的p對象的原型鏈結構圖以下:p對象----->Person.prototype------->Object.prototype--------->null
  • 對這個實例化對象而言,訪問對象的屬性,是首先在對象自己去找,若是沒有,就會去他的原型對象中找,一直找到原型鏈的終點;若是是修改對象的屬性,若是這個實例化對象中有這個屬性,就修改,沒有這個屬性就添加這個屬性。

__proto__屬性和prototype屬性的區別

  • prototype是function對象中專有的屬性。
  • __proto__是普通對象的隱式屬性,在new的時候,會指向prototype所指的對象;
  • __ptoto__其實是某個實體對象的屬性,而prototype則是屬於構造函數的屬性。__ptoto__只能在學習或調試的環境下使用。

參考連接

相關文章
相關標籤/搜索