您好,登錄后才能下訂單哦!
小編給大家分享一下如何搞定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...]);
2、將數(shù)組的空元素變?yōu)閡ndefined
空元素與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
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ù)組)。
上面的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)型。
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(); }
點(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è)資訊頻道!
免責(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)容。