深入理解call、apply、bind(改变函数中的this指向)
在JavaScript中call、apply、bind是Function 對象自帶的三個方法,這三個方法的主要作用是改變函數中的 this 指向,從而可以達到`接花移木`的效果。本文將對這三個方法進行詳細的講解,并列出幾個經典應用場景。
?
區分:
1、call(object,arg1,arg2) ,call方法的第一個參數是函數中this重新指向的對象,剩下的參數是傳入該函數的形參
不傳,或者傳null,undefined, 函數中的 this 指向 window 對象,傳遞另一個函數的函數名,函數中的 this 指向這個函數的引用,傳遞字符串、數值或布爾類型等基礎類型,函數中的 this 指向其對應的包裝對象,如 String、Number、Boolean,傳遞一個對象,函數中的 this 指向這個對象。
function say(){console.log(this);}function eat(){}var obj={name:'Bob',};say.call(); //this指向windowsay.call(null); //this指向windowsay.call(undefined); //this指向windowsay.call(eat); //this指向函數得引用 function eat(){}say.call(1); //this指向數值基本類型得包裝對象Numbersay.call("str"); //this指向數值基本類型得包裝對象Stringsay.call(true); //this指向數值基本類型得包裝對象Booleansay.call(obj); //this指向這個對象objcall的作用是允許在一個對象上調用該對象沒有定義的方法,并且這個方法可以訪問該對象中的屬性,如下
var a={name:'Bob',food:'fish',say:function(){console.log('HI,this is a.say!!');}}function b(name){ //b.call(a,'Tom');使得a對象能調用其他函數方法console.log("post Params:"+name); //a對象使用了b('Tom')方法, 輸出post Params: Tomconsole.log('I am '+this.name); //a對象獲取了自己的屬性 ,輸出 I am Bobthis.say(); //a對象使用自己的方法, 輸出 HI,this is a.say!!}b.call(a,'Tom');?
2、apply(object,[arg1,arg2]),apply方法的第一個參數是函數中this重新指向的對象,第二個參數數組是傳入該函數的形參;和call方法唯一區別是第二參數的傳遞形式是數組。使用如下
function b(x,y,z){ console.log(x,y,z); //會將多個參數拼接輸出 }//apply() 方法接收的參數是數組形式,但是傳遞給調用的函數時是用參數列別的形式傳遞的b.apply(null,[1,2,3]); //輸出 1 2 3 ,這里等同于window對象調用b方法并使用參數[1,2,3]?
3、bind(object,arg1,arg2) ,bind方法是ES5 新增的一個方法,傳參和call方法一致。與call、apply方法的區別是,call和apply方法會對目標函數進行自動執行,會返回一個新的函數。call和apply無法在事件綁定函數中使用。而bind彌補了這個缺陷,在實現改變函數 this 的同時又不會自動執行目標函數,因此可以完美的解決上述問題,
var obj = {name: 'onepixel'};//給document添加click事件監聽函數,并綁定onclick函數//通過bind方法設置onclick的this指向是obj,并傳遞參數p1,p2document.addEventListener('click',onClick.bind(obj,22,66),false);//可以理解為當網頁觸發click事件時,obj對象執行onclick函數并傳遞給該函數參數p1,p2function onClick(p1,p2){console.log(this.name,p1,p2); //輸出 onepixel 22 66} var button=document.getElementById("button"),text=document.getElementById("text");button.onclick=function(){ //聲明按鈕的點擊事件觸發的函數console.log(this.id);}.bind(text); //改變this的指向?
注意:一旦函數通過bind綁定了有效的this對象,那么在函數執行過程中this會指向該對象,即使使用call、apply也不能改變this的指向
bind 進行深入的理解,我們來看一下 bind 的 polyfill 實現(ie6~ie8不支持該方法):
if (!Function.prototype.bind) { //瀏覽器js中不支持bind方法情況下Function.prototype.bind = function(context) { //在函數原型對象自定義bind方法,context是this重指向的對象,也就是上面function(){}.bind(text)中的text對象var self = this //因為這個自定義的bind方法返回的是一個匿名函數,匿名函數具有全局性,其this會指向window。所以這里需要將bind方法觸發時的調用對象this進行保存, args = Array.prototype.slice.call(arguments); //這里借用了數組原型對象的slice方法將形參 類數組轉化為真正的數組return function() { //bind方法返回一個匿名函數return self.apply(context, args.slice(1)); //self就是函數觸發時的調用對象,this重指向bind方法的第一個參數對象,}}; }代碼解析:在瀏覽器不支持bind方法時,自定義bind方法中會返回一個匿名函數,該匿名函數保存bind方法調用時的this,這里是self=this; 并通過self.apply(newObj, args); 的原理實現改變this指向不執行函數的效果
?
使用場景:
1、繼承
JavaScript中沒有諸如Java、C# 等高級語言中的extend 關鍵字,因此JS 中沒有繼承的概念,如果一定要繼承的話,call 和 apply 可以實現這個功能:
function Animal(name,weight){this.name=name;this.weight=weight;}function Cat(){Animal.apply(this,['cat','10kg']); //理解為在Cat函數對象創建時會使用Animal()方法//Animal.call(this,'cat','10kg');this.say=function(){console.log('I am '+this.name+' , my weight is '+this.weight);}}var cat = new Cat();cat.say(); //輸出 I am cat , my weight is 10kg?
2、移花接木
在講下面的內容之前,我們首先來認識一下JavaScript 中的一個非標準專業術語:ArrayLike?(類數組/偽數組)
ArrayLike 對象即擁有數組的一部分行為,在DOM 中早已表現出來,而jQuery 的崛起讓ArrayLike 在JavaScript 中大放異彩。ArrayLike 對象的精妙在于它和JS 原生的 Array 類似,但是它是自由構建的,它來自開發者對JavaScript 對象的擴展,也就是說:對于它的原型(prototype)我們可以自由定義,而不會污染到JS原生的Array。?
ArrayLike 對象在JS中被廣泛使用,比如DOM 中的NodeList, 函數中的arguments 都是類數組對象,這些對象像數組一樣存儲著每一個元素,但它沒有操作數組的方法,而我們可以通過call 將數組的某些方法`移接`到ArrayLike 對象,從而達到操作其元素的目的。比如我們可以這樣遍歷函數中的arguments:
function test(){console.log(typeof(arguments)); //輸出Object ,ArrayLike是類數組對象//檢測arguments是否是Array的實例console.log(arguments instanceof Array); //輸出 falseconsole.log(Array.isArray(arguments)); //輸出 false//判斷arguments是否有forEach的方法console.log(arguments.forEach); //輸出 undefined//將數組中的forEach方法應用到arguments上Array.prototype.forEach.call(arguments,function(item){console.log(item); //輸出 1 2 3 4 5});}test(1,2,3,4,5);除此之外,對于apply 而言,我們上面提到了它獨有的一個特性,即apply 接收的是數組,在傳遞給調用函數的時候是以參數列表傳遞的。 這個特性讓apply 看起來比call 略勝一籌,比如有這樣一個場景:給定一個數組[1,3,4,7],然后求數組中的最大元素,而我們知道,數組中并沒有獲取最大值的方法,一般情況下,你需要通過編寫代碼來實現。而我們知道,Math 對象中有一個獲取最大值的方法,即Math.max(), max方法需要傳遞一個參數列表,然后返回這些參數中的最大值。而apply 不僅可以將Math 對象的max 方法應用到其他對象上,還可以將一個數組轉化為參數列表傳遞給max,看代碼就能一目了然:
var arr = [-1,2, 3, 1, 5, 4, 223, 11]; var a=Math.max.apply(null, arr); // 223 console.log(a);?
學習網址:https://blog.csdn.net/u014267183/article/details/52610600 、軟謀前端 (深入理解 call,apply 和 bind)?
?
總結
以上是生活随笔為你收集整理的深入理解call、apply、bind(改变函数中的this指向)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php sql desc,PHP SQL
- 下一篇: Redox随笔(1)-用Rust语言编写