什麼是JavaScript原型

JS 原型

轉載自【EC前端 - JavaScript原型

原型是JavaScript最重要的概念。同時也是初級開發者最忌憚的內容,緣由在於網上不多有關於它的合理描述。javascript

但事實上,原型很簡單,你能夠很輕鬆的掌握它的知識要點。html

什麼是原型

瞭解什麼是原型以前,咱們先看一個示例:前端

var obj = {};
obj.toString(); // "[object Object]"

上面的例子中,咱們聲明瞭一個空對象,並無爲它添加toString屬性方法,但這個方法卻能夠被成功調用並輸出。是否是以爲很神奇?java

緣由在於JavaScript的對象都有一個內置的[[Prototype]]私有屬性,這個屬性指向另外一個對象,咱們稱這個對象爲原對象的原型。當JS引擎訪問objtoString屬性時,首先會去obj對象查找,發現找不到,就沿着obj[prototype]屬性去他的原型上查找。數組

經過Chrome開發者工具,咱們能夠看到這個obj原型的真面目:函數

<div class="__diagram"> <img src="../__assets/prototype_01.jpg" alt="JavaScript空對象的原型" /> </div>工具

圖中咱們能夠看到,obj的原型對象已經定義了一個toString屬性。因此,空對象也可使用toString()這個屬性方法。學習

爲何須要原型

原型意義在於實現屬性的繼承this

想象一下:編寫一個JS腳本,建立1000個數組實例。若是在每一個數組實例中單獨定義數組的操做方法,不只代碼冗餘,還會內存資源極大的浪費。有了原型,咱們只須要把數組的操做方法定義在數組的原型上便可,實現了屬性的共享。spa

舉個例子:

咱們但願JS的數字類型能提供一個方法判斷當前數字是否爲奇數,沒有原型的狀況下,你只能這麼作:

var num = new Number(99);
num.isOdd = function(){return this % 100 !== 0};

num.isOdd(); // true

可是這樣是沒有意義的,由於isOdd()方法定義在num變量上面,其餘數字類型並不能使用它:

var num2 = 100;
num2.isOdd(); // num2.isOdd is not a function

有了原型概念之後,因爲全部數字類型都指向了同一個原型,咱們能夠把isOdd方法定義在這個原型上,這樣,全部數字類型就都能調用到這個方法了:

var num1 = 99, num2 = 100, num3 = 0;
Number.prototype.isOdd = function(){return this % 100 !== 0};

num1.isOdd(); // true
num2.isOdd(); // false
num3.isOdd(); // false
學習後面的內容,你將明白:Number.prototype指向數字類型的原型

原型鏈

原型並非一個特別的存在,它也只是一個普通的對象而已。

換句話說,原型也能夠擁有屬於它的原型。若是把對象的[[prototype]]屬性想象成鏈條,就造成了一條原型鏈

接下來,咱們經過一個示例來看下JS引擎是如何經過原型鏈查找屬性的:

如今建立三個對象,並經過Object.setPrototypeOf()方法將它們連結成一條原型鏈:

var son = {a: 1, b: 2},
    parent = {b: 3, c: 4},
    ancestor = {d: 5};

Object.setPrototypeOf(son, parent);
Object.setPrototypeOf(parent, ancestor);

son.a // 1
son.b // 2
son.c // 4
son.d // 5

這三個對象造成將造成一條原型鏈,JS引擎將從左往右有序地查找目標屬性:

<div class="__diagram"> <img src="../__assets/prototype_02.jpg" alt="JS在原型鏈中查找屬性" /> </div>

如何設置和修改對象的原型

JavaScript分別經過 Object.setPrototypeOf()Object.getPrototypeOf() 兩個方法來設置和獲取對象的原型。

var parent = {type: 'parent'}, son = {type: 'son'};
Object.setPrototypeOf(son, parent);
Object.getPrototypeOf(son) === parent // true

內置對象實例的原型

JavaScript提供了一些內置對象(構造函數),好比Object, String, Array, Boolean等等,它們提供了prototype屬性,指向實例的原型。所以,能夠簡單地經過instance.constructor.prototype來獲取

var obj = {}, str = '', arr = [], bl = true;

Object.getPrototypeOf(obj) === obj.constructor.prototype // true
Object.getPrototypeOf(str) === str.constructor.prototype // true
Object.getPrototypeOf(arr) === arr.constructor.prototype // true
Object.getPrototypeOf(bl) === bl.constructor.prototype // true
若是你不理解constructor這個屬性,能夠閱讀構造函數一節。

經過instance.constructor.prototype這種方式獲取原型的方式並非絕對可靠的。由於實例的constructor屬性是可改變的(mutable)。一旦屬性,instance.constructor.prototype便沒法正確指向實例的原型。

var obj = new Object();
obj.constructor = Array;
Object.getPrototypeOf(obj) === obj.constructor.prototype // false

上面的例子中,咱們隨意修改了obj的constructor屬性,而後obj.constructor.prototype便再也不指向obj的原型了。

另外,對於自定義構造函數而言,其constructor也是可變的(內置構造函數的constructor被配置爲不可改變)

綜合來講,咱們推薦使用 Object.getPrototypeOf() 方法獲取實例原型。

相關文章
相關標籤/搜索