1. 程式人生 > >JavaScript中this關鍵字改變指向的三種方法(apply、call、bind)

JavaScript中this關鍵字改變指向的三種方法(apply、call、bind)

首先,瞭解一下this關鍵字。this關鍵字就涉及到函式呼叫的內容。函式的幾種呼叫方式:

  1. 普通函式呼叫
  2. 作為方法來呼叫
  3. 作為建構函式來呼叫
  4. 使用apply/call方法來呼叫
  5. Function.prototype.bind方法
  6. ES6箭頭函式

但是不管函式是按哪種方法來呼叫的,都需要記住一點:誰呼叫這個函式或方法,this關鍵字就指向誰

普通函式呼叫

var age = 18;
function person(){
    this.name = "Tony";
    console.log(this);      //window
    console.log(this.name); //Tony
    console.log(this.age);  //18
}
person();

程式碼中定義一個普通函式person,在呼叫時實際上person是作為全域性物件window的一個方法來進行呼叫的,即window.person();
因此是window物件呼叫了person方法,那麼person函式當中的this即指window,同時window還擁有了另外一個屬性name,值為“Tony”。

定義一個全域性變數age,它相當於是window的一個屬性。在呼叫person函式時它的this指向window,所以在函式內部可以訪問到age變數,這也解釋了函式內部可訪問全域性變數。

作為方法來呼叫

var person = {
    name : "Tony",
    showName:function(){
        console.log(this.name);
    }
}
person.showName();

var name = "Tom";
var showname = person.showName;
showname();

定義一個物件person包含一個showName的方法,在物件person內呼叫方法返回的是Tony,顯然this指向了物件person。

再定義一個全域性變數name,將person.showName方法賦值給變數showname,因為在全域性定義的嘛,所以showname也相當於window的一個屬性,此時呼叫showname它的this是指向window的,所以返回的值就是全域性變數“Tom”。

var personA = {
    name : "Tony",
    showName:function(){
        console.log(this.name);
    }
}
var personB = {
    name : "Tom",
    sayName:personA.showName
}
personB.sayName();    //Tom

personB物件中的方法呼叫personA中的方法,雖然showName是personA中定義的,但呼叫了personB,那this就指向了personB。

作為建構函式來呼叫

function Person(name){
    this.name = name;
}
var personA = new Person("Tony");
console.log(personA.name);

建構函式new出來的例項,this指向這個例項物件。

下面就來了解一下this指向改變的三種方法。

在JavaScript中,callapplybindFunction物件自帶的三個方法,這三個方法的主要作用是改變函式中的this指向。所以可知它們的共同點就是都用來改變函式的this物件的指向的;還有就是三者第一個引數都是this要指向的物件,也就是想指定的上下文(函式的每次呼叫都會擁有一個特殊值——本次呼叫的上下文(context)——這就是this關鍵字的值。),然後再利用後續引數傳參。而它們的不同也就是傳參的不同,bind 是返回對應函式,便於稍後呼叫;apply 、call 則是立即呼叫 。

apply()方法 

它接收兩個引數,一個是函式執行的作用域(this),另一個是引數陣列。

語法:

apply([thisObj [,argArray] ]);

定義:應用某一物件的一個方法,用另一個物件替換當前物件

說明:如果argArray不是一個有效陣列或不是arguments物件,那麼將導致一個 TypeError,如果沒有提供argArray和thisObj任何一個引數,那麼Global物件將用作thisObj。

function Person(name){
    this.name = name;
    this.showName = function(){
        console.log(this.name);
    }
}
function Student(name){
    Person.apply(this,arguments);
}
var stu = new Student("Tony");
stu.showName();

call()方法 

它第一個引數和apply()方法的一樣,但是傳遞給函式的引數必須列舉出來。

語法:

call([thisObject[,arg1 [,arg2 [,...,argn]]]]);

定義:呼叫一個物件的一個方法,以另一個物件替換當前物件。

說明: call方法可以用來代替另一個物件呼叫一個方法,call方法可以將一個函式的物件上下文從初始的上下文改變為thisObj指定的新物件。

thisObj取值的情況:

  1. 不傳,或者傳null,undefined, 函式中的this指向window物件
  2. 傳遞另一個函式的函式名,函式中的this指向這個函式的引用
  3. 傳遞字串、數值或布林型別等基礎型別,函式中的this指向其對應的包裝物件,如 String、Number、Boolean
  4. 傳遞一個物件,函式中的this指向這個物件

call方法的一些常見例子:

1.

function add(a,b){
    console.log(a+b);
}
function subtraction(a,b){
    console.log(a-b);
}
add.call(subtraction,3,1);    //4

定義一個加法函式、加法函式,最後的呼叫語句意思就是用加法替代了減法,在減法函式中通過call方法改變了this的指向,已經不再計算a-b,而是作用加法中的a+b。

2.

function Student(){
    this.name = "Tom";
    this.showName = function(){
        console.log(this.name);
    }
}
function Teacher(){
    this.name = "Tony";
}
var stu = new Student();
var tea = new Teacher();
stu.showName.call(tea);

定義一個學生函式,它有屬性name和方法showName,而老師函式只有name屬性,最後一個呼叫語句的意思就是把學生的方法用到了老師函式中執行,即便老師是沒有這個方法的。

3.

function Parent(name){
    this.name = name;
    this.showName = function(){
        console.log(this.name);
    }
}
function Son(name){
    Parent.call(this,name);
}
var baby = new Son("Tony");
baby.showName();

Parent.call(this,name)意思就是使用父母這個物件替代掉兒子函式中的this物件,那麼這個兒子函式中就可以直接呼叫父母物件中的屬性和方法,這也就是繼承。

bind()

bind()最簡單的用法是建立一個函式,使這個函式不論怎麼呼叫都有同樣的this值。

var person = {
    name : "Tony",
    showName : function(){
        console.log(this.name);
    }
}
person.showName();      //Tony

var name = "Tom";
var getName = person.showName;
getName();              //Tom

var boundGetName = getName.bind(person);
boundGetName();         //Tony