JavaScript -- 繼承與原型鏈

JavaScript對象有一個指向一個原型對象的鏈,當試圖訪問一個對象的屬性的時候,他不單單會在該對象上面搜尋,還會搜尋該對象的原型,以及對象的原型的原型,依次層層搜索,直到找到名字匹配的屬性或者到達原型鏈的末端javascript

// 讓咱們假設咱們有一個對象 o, 其有本身的屬性 a 和 b:
// {a: 1, b: 2}
// o 的 [[Prototype]] 有屬性 b 和 c:
// {b: 3, c: 4}
// 最後, o.[[Prototype]].[[Prototype]] 是 null.
// 這就是原型鏈的末尾,即 null,
// 根據定義,null 沒有[[Prototype]].
// 綜上,整個原型鏈以下: 
// {a:1, b:2} ---> {b:3, c:4} ---> null

console.log(o.a); // 1
// a是o的自身屬性嗎?是的,該屬性的值爲1

console.log(o.b); // 2
// b是o的自身屬性嗎?是的,該屬性的值爲2
// 原型上也有一個'b'屬性,可是它不會被訪問到.這種狀況稱爲"屬性遮蔽 (property shadowing)"

console.log(o.c); // 4
// c是o的自身屬性嗎?不是,那看看原型上有沒有
// c是o.[[Prototype]]的屬性嗎?是的,該屬性的值爲4

console.log(o.d); // undefined
// d是o的自身屬性嗎?不是,那看看原型上有沒有
// d是o.[[Prototype]]的屬性嗎?不是,那看看它的原型上有沒有
// o.[[Prototype]].[[Prototype]] 爲 null,中止搜索
// 沒有d屬性,返回undefined

繼承方法

當繼承的函數被調用時,this 指向的是當前繼承的對象,而不是繼承的函數所在的原型對象。java

var o = {
  a: 2,
  m: function(){
    return this.a + 1;
  }
};

console.log(o.m()); // 3
// 當調用 o.m 時,'this'指向了o.

var p = Object.create(o);
// p是一個繼承自 o 的對象

p.a = 4; // 建立 p 的自身屬性 a
console.log(p.m()); // 5
// 調用 p.m 時, 'this'指向 p. 
// 又由於 p 繼承 o 的 m 函數
// 此時的'this.a' 即 p.a,即 p 的自身屬性 'a'

使用不一樣的方法來建立對象和生成原型鏈

語法結構建立的對象

var o = {a: 1};

// o 這個對象繼承了Object.prototype上面的全部屬性
// o 自身沒有名爲 hasOwnProperty 的屬性
// hasOwnProperty 是 Object.prototype 的屬性
// 所以 o 繼承了 Object.prototype 的 hasOwnProperty
// Object.prototype 的原型爲 null
// 原型鏈以下:
// o ---> Object.prototype ---> null

var a = ["yo", "whadup", "?"];

// 數組都繼承於 Array.prototype 
// (Array.prototype 中包含 indexOf, forEach等方法)
// 原型鏈以下:
// a ---> Array.prototype ---> Object.prototype ---> null

function f(){
  return 2;
}

// 函數都繼承於Function.prototype
// (Function.prototype 中包含 call, bind等方法)
// 原型鏈以下:
// f ---> Function.prototype ---> Object.prototype ---> null

構造器建立的對象

在 JavaScript 中,構造器其實就是一個普通的函數。當使用 new 操做符 來做用這個函數時,它就能夠被稱爲構造方法(構造函數)。數組

function Graph() {
  this.vertices = [];
  this.edges = [];
}

Graph.prototype = {
  addVertex: function(v){
    this.vertices.push(v);
  }
};

var g = new Graph();
// g是生成的對象,他的自身屬性有'vertices'和'edges'.
// 在g被實例化時,g.[[Prototype]]指向了Graph.prototype.

Object.create 建立的對象

ECMAScript 5 中引入了一個新方法:Object.create()。能夠調用這個方法來建立一個新對象。新對象的原型就是調用 create 方法時傳入的第一個參數:ide

var a = {a: 1}; 
// a ---> Object.prototype ---> null

var b = Object.create(a);
// b ---> a ---> Object.prototype ---> null
console.log(b.a); // 1 (繼承而來)

var c = Object.create(b);
// c ---> b ---> a ---> Object.prototype ---> null

var d = Object.create(null);
// d ---> null
console.log(d.hasOwnProperty); // undefined, 由於d沒有繼承Object.prototype

class 關鍵字建立的對象

ECMAScript6 引入了一套新的關鍵字用來實現 class。使用基於類語言的開發人員會對這些結構感到熟悉,但它們是不一樣的。JavaScript 仍然基於原型。這些新的關鍵字包括 class, constructorstaticextendssuper函數

"use strict";

class Polygon {
  constructor(height, width) {
    this.height = height;
    this.width = width;
    console.log(height)    //2
  }
}

class Square extends Polygon {
  constructor(sideLength) {
    super(sideLength, sideLength);
  }
  get area() {
    return this.height * this.width;
  }
  set sideLength(newLength) {
    this.height = newLength;
    this.width = newLength;
  }
}

var square = new Square(2);

性能

function Graph() {
  this.vertices = [];
  this.edges = [];
}

Graph.prototype = {
  addVertex: function(v){
    this.vertices.push(v);
  }
};

var g = new Graph();
console.log(g.hasOwnProperty('vertices'));
// true

console.log(g.hasOwnProperty('nope'));
// false

console.log(g.hasOwnProperty('addVertex'));
// false

console.log(g.__proto__.hasOwnProperty('addVertex'));
// true

hasOwnProperty 是 JavaScript 中惟一處理屬性而且不會遍歷原型鏈的方法。性能

所以,當你執行:this

var o = new Foo();

JavaScript 實際上執行的是(或者大體這樣):prototype

var o = new Object();
o._proto_ = Foo.prototype;
Foo.call(0)
o.someProp;

它檢查o是否具備someProp屬性。code

若是沒有,它會查找 Object.getPrototypeOf(o).someProp對象

若是仍舊沒有,它會繼續查找 Object.getPrototypeOf(Object.getPrototypeOf(o)).someProp

ps:

Object.getPrototypeOf() 方法返回指定對象的原型(內部[[Prototype]]屬性的值)。

var proto = {};
var obj = Object.create(proto);
var a= Object.getPrototypeOf(obj)
console.log(a); {}

若是以爲還不錯,請訪問MDN

相關文章
相關標籤/搜索