函數call() apply() bind() 三個方法目的相同: 改變函數中this的指向;三者在使用上存在差別,這致使三者有各類的應用場景:call()、apply()更爲接近;html
相同點: call、apply、bind 第一個參數:this指向的對象(上下文),以後的參數爲要傳入的變量;數組
不一樣點:app
var obj = {name:"Hello , function !"} function fun(){ console.log(this.name) } fun.call(obj) fun.apply(obj) fun.bind(obj)() //1.call、apply 使用接近,只是參數傳遞形式不一樣 //2.call、apply 會當即執行函數;bind返回一個綁定obj的函數,不會當即執行(這點很重要!決定了bind的使用場景)
一,call()函數
1.用法:oop
call( [obj , [arg1,[arg2,...]]] )this
2.第一個參數obj的取值:spa
2.1).不傳、null、undefined,函數的this指向window對象(在不嚴格模式下,this指向window;嚴格模式下,對應的this指向undefined、null、undefined)設計
function A(){console.log(this)} // 不嚴格模式下 A.call() // window A.call(null) // window A.call(undefined) // window function B(){"use strict"; console.log(this)} // 嚴格模式下 B.call() // undefined B.call(null) // null B.call(undefined) // undefined
2.2).傳入數值、字符串、布爾值等基礎類型,this指向對應的包裝類型Number、String、Booleancode
2.3).傳入函數名,this指向這個函數的引用htm
A.call( B ) // function B(){"use strict"; console.log(this) }
2.4).傳入對象,this指向這個對象(最經常使用)
3.使用場景
3.1)一個容易犯錯的例子
function add(x,y){ console.log( x+y ) } function sub(x,y){ console.log( x-y ) } add.call( sub, 8, 6 ) // 14 // 執行的是add函數,只不過add函數中this指向了sub函數
3.2)改變this的指向(經常使用)
var name = "hi!" var obj = {name:"hello!"} function A(){console.log(this.name)} A() // "hi!" A.call(obj) // "hello!"
3.3) 繼承(經常使用)
function Person(id,name){ this.id = id; this.name = name; } function Student(id,name,grade,score){ Person.call(this,id,name) // 使用call()完成繼承 this.grade = grade; this.score = score; } var stu = new Student('123','Jame','grade 2','98')
二,apply()
apply 和 call 使用基本相同,僅僅是參數傳遞不一樣;call() 參數一個一個傳;apply() 傳遞的是一個數組;
當傳遞參數個數已知時,使用call();若是參數不定,使用apply();
用法:
apply(obj,[arg1,arg2,...])
例題:
1.自定義log()函數,模擬console.log()函數;
分析:
1.1)console.log()函數,能夠傳遞多個不一樣類型的參數;經過自定義函數簡單模擬console.log()能夠這樣作:
function log(arg){ console.log(arg) }
可是這樣有個問題,只能傳遞一個參數,多個參數怎麼辦?
2.1) 因爲多個參數就涉及到arguments對象,能夠將log()改裝成:
function log(){ console.log(arguments) }
這樣還不行,console.log()會把arguments直接打印出來;
此時的arguments僅僅是console.log()函數中arguments對象的arguments[0];
若是想按照console.log()方式打印出來,必須將log()的arguments傳值給console.log()函數的arguments對象;
正確方式以下:
// 只能使用apply function log(){ console.log.apply(console,arguments) } // 不能使用call函數,不然console.log()函數仍然會把 log()函數的arguments對象當成自身arguments[0]
三,bind()
函數綁定bind是ES5中新增的,IE6,7,8不支持;
bind綁定對象後不會當即執行,而是返回一個函數,以便特定狀況下使用;這個技巧大量的用在回調函數、事件處理程序中;
1.事件處理程序中的this指向
DOM元素綁定事件後,事件處理程序中this默認狀況下指向DOM元素;(IE8 默認指向window)
若是要使用別的對象中的方法,就必須使用bind()進行綁定
var handler = { name: "Hello world", handClick: function(event){ console.log( this.name ) } } document.getElementById('demo').addEventListener('click',handler.handClick); // 輸出undefined,DOM綁定後的函數handClick中的this指向DOM元素;而this中不包含name
若是要想輸出handler對象中的name,須要使用bind綁定該對象
document.addEventListener('click',handler.handClick.bind(handler))
2.回調函數中的this指向,常見的定時器
在定時器中,回調函數中的this默認指向window;
常常須要在定時器的回調函數中使用別的對象中的屬性、方法,解決的辦法有三種:1.新建一箇中間變量 2.bind綁定 3.ES6中的箭頭函數 ,最經常使用的是bind、ES6箭頭函數
var obj = { msg:"Hello world", getInfo: function(){ setTimeout(function(){console.log(this.msg)},0) // this->window ,輸出undefined // 1.使用中間變量that var that = this; setTimeout(function(){console.log(that.msg),0}) // that->obj,輸出"Hello world" // 2.使用bind setTimeout(function(){console.log(that.msg).bind(obj),0}) // this->obj,輸出"Hello world" // 2.使用ES6箭頭函數,箭頭函數中的this指向定義這個函數時所在的對象 setTimeout(() => {console.log(this.msg)},0}) // this->obj,輸出"Hello world" } }
bind函數的應用場景基本就是回調函數、事件處理程序;
如何實現bind()函數?(綁定一個上下文,並返回綁定了這個上下文的函數)
function bind(fn,context){ return function(){ return fn.apply(context,arguments) } }
參考:(感謝如下文檔)
[1] 理解call、apply、bind[2] 《JavaScript高級程序設計(第3版)》