JavaScript
是面對對象編程,可是它又跟其餘編程語言不同,不一樣之處是JavaScript
面對對象並非依賴於抽象類,而是經過原型鏈。在C++
和Java
使用new
命令時,都會調用"類"的構造函數。而在Javascript
語言中,new
命令後面跟的不是類,而是構造函數。可是,用構造函數生成實例對象,有一個缺點,那就是沒法共享屬性和方法。爲了解決這個問題,因而在構造函數設置了prototype
屬性,也就是原型。前端
用構造函數生成實例對象,有一個缺點,那就是沒法共享屬性和方法。從而形成對系統資源的浪費。具體例子以下:es6
function Cat(name, color) {
this.name = name;
this.color = color;
this.meow = function () {
console.log('喵喵');
};
}
var cat1 = new Cat('大毛', '白色');
var cat2 = new Cat('二毛', '黑色');
cat1.meow === cat2.meow
// false
複製代碼
從上面的代碼,能夠cat1
和cat2
的meow
方法是獨立的可是實現的功能都是同樣的,這既沒有必要,又浪費系統資源,由於全部meow
方法都是一樣的行爲,徹底應該共享。這個問題的解決方法,就是JavaScript
構造函數中添加prototype
屬性。編程
只有函數纔有prototype屬性,又稱爲顯式原型。
prototype屬性包含一個對象,全部實例對象須要共享的屬性和方法,都放在這個對象裏面;那些不須要共享的屬性和方法,就放在構造函數裏面。瀏覽器
JavaScript
規定,每一個函數都有一個prototype
屬性,指向一個對象。bash
function f() {}
typeof f.prototype // "object"
複製代碼
對於普通函數來講,該屬性基本無用。可是,對於構造函數來講,生成實例的時候,該屬性會自動成爲實例對象的原型。前端工程師
function Animal(name) {
this.name = name;
}
Animal.prototype.color = 'white';
var cat1 = new Animal('大毛');
var cat2 = new Animal('二毛');
cat1.color // 'white'
cat2.color // 'white'
複製代碼
上面代碼中,構造函數Animal
的prototype
屬性,就是實例對象cat1
和cat2
的原型對象。原型對象上添加一個color
屬性,結果,實例對象都共享了該屬性。編程語言
原型對象的屬性不是實例對象自身的屬性。只要修改原型對象,變更就馬上體如今全部實例對象上函數
Animal.prototype.color = 'yellow';
cat1.color // "yellow"
cat2.color // "yellow"
複製代碼
若是實例對象自身就有某個屬性或方法,它就不會再去原型對象尋找這個屬性或方法。學習
cat1.color = 'black';
cat1.color // 'black'
cat2.color // 'yellow'
Animal.prototype.color // 'yellow';
複製代碼
總結一下,原型對象的做用,就是定義全部實例對象共享的屬性和方法。這也是它被稱爲原型對象的緣由,而實例對象能夠視做從原型對象衍生出來的子對象。ui
注:Object.prototype中Object是一個全局對象,也是一個構造函數,以及其餘基本類型的全局對象也都是構造函數。
_proto_
是每一個對象都有的屬性,又稱爲隱式原型。可是,_proto_
不是一個規範屬性,只是部分瀏覽器實現了此屬性,對應的標準屬性是[[Peototype]]
。
大多狀況下,
_proto_
能夠理解爲‘構造函數的原型’,是實例和它的構造函數之間創建的連接,即_proto_ ===constructor.prototype
例子:
// 字面量方式
var a = {};
console.log(a.prototype); //undefined
console.log(a.__proto__); //Object {}
//構造器方式
var b = function(){}
console.log(b.prototype); //b {}
console.log(b.__proto__); //function() {}
// Object.create方式
var a1 = {}
var a2 = Object.create(a1)
console.log(b.prototype); //undefined
console.log(b.__proto__); //Object a1
複製代碼
本段摘自阮一峯ES6入門,具體解析能夠點擊查看。
一、_proto_
屬性沒有寫入ES6
的正文,而是寫入了附錄,
二、緣由是__proto__
先後的雙下劃線,說明它本質上是一個內部屬性,而不是一個正式的對外的API
,只是因爲瀏覽器普遍支持,才被加入了ES6
。
三、標準明確規定,只有瀏覽器必須部署這個屬性,其餘運行環境不必定須要部署,並且新的代碼最好認爲這個屬性是不存在的。
四、不管從語義的角度,仍是從兼容性的角度,都不要使用這個屬性,而是使用下面的Object.setPrototypeOf()
(寫操做)、Object.getPrototypeOf()
(讀操做)、Object.create()
(生成操做)代替。
每一個對象都會在其內部初始化一個屬性,就是prototype
(原型),當咱們訪問一個對象的屬性時,若是這個對象內部不存在這個屬性,那麼他就會去prototype
裏找這個屬性,這個prototype
又會有本身的prototype
,因而就這樣一直找下去,也就是咱們平時所說的原型鏈的概念。
全部對象的原型最終均可以上溯到Object.prototype
,Object.prototype
對象有沒有它的原型呢?回答是Object.prototype
的原型是null
。null
沒有任何屬性和方法,也沒有本身的原型。所以,原型鏈的盡頭就是null
。
一、字符串原型鏈終點
let test = 'hello'
// 字符串原型
let stringPrototype = Object.getPrototype(test)
// 字符串的原型是String對象
stringPrototype === String.Prototype // true
// String對象的原型是Object對象
Object.getPrototypeOf(stringPrototype) === Object.prototype // true
複製代碼
二、函數原理鏈的終點
let test = function () {}
let fnPrototype = Object.getPrototype(test)
fnPrototype === Function.prototype // true
Object.getPrototypeOf(Function.prototype) === Object.Prototype // true
複製代碼
一、屬性查找
當JS引擎查找對象的屬性時,先查找對象自己是否存在該屬性,若是不存在,會去原型鏈上查找,但不會查找本自身的prototype。
用一個例子來形象說明一下:
let test = 'hello'
// 字符串原型
let stringPrototype = Object.getPrototype(test)
// 字符串的原型是String對象
stringPrototype === String.Prototype // true
// String對象的原型是Object對象
Object.getPrototypeOf(stringPrototype) === Object.prototype // true
複製代碼
二、拒絕查找原型鏈
hasOwnPrototype
:指示對象自身屬性中是否具備指定的屬性。
語法:obj.hasOwnPrototype(prop)
。
參數prop
:查找的屬性。
返回值:Boolean
判斷是否存在。
let test = {'ABC': 'EDF' }
test.hasOwnPrototype('ABC') // true
test.hasOwnPrototype('toString') // false
複製代碼
該方法是在Object.prototype
上,全部對象均可以使用,且會忽略掉那些從原型鏈上繼承的屬性。
prototype
對象有一個constructor
屬性,默認指向prototype
對象所在的構造函數。
function P() {}
P.prototype.constructor === P // true
複製代碼
因爲constructor
屬性定義在prototype
對象上面,意味着能夠被全部實例對象繼承。
function P() {}
var p = new P();
p.constructor === P // true
p.constructor === P.prototype.constructor // true
p.hasOwnProperty('constructor') // false
複製代碼
上面代碼中,p
是構造函數P
的實例對象,可是p
自身沒有constructor
屬性,該屬性實際上是讀取原型鏈上面的P.prototype.constructor
屬性。
constructor
屬性的做用是,能夠得知某個實例對象,究竟是哪個構造函數產生的。
舉個小栗子:
function F() {};
var f = new F();
f.constructor === F // true
f.constructor === RegExp // false
複製代碼
另外一方面,有了constructor
屬性,就能夠從一個實例對象新建另外一個實例。
我再舉個小栗子:
function Constr() {}
var x = new Constr();
var y = new x.constructor();
y instanceof Constr // true
複製代碼
天下無難事,只怕有心人。原型和原型鏈算是前端工程師的開發基礎了,原型雖然不太好理解,可是我相信只要有心學習和實踐,也沒有想象中的那麼難。但願,這篇文章對我本身幫組的同時,童鞋們看完以後也有必定的收穫。