你不知道的Javascript(上卷)讀書筆記之四 ---- 提高、this

1. 提高數組

使用var聲明的變量聲明和函數的聲明(函數表達式不會)會被提高至所在函數做用域頂部 app

a. 從編譯器角度出發 函數

回憶一下, 中關於編譯器的內容,引擎會在解釋 JavaScript 代碼以前首先對其進行編譯。 this

編譯階段中的一部分工做就是找到全部的聲明, 並用合適的做用域將它們關聯起來。值得注意的是, 每一個做用域都會進行提高操做。 spa

另外,函數聲明會被提高至所在函數做用域頂部,可是函數表達式不會。code

foo(); //沒有錯誤
function foo() {
}

可是對象

foo(); // TypeError
bar(); // ReferenceError
var foo = function bar() {};

在這裏foo進行了變量聲明,進行了變量提高,可是對它進行調用會拋出TypeError錯誤,bar是一個函數表達式,不會進行提高 blog

b. 函數優先 ip

函數優先於變量進行提高 作用域

 

2. this

若是是在Java中,那麼this其實沒有什麼理解的難度,this指向類對象自己。可是在Javascript中,每個函數都會隱式地傳遞一個this,而且根據函數調用的不一樣狀況,this會有不一樣的指向。每一個函數的this都是在調用時被綁定的,this的指向徹底取決於函數的調用位置。

a.調用位置

在理解this以前,首先必需要理解什麼是調用位置,this徹底依靠調用位置(而不是聲明位置)來綁定this的指向,相同的函數被不一樣區域的調用會致使this指向 的不一樣。

b.綁定位置

this的綁定能夠理解爲如下四種調用規則:

        b.1 默認綁定 ---- 適用於獨立函數調用:

function foo(){ console.log(this.a); } var a = 2; foo();

在獨立函數調用時,函數調用應用了this的默認綁定,this指向全局做用域

在嚴格模式下,沒法使用默認綁定,this只會指向undefined;

        b.2 隱式綁定 ---- 調用時存在上下文對象

function foo() {
    console.log(this.a);
}
var obj = {a:2, foo: foo};
obj.foo();//調用位置

調用位置會使用obj上下文來調用函數,隱式綁定規則會把函數調用中的this 綁定到上下文對象,在隱式綁定規則下this會指向obj。

可是,隱式綁定存在着隱藏的風險:隱式丟失

隱式綁定的函數會丟失綁定的對象(或者能夠稱爲丟失了函數綁定的上下文對象),它會使用默認綁定原則,綁定到undefined或者全局做用域上,

var obj = {
    a: 2,
    foo: function (){
    console.log(this.a);
    }
};
var bar = obj.foo();
var a = 「globals」;
bar(); // 「globals」

雖然bar()引用的是obj中foo()函數,可是函數在調用的時候 並無傳入一個上下文對象,在這種狀況下調用,就跟調用普通的獨立函數沒有區別

        b.3 顯式綁定

存在一些狀況,咱們想要往一個函數中顯式地綁定this,咱們可使用call(..) 和apply(...)函數

function foo(){
    console.log(this.a);
}
var obj = {
    a:2
};
var bar = function(){
    foo.call(obj);
}
bar();

        b.4 new綁定

不一樣於傳統的面嚮對象語言,Js中的構造函數是特殊的函數,雖然都是使用 new 進行調用,可是在Js中使用new的機制和傳統面嚮對象語言徹底不一樣。

在使用new來調用構造函數時,會執行如下步驟:

First.建立一個全新的對象。

Second.這個對象會被執行[原型]鏈接

Third.新對象將會綁定到函數調用的this

Fourth.若是函數沒有返回其餘對象,那麼new 表達式中的函數將會自動返回 這個新對象

c.四種綁定方式的優先級

首先優先級最低的是默認綁定,當其餘綁定規則都不生效時使用默認綁定

//c.1 顯式綁定 > 隱式綁定
obj.foo.call(obj2) // 傳入的this綁定的對象是obj2
//c.2 new 綁定 > 隱式綁定
new obj.foo(); // this綁定的是新建立的對象
//c.2 new 綁定 > 顯式綁定
function foo(p1, p2) {
    this.val = p1 + p2;
}
 
var bar = foo.bind(null, 「p1」);
var baz = new bar(「p2」);
baz.val; // p1p2

總結一下就是: new > 顯式 > 隱式 > 默認

d. 綁定例外

例外狀況一:把null, undefined 做爲this的綁定對象傳入call/apply/bind時會使用默認綁定規則

在何時下會傳入null?

作法一:使用apply(..)來展開一個數組
function foo(a, b){
    console.log(「a:」 + a + 「, b:」 + b);
}
foo.apply(null, [2, 3]);
作法二:對參數進行」柯里化」(預先設置參數)
var bar = foo.bind(null, 2);
bar(3);

在ES6中,能夠用...操做符代替apply(..)來展開數組,好比foo(...[1,2])

例外狀況二:一個函數的間接引用,這個時候會使用默認綁定

function foo(){
    console.log(this.a);
}
var a = 2;
var obj = {a: 3, foo: foo};
var obj2 = {a: 4};
(obj2.foo = obj.foo)(); // 2

例外狀況三:ES6新出現的箭頭函數

ES6中新增長了一種沒法使用以前四種規則的特殊函數類型:箭頭函數

箭頭函數不使用this的四種標準規則,而是根據外層(函數或者全局)做用域來決定 this

function foo() {
    return () => {
        console.log(this.a);
    }
}
 
var obj1 = {a:2};
var obj2 = {a:3};
var bar = foo.call(obj1);
bar.call(obj2); // 2

foo內部的箭頭函數會捕獲調用foo()時候的this,一經綁定沒法修改。

實際上它們的效果就與ES6以前的這種寫法差很少:

function foo() {
    var self = this;
    return function(){
        console.log(self.a);    
    }
}
相關文章
相關標籤/搜索