溫馨提示×

溫馨提示×

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

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

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

發布時間:2021-11-22 15:11:12 來源:億速云 閱讀:134 作者:小新 欄目:開發技術

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

先來看一個例子

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

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

call方法,可以指定函數內部this的指向(即函數執行時所在的作用域),然后在所指定的作用域中,調用該函數。

var obj = {}; //創建一個對象
obj.name = "James";  //給對象添加一個屬性
obj.say = function(){  //給對象添加一個方法
    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函數的this指向window,call方法改變了this的指向,讓this對象指向了obj,然后在對象obj的作用域中運行函數fn

call方法的參數是一個對象,如果參數為空,null和undefined,則默認傳入全局對象。我們把上面的例子做下修改:

var name = 'Kobe'; //定義全局變量
var obj = {}; //創建一個對象
obj.name = "James";  //給對象添加一個屬性
obj.say = function(){  //給對象添加一個方法
    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方法還可以接受多個參數  func.call(this要指向的那個對象, 參數1, 參數2……),call方法的第一個參數就是this要指向的那個對象,后面的參數則是調用函數時需要所需的參數

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函數的this指向window,執行fn時輸出的就是全局變量。通過call方法把this指向了obj對象,然后再obj的作用域中運行函數fn,所以輸出的就是Westbook而不再是Kobe了

apply方法的作用與call方法類似,也是改變this指向,然后再調用該函數。唯一的區別就是,它接收一個數組作為函數執行時的參數,使用格式如下:

func.apply(this要指向的那個對象,[參數1,參數2……])

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

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

apply方法的實際應用:

1、找出數組中最大的元素

Math對象提供了獲得最大值和最小值的API

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

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

問題:max和min不支持獲取數組中的最大值,因為Javascript沒有提供獲取數組最大值的API,解決辦法就是:Math.max.apply(null,[a,b,b...]);

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

2、將數組的空元素變為undefined

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

空元素與undefined的差別在于,數組的forEach方法會跳過空元素,但是不會跳過undefined。所以,在遍歷內部元素的時候會得到不同的結果

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、把類數組對象轉為真正的數組(call和apply都可以)

Array.prototype.slice.call(obj)-->obj是一個類數組,通過這個函數可以把類數組轉換成一個真正的數組(參數位置就是類數組)。  

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

上面的apply/call方法中的參數都是對象,返回結果都是數組,這就起到了把對象轉成數組的目的。同時可以看出,這個方法起作用的前提是,被處理的對象必須有length屬性,以及相對應的數字鍵

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

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

4、綁定回調函數的對象

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

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

點擊按鈕以后,控制臺將會顯示true。由于apply方法(或者call方法)不僅綁定函數執行時所在的對象,還會立即執行函數,因此不得不把綁定語句寫在一個函數體內。更簡潔的寫法是采用下面介紹的bind方法。

bind方法用于將函數體內的this綁定到某個對象,然后返回一個新函數。

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

obj的say方法賦值給一個全局變量fn以后,fn的this指向了window,所以是無法輸出name的?,F在通過bind方法強行改變讓this指向obj對象。

bind方法比call/apply方法更進一步的是,除了綁定this以外,還可以綁定原函數的參數

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對象,還將add函數的第一個參數x綁定成5,然后返回一個新的函數newAdd,這個函數只要再接受一個參數y就能運行了

為了進一步驗證,我們可以把上面的代碼稍微改變一下

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

現在是同時綁定了參數x為5,y為6,后續無論怎么往newAdd函數中傳值,都不能改變輸出結果

如果bind方法的第一個參數是null或者undefined,那么,this就綁定到了全局對象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已經綁定成5,無法改成10

上面代碼中,函數add內部并沒有this,使用bind方法的主要目的是綁定參數x,以后每次運行新函數plus,就只需要提供另一個參數y就夠了。而且因為add內部沒有this,所以bind的第一個參數是null,不過這里如果是其他對象,也沒有影響。

對于那些不支持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方法使用注意點:

1、每次返回一個新函數

bind方法每運行一次,就返回一個新函數,這會產生一些問題。比如:事件監聽時,不能寫成下面這樣

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

上面代碼中,click事件綁定bind方法生成的一個匿名函數。這樣會導致無法取消綁定,所以,上面的代碼是無效的。正確的寫法:

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

2、結合回調函數使用

回調函數是Javascript最常用的模式之一,但是一個常見的錯誤是,將包含this的方法直接當回調函數

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

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

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

上面代碼中,counter.inc方法被當作回調函數,傳入了callIt,調用時其內部的this指向callIt運行時所在的對象,即頂層對象window,所以得不到預想結果。注意,上面的counter.inc方法內部使用了嚴格模式,在該模式下,this指向頂層對象時會報錯,一般模式不會。  

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

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

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

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

還有一種情況比較隱蔽,就是某些數組方法可以接受一個函數當作參數。這些函數內部的this指向,很可能也會出錯。

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的指向劃分出來,很容易就能判斷不會有輸出結果,因為全局沒有name。解決這個問題也是通過bind綁定this

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

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

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

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、結合call方法使用

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

[1, 2, 3].slice(0, 1); //從第0位開始,截取1位,返回一個數組
// [1]

// 等同于

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

上面的代碼中,數組的slice方法從[1, 2, 3]里面,按照指定位置和長度切分出另一個數組。這樣做的本質是在[1, 2, 3]上面調用Array.prototype.slice方法,因此可以用call方法表達這個過程,得到同樣的結果。  

call方法實質上是調用Function.prototype.call方法,因此上面的表達式可以用bind方法改寫。

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)的形式。這種形式的改變還可以用于其他數組方法。

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)  //在數組末尾添加一個元素4
a // [1, 2, 3, 4]
pop(a)  //刪除數組最后一個元素
a // [1, 2, 3]

如果再進一步,將Function.prototype.call方法綁定到Function.prototype.bind對象,就意味著bind的調用形式也可以被改寫。

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”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女