溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

如何搞定this綁定方法call apply bind

發(fā)布時(shí)間:2021-11-22 15:11:12 來(lái)源:億速云 閱讀:94 作者:小新 欄目:開(kāi)發(fā)技術(shù)

小編給大家分享一下如何搞定this綁定方法call apply bind,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

先來(lái)看一個(gè)例子

var obj = {}; //創(chuàng)建一個(gè)對(duì)象
obj.name = "James";  //給對(duì)象添加一個(gè)屬性
obj.say = function(){  //給對(duì)象添加一個(gè)方法
    console.log('My name is' + this.name);
};
obj.say(); //this指向obj,所以輸出"My name is James"
var fn = obj.say;
fn(); //this指向了window,全局中沒(méi)有name變量,所以只能輸出"My name is",沒(méi)有name

由于fn里面的this指向了window而不是obj,所以name沒(méi)有輸出。如果也想讓fn輸出James,也就意味著必須讓fn里面的this指向obj,這個(gè)時(shí)候需要我們強(qiáng)行改變this指向了。

call方法,可以指定函數(shù)內(nèi)部this的指向(即函數(shù)執(zhí)行時(shí)所在的作用域),然后在所指定的作用域中,調(diào)用該函數(shù)。

var obj = {}; //創(chuàng)建一個(gè)對(duì)象
obj.name = "James";  //給對(duì)象添加一個(gè)屬性
obj.say = function(){  //給對(duì)象添加一個(gè)方法
    console.log('My name is' + this.name);
};
obj.say(); //this指向obj,所以輸出"My name is James"
var fn = obj.say;
fn.call(obj); //輸出"My name is James"

上面的代碼中,fn函數(shù)的this指向window,call方法改變了this的指向,讓this對(duì)象指向了obj,然后在對(duì)象obj的作用域中運(yùn)行函數(shù)fn

call方法的參數(shù)是一個(gè)對(duì)象,如果參數(shù)為空,null和undefined,則默認(rèn)傳入全局對(duì)象。我們把上面的例子做下修改:

var name = 'Kobe'; //定義全局變量
var obj = {}; //創(chuàng)建一個(gè)對(duì)象
obj.name = "James";  //給對(duì)象添加一個(gè)屬性
obj.say = function(){  //給對(duì)象添加一個(gè)方法
    console.log('My name is' + this.name);
};
var fn = obj.say;
fn.call(); //輸出"My name is Kobe"
fn.call(null); //輸出"My name is Kobe"
fn.call(undefined); //輸出"My name is Kobe"
fn.call(window); //輸出"My name is Kobe"
fn.call(obj); //輸出"My name is James"

call方法還可以接受多個(gè)參數(shù)  func.call(this要指向的那個(gè)對(duì)象, 參數(shù)1, 參數(shù)2……),call方法的第一個(gè)參數(shù)就是this要指向的那個(gè)對(duì)象,后面的參數(shù)則是調(diào)用函數(shù)時(shí)需要所需的參數(shù)

var uname ='Kobe';
var team ='Lakers';
var num =24;
var obj = {};
obj.uname ='Westbook';
obj.team ='Thunder';
obj.num =0;
obj.introduce =function(){
    console.log('My name is'+ this.uname+', I am from'+this.team+',My number is'+this.num);
};
var fn = obj.introduce;
fn();  //My name is Kobe, I am from Lakers,My number is 24
fn.call(obj,uname,team,num); //My name is Westbook, I am from Thunder,My number is 0

這段代碼中,fn函數(shù)的this指向window,執(zhí)行fn時(shí)輸出的就是全局變量。通過(guò)call方法把this指向了obj對(duì)象,然后再obj的作用域中運(yùn)行函數(shù)fn,所以輸出的就是Westbook而不再是Kobe了

apply方法的作用與call方法類(lèi)似,也是改變this指向,然后再調(diào)用該函數(shù)。唯一的區(qū)別就是,它接收一個(gè)數(shù)組作為函數(shù)執(zhí)行時(shí)的參數(shù),使用格式如下:

func.apply(this要指向的那個(gè)對(duì)象,[參數(shù)1,參數(shù)2……])

所以上面的例子中,除了使用call方法以外,我們還可以使用apply方法

fn.apply(obj,[uname,team,num]);  //My name is Westbook, I am from Thunder,My number is 0

apply方法的實(shí)際應(yīng)用:

1、找出數(shù)組中最大的元素

Math對(duì)象提供了獲得最大值和最小值的API

Math.max(a,b,c...); 獲得參數(shù)中最大的值

Math.min(a,b,c...);  獲得參數(shù)中最小的值

問(wèn)題:max和min不支持獲取數(shù)組中的最大值,因?yàn)镴avascript沒(méi)有提供獲取數(shù)組最大值的API,解決辦法就是:Math.max.apply(null,[a,b,b...]);

如何搞定this綁定方法call apply bind

2、將數(shù)組的空元素變?yōu)閡ndefined

如何搞定this綁定方法call apply bind

空元素與undefined的差別在于,數(shù)組的forEach方法會(huì)跳過(guò)空元素,但是不會(huì)跳過(guò)undefined。所以,在遍歷內(nèi)部元素的時(shí)候會(huì)得到不同的結(jié)果

var arr=['a', ,'b'];
function show(i){
    console.log(i);
};
arr.forEach(show);//a b
Array.apply(null,arr).forEach(show); //a undefined b

如何搞定this綁定方法call apply bind

3、把類(lèi)數(shù)組對(duì)象轉(zhuǎn)為真正的數(shù)組(call和apply都可以)

Array.prototype.slice.call(obj)-->obj是一個(gè)類(lèi)數(shù)組,通過(guò)這個(gè)函數(shù)可以把類(lèi)數(shù)組轉(zhuǎn)換成一個(gè)真正的數(shù)組(參數(shù)位置就是類(lèi)數(shù)組)。  

如何搞定this綁定方法call apply bind  

上面的apply/call方法中的參數(shù)都是對(duì)象,返回結(jié)果都是數(shù)組,這就起到了把對(duì)象轉(zhuǎn)成數(shù)組的目的。同時(shí)可以看出,這個(gè)方法起作用的前提是,被處理的對(duì)象必須有l(wèi)ength屬性,以及相對(duì)應(yīng)的數(shù)字鍵

Object.prototype.toString.call(arg)-->arg可以是任意一種類(lèi)型的值,這個(gè)函數(shù)最精確的判斷出參數(shù)的類(lèi)型。

如何搞定this綁定方法call apply bind

4、綁定回調(diào)函數(shù)的對(duì)象

<button id="button">Button</button>
var obj = new Object();
obj.fun = function(){
    console.log(this===obj); //this對(duì)象指向obj
};
var fun = function(){ //全局函數(shù)fun,this指向window
    obj.fun.apply(obj); //強(qiáng)行把this指向obj (這里也可以用call)
};
var button = document.getElementById('button');
button.onclick=function(){
    fun();
}

如何搞定this綁定方法call apply bind

點(diǎn)擊按鈕以后,控制臺(tái)將會(huì)顯示true。由于apply方法(或者call方法)不僅綁定函數(shù)執(zhí)行時(shí)所在的對(duì)象,還會(huì)立即執(zhí)行函數(shù),因此不得不把綁定語(yǔ)句寫(xiě)在一個(gè)函數(shù)體內(nèi)。更簡(jiǎn)潔的寫(xiě)法是采用下面介紹的bind方法。

bind方法用于將函數(shù)體內(nèi)的this綁定到某個(gè)對(duì)象,然后返回一個(gè)新函數(shù)。

var obj = {};
obj.name = "Kobe";
obj.say = function(){ //this指向obj
    console.log("My name is"+this.name);
};
var fn = obj.say.bind(obj); //全局函數(shù)fn,this指向window
fn(); //My name is Kobe

obj的say方法賦值給一個(gè)全局變量fn以后,fn的this指向了window,所以是無(wú)法輸出name的?,F(xiàn)在通過(guò)bind方法強(qiáng)行改變讓this指向obj對(duì)象。

bind方法比call/apply方法更進(jìn)一步的是,除了綁定this以外,還可以綁定原函數(shù)的參數(shù)

var add = function (x, y) {
    console.log (x * this.m + y * this.n);
};

var obj = {
    m: 2,
    n: 3
};

var newAdd = add.bind(obj, 5);

newAdd(6); //28    5*2+6*3
newAdd(8); //34    5*2+8*3
newAdd(10); //40   5*2+10*3

上面代碼中,bind不僅綁定了this對(duì)象,還將add函數(shù)的第一個(gè)參數(shù)x綁定成5,然后返回一個(gè)新的函數(shù)newAdd,這個(gè)函數(shù)只要再接受一個(gè)參數(shù)y就能運(yùn)行了

為了進(jìn)一步驗(yàn)證,我們可以把上面的代碼稍微改變一下

var add = function (x, y) {
    console.log (x * this.m + y * this.n);
};

var obj = {
    m: 2,
    n: 3
};

var newAdd = add.bind(obj, 5, 6);

newAdd(6);     //28
newAdd(8);     //28
newAdd(10);    //28
newAdd(11,12); //28

現(xiàn)在是同時(shí)綁定了參數(shù)x為5,y為6,后續(xù)無(wú)論怎么往newAdd函數(shù)中傳值,都不能改變輸出結(jié)果

如果bind方法的第一個(gè)參數(shù)是null或者undefined,那么,this就綁定到了全局對(duì)象window

function add(x, y) {
    console.log(x + y);
}

var plus = add.bind(null, 5); //this指向window,x綁定成5
plus(10);     // 15 y=10 5+10
plus(10,10);  //15 x已經(jīng)綁定成5,無(wú)法改成10

上面代碼中,函數(shù)add內(nèi)部并沒(méi)有this,使用bind方法的主要目的是綁定參數(shù)x,以后每次運(yùn)行新函數(shù)plus,就只需要提供另一個(gè)參數(shù)y就夠了。而且因?yàn)閍dd內(nèi)部沒(méi)有this,所以bind的第一個(gè)參數(shù)是null,不過(guò)這里如果是其他對(duì)象,也沒(méi)有影響。

對(duì)于那些不支持bind方法的老式瀏覽器,可以自行定義bind方法。

if(!('bind' in Function.prototype)){
    Function.prototype.bind = function(){
        var fn = this;
        var context = arguments[0];
        var args = Array.prototype.slice.call(arguments, 1);
        return function(){
            return fn.apply(context, args);
        }
    }
}

bind方法使用注意點(diǎn):

1、每次返回一個(gè)新函數(shù)

bind方法每運(yùn)行一次,就返回一個(gè)新函數(shù),這會(huì)產(chǎn)生一些問(wèn)題。比如:事件監(jiān)聽(tīng)時(shí),不能寫(xiě)成下面這樣

element.addEventListener('click', o.m.bind(o));
element.removeEventListener('click', o.m.bind(o));

上面代碼中,click事件綁定bind方法生成的一個(gè)匿名函數(shù)。這樣會(huì)導(dǎo)致無(wú)法取消綁定,所以,上面的代碼是無(wú)效的。正確的寫(xiě)法:

var listener = o.m.bind(o);
element.addEventListener('click', listener);
//  ...
element.removeEventListener('click', listener);

2、結(jié)合回調(diào)函數(shù)使用

回調(diào)函數(shù)是Javascript最常用的模式之一,但是一個(gè)常見(jiàn)的錯(cuò)誤是,將包含this的方法直接當(dāng)回調(diào)函數(shù)

var counter = {
    count: 0,
    inc: function () {
        'use strict';  //注意:嚴(yán)格模式下this對(duì)象是undefined
        this.count++;  //this對(duì)象不是counter
        alert(this);
    }
};

function callIt(callback) { //全局函數(shù)callIt的this對(duì)象在嚴(yán)格模式下也不是window了
    callback();
}

callIt(counter.inc); //Uncaught TypeError: Cannot read property 'count' of undefined

上面代碼中,counter.inc方法被當(dāng)作回調(diào)函數(shù),傳入了callIt,調(diào)用時(shí)其內(nèi)部的this指向callIt運(yùn)行時(shí)所在的對(duì)象,即頂層對(duì)象window,所以得不到預(yù)想結(jié)果。注意,上面的counter.inc方法內(nèi)部使用了嚴(yán)格模式,在該模式下,this指向頂層對(duì)象時(shí)會(huì)報(bào)錯(cuò),一般模式不會(huì)。  

解決方法就是使用bind方法,將counter.inc綁定counter。

var counter = {
    count: 0,
    inc: function () {
        'use strict';  //注意:嚴(yán)格模式下this對(duì)象是undefined
        console.log(this.count++);  //this對(duì)象不是counter
    }
};

function callIt(callback) { //全局函數(shù)callIt的this對(duì)象在嚴(yán)格模式下也不是window了
    callback();
}

callIt(counter.inc.bind(counter)); //0 注意前++和后++的區(qū)別,前++返回新值,后++返回舊值
callIt(counter.inc.bind(counter)); //1

還有一種情況比較隱蔽,就是某些數(shù)組方法可以接受一個(gè)函數(shù)當(dāng)作參數(shù)。這些函數(shù)內(nèi)部的this指向,很可能也會(huì)出錯(cuò)。

var obj = {
    name: '張三',
    times: [1, 2, 3],
    print: function () { //this指向obj
        this.times.forEach(function (n) {//this指向window
            console.log(this.name);
        });
    }
};

obj.print();

只需要把this的指向劃分出來(lái),很容易就能判斷不會(huì)有輸出結(jié)果,因?yàn)槿譀](méi)有name。解決這個(gè)問(wèn)題也是通過(guò)bind綁定this

var obj = {
    name: '張三',
    times: [1, 2, 3],
    print: function () { //this指向obj
        this.times.forEach(function (n) {
            console.log(this.name);
            }.bind(this) //遍歷數(shù)組的同時(shí)綁定了this
        );
    }
};

obj.print(); //張三*3

除此之外,還可以用留住this的方法,具體可以參照“撲朔迷離的this關(guān)鍵字”一文

var obj = {
    name: '張三',
    times: [1, 2, 3],
    print: function () {
        var me=this; //留住this
        this.times.forEach(function (n) {
            console.log(me.name);
        });
    }
};

obj.print(); 張三*3

3、結(jié)合call方法使用

利用bind方法,可以改寫(xiě)一些JavaScript原生方法的使用形式,以數(shù)組的slice方法為例

[1, 2, 3].slice(0, 1); //從第0位開(kāi)始,截取1位,返回一個(gè)數(shù)組
// [1]

// 等同于

Array.prototype.slice.call([1, 2, 3], 0, 1);
// [1]

上面的代碼中,數(shù)組的slice方法從[1, 2, 3]里面,按照指定位置和長(zhǎng)度切分出另一個(gè)數(shù)組。這樣做的本質(zhì)是在[1, 2, 3]上面調(diào)用Array.prototype.slice方法,因此可以用call方法表達(dá)這個(gè)過(guò)程,得到同樣的結(jié)果。  

call方法實(shí)質(zhì)上是調(diào)用Function.prototype.call方法,因此上面的表達(dá)式可以用bind方法改寫(xiě)。

var slice = Function.prototype.call.bind(Array.prototype.slice);

slice([1, 2, 3], 0, 1) // [1]

可以看到,利用bind方法,將[1, 2, 3].slice(0, 1)變成了slice([1, 2, 3], 0, 1)的形式。這種形式的改變還可以用于其他數(shù)組方法。

var push = Function.prototype.call.bind(Array.prototype.push);
var pop = Function.prototype.call.bind(Array.prototype.pop);
var a = [1 ,2 ,3];
push(a, 4)  //在數(shù)組末尾添加一個(gè)元素4
a // [1, 2, 3, 4]
pop(a)  //刪除數(shù)組最后一個(gè)元素
a // [1, 2, 3]

如果再進(jìn)一步,將Function.prototype.call方法綁定到Function.prototype.bind對(duì)象,就意味著bind的調(diào)用形式也可以被改寫(xiě)。

function f() {
  console.log(this.v);
}
var o = { v: 123 };
var bind = Function.prototype.call.bind(Function.prototype.bind);
bind(f, o)() // 123

上面代碼表示,將Function.prototype.call方法綁定Function.prototype.bind以后,bind方法的使用形式從f.bind(o),變成了bind(f, o)。

以上是“如何搞定this綁定方法call apply bind”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI