原型與閉包是JavaScript與其餘面嚮對象語言差別最大的兩個點,深刻學習js須要咱們理解原型es6
在js中咱們常用typeof運算符來判斷一個變量的類型。 常見的類型能夠分爲值類型與引用類型vim
值類型有:undefined、number、string、boolean數組
引用類型有:obejct、function(數組、對象、空值都是object)閉包
對象是什麼??對象就是一些屬性值的集合記住這一句話就能夠很好地幫助你判斷這是否是一個對象app
咱們都知道,對於引用類型的判斷,咱們經常使用instanceof運算符。咱們隊一個函數使用這個判斷符時,得出的結果是object。那麼函數與對象之間究竟有着什麼樣的區別與聯繫呢?函數
在es6以前,若是咱們想要定義一個對象,那麼咱們經常使用的建立構造函數的方式就是。學習
function Fn() {
this.name = '王福朋';
this.year = 1988;
}
var fn1 = new Fn();
複製代碼
很顯然,咱們創造了一個fn1對象,有名字和出生年份兩個屬性。ui
這裏有一個重要的結論就是*一切的對象都是經過函數來創造的(構造函數)*不信的話咱們能夠對Object和Array作typeof運算,獲得的結果就是function。this
本小節最後,咱們來理一理思路,全部的對象都是由函數構造的,而一個函數又是一個對象。那麼問題來了。。。hahahahspa
![函數與原型的關係](http://i2.bvimg.com/668731/279cd18f4be682e0.png)
如圖所示,每個函數都有它的原型(prototype),原型是是一個對象,裏面有着許許多多的屬性值。咱們在使用函數建立對象時,函數中的prototype就會建立一個引用,這個引用就是__proto__這是每個對象的隱藏屬性,指向構造函數的原型。即fn.proto===Fn.prototype
隱式原型指的就時對象的隱藏屬性_proto__
每一個函數function都有一個prototype,即原型。這裏再加一句話——每一個對象都有一個__proto__,可成爲隱式原型。
咱們都知道函數instanceof Obejct的值爲true。因此它的本質就是Object的prototype。那麼Obeject的_proto_指向哪裏呢?答案是null。這是一個特例。
那麼函數是一個對象,對象就有隱式原型,它的隱式原型指向的就是創造這個對象的函數Function的prototype。因此咱們能夠獲得一個很繞的原型鏈圖。
![原型鏈圖](http://i1.bvimg.com/668731/76948b944cede7ac.png)
最後,咱們都知道prototype屬性是一個對象,那麼Function。prototype這個對象的_proto_指向哪裏呢?固然只指向Object.prototype啦!!
首先咱們須要知道,所謂繼承,繼承的是什麼。
function Foo () {}
var f1 = new Foo()
f1.a = 100
Foo.prototype.a = 1
Foo.prototype.b = 100
console.log(f1.a) // 100
console.log(f1.b) //100
複製代碼
經過以前的學習,我門已經知道了f1具備__proto__屬性,這個屬性指向構造對象的函數Foo的prototype上。那麼當咱們訪問f1的屬性值時,js會先從對象的基本屬性中查找,若是沒有就從__proto__這條鏈一直往上找。這種繼承的屬性尋找機制,就是原型鏈。
咱們可使用hasOwnProperty()方法來查詢某個屬性值是否屬於這個對象的基本屬性,若是是從原型鏈上繼承過來的就會返回false。
先看三個例子
// Example 1
console.log(a) //Eroor
//-----------------------------
console.log(a) //undefined
var a
//-----------------------------
console.log(a) //undefined
var a = 10
//-----------------------------
// Example 2
console.log(this) //任何狀況下都有值,可是取值會變
//-----------------------------
// Example3
console.log(f1) // function f1 () {}
function f1 () {}
console.log(f2) // undefined
var f2 = function () {}
複製代碼
咱們來看上面三個例子。
第一個例子的一二個,我想你們都知道爲何,一個是沒有聲明,第二個是存在聲明提高,可是沒有賦值,第三個的緣由是,console的時候尚未執行賦值。
第二個例子,後面會詳細說。
第三個例子,首先咱們須要知道第一個是函數聲明,第二個是函數表達式,函數聲明一樣的具備提高,因此有輸出,而表達式理由同例1的第三個。
經過上面的例子咱們發下,在不一樣的語句條件下,訪問某些變量,這些變量的值是會根據語句條件的不一樣而不一樣的。咱們就管這種語句條件叫作上下文環境。
執行上下文環境下一個通俗的定義——在執行代碼以前,把將要用到的全部的變量都事先拿出來,有的直接賦值了,有的先用undefined佔個空。
在函數中this到底取何值,是在函數真正被調用執行的時候肯定的,函數定義的時候肯定不了。由於this的取值是執行上下文環境的一部分,每次調用函數,都會產生一個新的執行上下文環境。
不只僅是構造函數的prototype,即使是在整個原型鏈中,this表明的也都是當前對象的值
function Foo () {
this.a = 1
console.log(this)
}
var f1 = new Foo() // Foo {a:1}
Foo() //Window
複製代碼
當函數做爲構造函數使用時,this指向的就是它即將new出來的對象,即f1,可是若是直接使用指向的就是Window
一個對象調用它屬性中的某個方法時,方法中的this指向的就是這個對象
當一個函數被call或者apply調用時,函數中this的值就是傳入的對象
在全局環境與普通函數調用時,this的值都是Window。
var obj = {
fn: function () {
function f () {
console.log(this) //Window
}
}
}
複製代碼
上面這是一種特殊狀況,咱們都知道,fn做爲obj的方法,它的this應該是obj。可是fn中的f是一個普通函數,普通函數的this就是Window。