javascript中call apply 與 bind方法詳解
在JavaScript中,call、apply和bind是Function對(duì)象自帶的三個(gè)方法,本文將通過(guò)幾個(gè)場(chǎng)景的應(yīng)用,來(lái)詳細(xì)理解三個(gè)方法。
call()
call() 方法在使用一個(gè)指定的this值和若干個(gè)指定的參數(shù)值的前提下調(diào)用某個(gè)函數(shù)或方法。
當(dāng)調(diào)用一個(gè)函數(shù)時(shí),可以賦值一個(gè)不同的 this 對(duì)象。this 引用當(dāng)前對(duì)象,即 call 方法的第一個(gè)參數(shù)。
通過(guò) call 方法,你可以在一個(gè)對(duì)象上借用另一個(gè)對(duì)象上的方法,比如Object.prototype.toString.call([]),就是一個(gè)Array對(duì)象借用了Object對(duì)象上的方法。
語(yǔ)法 fun.call(thisArg[, arg1[, arg2[, ...]]])
thisArg
在fun函數(shù)運(yùn)行時(shí)指定的this值。需要注意的是下面幾種情況
(1)不傳,或者傳null,undefined, 函數(shù)中的this指向window對(duì)象
(2)傳遞另一個(gè)函數(shù)的函數(shù)名,函數(shù)中的this指向這個(gè)函數(shù)的引用,并不一定是該函數(shù)執(zhí)行時(shí)真正的this值
(3)值為原始值(數(shù)字,字符串,布爾值)的this會(huì)指向該原始值的自動(dòng)包裝對(duì)象,如 String、Number、Boolean
(4)傳遞一個(gè)對(duì)象,函數(shù)中的this指向這個(gè)對(duì)象
arg1, arg2, ...
指定的參數(shù)列表。
例子
初級(jí)應(yīng)用例子
function a(){
//輸出函數(shù)a中的this對(duì)象
console.log(this);
}
//定義函數(shù)b
function b(){}
var obj = {name:'這是一個(gè)屌絲'}; //定義對(duì)象obj
a.call(); //window
a.call(null); //window
a.call(undefined);//window
a.call(1); //Number
a.call(''); //String
a.call(true); //Boolean
a.call(b);// function b(){}
a.call(obj); //Object
使用call方法調(diào)用匿名函數(shù)并且指定上下文的this
在下面的例子中,當(dāng)調(diào)用 greet 方法的時(shí)候,該方法的 this 值會(huì)綁定到 i對(duì)象。
function greet() {
var reply = [this.person, '是一個(gè)輕量的', this.role].join(' ');
console.log(reply);
}
var i = {function greet() {
var reply = [this.person, '是一個(gè)輕量的', this.role].join(' ');
console.log(reply);
}
var i = {
person: 'JSLite.io', role: 'Javascript 庫(kù)。'
};
greet.call(i);
// JSLite.io 是一個(gè)輕量的 Javascript 庫(kù)。
person: 'JSLite.io', role: 'Javascript 庫(kù)。'
};
greet.call(i);
// JSLite.io 是一個(gè)輕量的 Javascript 庫(kù)。
使用call方法調(diào)用匿名函數(shù)
在下例中的for循環(huán)體內(nèi),我們創(chuàng)建了一個(gè)匿名函數(shù),然后通過(guò)調(diào)用該函數(shù)的call方法,將每個(gè)數(shù)組元素作為指定的this值執(zhí)行了那個(gè)匿名函數(shù)。這個(gè)匿名函數(shù)的主要目的是給每個(gè)數(shù)組元素對(duì)象添加一個(gè)print方法,這個(gè)print方法可以打印出各元素在數(shù)組中的正確索引號(hào)。當(dāng)然,這里不是必須得讓數(shù)組元素作為this值傳入那個(gè)匿名函數(shù)(普通參數(shù)就可以),目的是為了演示call的用法。
var animals = [
{species: 'Lion', name: 'King'},
{species: 'Whale', name: 'Fail'}
];
for (var i = 0; i < animals.length; i++) {
(function (i) {
this.print = function () {
console.log('#' + i + ' ' + this.species + ': ' + this.name);
}
this.print();
}).call(animals[i], i);
}
//#0 Lion: King
//#1 Whale: Fail
使用call方法調(diào)用函數(shù)傳參數(shù)
var a = {
name:'JSLite.io', //定義a的屬性
say:function(){ //定義a的方法
console.log("Hi,I'm function a!");
}
};
function b(name){
console.log("Post params: "+ name);
console.log("I'm "+ this.name);
this.say();
}
b.call(a,'test');
//Post params: test
//I'm onepixel
//I'm function a!
apply()
語(yǔ)法與 call() 方法的語(yǔ)法幾乎完全相同,唯一的區(qū)別在于,apply的第二個(gè)參數(shù)必須是一個(gè)包含多個(gè)參數(shù)的數(shù)組(或類數(shù)組對(duì)象)。apply的這個(gè)特性很重要,
在調(diào)用一個(gè)存在的函數(shù)時(shí),你可以為其指定一個(gè) this 對(duì)象。 this 指當(dāng)前對(duì)象,也就是正在調(diào)用這個(gè)函數(shù)的對(duì)象。 使用 apply, 你可以只寫一次這個(gè)方法然后在另一個(gè)對(duì)象中繼承它,而不用在新對(duì)象中重復(fù)寫該方法。
語(yǔ)法:fun.apply(thisArg[, argsArray])
注意: 需要注意:Chrome 14 以及 Internet Explorer 9 仍然不接受類數(shù)組對(duì)象。如果傳入類數(shù)組對(duì)象,它們會(huì)拋出異常。
參數(shù)
thisArg
同上call 的thisArg參數(shù)。
argsArray
一個(gè)數(shù)組或者類數(shù)組對(duì)象,其中的數(shù)組元素將作為單獨(dú)的參數(shù)傳給 fun 函數(shù)。如果該參數(shù)的值為null 或 undefined,則表示不需要傳入任何參數(shù)。從ECMAScript 5 開始可以使用類數(shù)組對(duì)象。
例子
function jsy(x,y,z){
console.log(x,y,z);
}
jsy.apply(null,[1,2,3]);
// 1 2 3
使用apply來(lái)鏈接構(gòu)造器的例子
你可以使用apply來(lái)給一個(gè)對(duì)象鏈接構(gòu)造器,類似于Java. 在接下來(lái)的例子中我們會(huì)創(chuàng)建一個(gè)叫做construct的全局的Function函數(shù),來(lái)使你能夠在構(gòu)造器中使用一個(gè)類數(shù)組對(duì)象而非參數(shù)列表。
Function.prototype.construct = function(aArgs) {
var fConstructor = this,
fNewConstr = function() {
fConstructor.apply(this, aArgs);
};
fNewConstr.prototype = fConstructor.prototype;
return new fNewConstr();
};
function MyConstructor () {
for (var nProp = 0; nProp < arguments.length; nProp++) {
console.log(arguments,this)
this["property" + nProp] = arguments[nProp];
}
}
var myArray = [4, "Hello world!", false];
var myInstance = MyConstructor.construct(myArray);
console.log(myInstance.property1); // logs "Hello world!"
console.log(myInstance instanceof MyConstructor); // logs "true"
console.log(myInstance.constructor); // logs "MyConstructor"
使用apply和內(nèi)置函數(shù)
聰明的apply用法允許你在某些本來(lái)需要寫成遍歷數(shù)組變量的任務(wù)中使用內(nèi)建的函數(shù)。在接下里的例子中我們會(huì)使用Math.max/Math.min來(lái)找出一個(gè)數(shù)組中的最大/最小值。
//里面有最大最小數(shù)字值的一個(gè)數(shù)組對(duì)象
var numbers = [5, 6, 2, 3, 7];
/* 使用 Math.min/Math.max 在 apply 中應(yīng)用 */
var max = Math.max.apply(null, numbers);
// 一般情況是用 Math.max(5, 6, ..) 或者 Math.max(numbers[0], ...) 來(lái)找最大值
var min = Math.min.apply(null, numbers);
//通常情況我們會(huì)這樣來(lái)找到數(shù)字的最大或者最小值
//比對(duì)上面的栗子,是不是下面的看起來(lái)沒(méi)有上面的舒服呢?
max = -Infinity, min = +Infinity;
for (var i = 0; i < numbers.length; i++) {
if (numbers[i] > max)
max = numbers[i];
if (numbers[i] < min)
min = numbers[i];
}
參數(shù)數(shù)組切塊后循環(huán)傳入
function minOfArray(arr) {
var min = Infinity;
var QUANTUM = 32768;
for (var i = 0, len = arr.length; i < len; i += QUANTUM) {
var submin = Math.min.apply(null, arr.slice(i, Math.min(i + QUANTUM, len)));
console.log(submin, min)
min = Math.min(submin, min);
}
return min;
}
var min = minOfArray([5, 6, 2, 3, 7]);
bind
bind() 函數(shù)會(huì)創(chuàng)建一個(gè)新函數(shù)(稱為綁定函數(shù))
bind是ES5新增的一個(gè)方法
傳參和call或apply類似
不會(huì)執(zhí)行對(duì)應(yīng)的函數(shù),call或apply會(huì)自動(dòng)執(zhí)行對(duì)應(yīng)的函數(shù)
返回對(duì)函數(shù)的引用
語(yǔ)法 fun.bind(thisArg[, arg1[, arg2[, ...]]])
下面例子:當(dāng)點(diǎn)擊網(wǎng)頁(yè)時(shí),EventClick被觸發(fā)執(zhí)行,輸出JSLite.io p1 p2, 說(shuō)明EventClick中的this被bind改變成了obj對(duì)象。如果你將EventClick.bind(obj,'p1','p2') 變成 EventClick.call(obj,'p1','p2') 的話,頁(yè)面會(huì)直接輸出 JSLite.io p1 p2
var obj = {name:'JSLite.io'};
/**
* 給document添加click事件監(jiān)聽(tīng),并綁定EventClick函數(shù)
* 通過(guò)bind方法設(shè)置EventClick的this為obj,并傳遞參數(shù)p1,p2
*/
document.addEventListener('click',EventClick.bind(obj,'p1','p2'),false);
//當(dāng)點(diǎn)擊網(wǎng)頁(yè)時(shí)觸發(fā)并執(zhí)行
function EventClick(a,b){
console.log(
this.name, //JSLite.io
a, //p1
b //p2
)
}
// JSLite.io p1 p2
兼容
if (!Function.prototype.bind) {
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this, // this在這里指向的是目標(biāo)函數(shù)
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fNOP
? this //此時(shí)的this就是new出的obj
: oThis || this,//如果傳遞的oThis無(wú)效,就將fBound的調(diào)用者作為this
//將通過(guò)bind傳遞的參數(shù)和調(diào)用時(shí)傳遞的參數(shù)進(jìn)行合并,并作為最終的參數(shù)傳遞
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
//將目標(biāo)函數(shù)的原型對(duì)象拷貝到新函數(shù)中,因?yàn)槟繕?biāo)函數(shù)有可能被當(dāng)作構(gòu)造函數(shù)使用
fBound.prototype = new fNOP();
//返回fBond的引用,由外部按需調(diào)用
return fBound;
};
}
兼容例子來(lái)源于:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Compatibility
應(yīng)用場(chǎng)景:繼承
function Animal(name,weight){
this.name = name;
this.weight = weight;
}
function Cat(){
// 在call中將this作為thisArgs參數(shù)傳遞
// Animal方法中的this就指向了Cat中的this
// 所以Animal中的this指向的就是cat對(duì)象
// 在Animal中定義了name和weight屬性,就相當(dāng)于在cat中定義了這些屬性
// cat對(duì)象便擁有了Animal中定義的屬性,從而達(dá)到了繼承的目的
Animal.call(this,'cat','50');
//Animal.apply(this,['cat','50']);
this.say = function(){
console.log("I am " + this.name+",my weight is " + this.weight);
}
}
//當(dāng)通過(guò)new運(yùn)算符產(chǎn)生了cat時(shí),Cat中的this就指向了cat對(duì)象
var cat = new Cat();
cat.say();
//輸出=> I am cat,my weight is 50
原型擴(kuò)展
在原型函數(shù)上擴(kuò)展和自定義方法,從而不污染原生函數(shù)。例如:我們?cè)?Array 上擴(kuò)展一個(gè) forEach
function test(){
// 檢測(cè)arguments是否為Array的實(shí)例
console.log(
arguments instanceof Array, //false
Array.isArray(arguments) //false
);
// 判斷arguments是否有forEach方法
console.log(arguments.forEach);
// undefined
// 將數(shù)組中的forEach應(yīng)用到arguments上
Array.prototype.forEach.call(arguments,function(item){
console.log(item); // 1 2 3 4
});
}
test(1,2,3,4);
- 再談JavaScript中bind、call、apply三個(gè)方法的區(qū)別與使用方式
- 使用JS簡(jiǎn)單實(shí)現(xiàn)apply、call和bind方法的實(shí)例代碼
- JS 函數(shù)的 call、apply 及 bind 超詳細(xì)方法
- JavaScript函數(shù)之call、apply以及bind方法案例詳解
- Javascript中call,apply,bind方法的詳解與總結(jié)
- 實(shí)例講解JavaScript中call、apply、bind方法的異同
- 深入理解JavaScript中的call、apply、bind方法的區(qū)別
- JS中改變this指向的方法(call和apply、bind)
- js apply/call/caller/callee/bind使用方法與區(qū)別分析
- JavaScript手寫call,apply,bind方法
相關(guān)文章
簡(jiǎn)介JavaScript中Math.cos()余弦方法的使用
這篇文章主要介紹了簡(jiǎn)介JavaScript中Math.cos()余弦方法的使用,是JS入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-06-06
JS失效 提示HTML1114: (UNICODE 字節(jié)順序標(biāo)記)的代碼頁(yè) utf-8 覆蓋(META 標(biāo)記)的沖突的代
今天使用F12調(diào)試的時(shí)候提示HTML1114: (UNICODE 字節(jié)順序標(biāo)記)的代碼頁(yè) utf-8 覆蓋(META 標(biāo)記)的沖突的代碼頁(yè) utf-8,需要的朋友可以參考下2017-06-06
ASP小貼士/ASP Tips javascript tips可以當(dāng)桌面
今天看到《ASP小貼士/ASP Tips》 我也去把JavaScript的tips 下下來(lái)了。 看看是A4的。 自己把他改成1024 * 768 剛好可以用來(lái)做桌面2009-12-12
淺談javascript中關(guān)于日期和時(shí)間的基礎(chǔ)知識(shí)
下面小編就為大家?guī)?lái)一篇淺談javascript中關(guān)于日期和時(shí)間的基礎(chǔ)知識(shí)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-07-07
javascript中聲明函數(shù)的方法及調(diào)用函數(shù)的返回值
這篇文章主要介紹了javascript中聲明函數(shù)的方法及調(diào)用函數(shù)時(shí)的返回值,示例如下,不了解的方法可以參考下2014-07-07
Javascript 實(shí)現(xiàn)簡(jiǎn)單計(jì)算器實(shí)例代碼
這篇文章主要介紹了Javascript 實(shí)現(xiàn)簡(jiǎn)單計(jì)算器實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2016-10-10
JS的框架Polymer中的dom-if和is屬性使用說(shuō)明
這篇文章主要介紹了JS的框架Polymer中的dom-if和is屬性使用說(shuō)明,Polymer是Google開發(fā)的一款JavaScript框架,需要的朋友可以參考下2015-07-07
JavaScript面對(duì)國(guó)際化編程時(shí)的一些建議
這篇文章主要介紹了JavaScript面對(duì)國(guó)際化編程時(shí)的一些建議,包括時(shí)區(qū)與語(yǔ)言編碼等一些值得注意的問(wèn)題,需要的朋友可以參考下2015-06-06
JavaScript對(duì)內(nèi)存分配及管理機(jī)制詳細(xì)解析
本文主要講述了JavaScript的垃圾回收原理和具體的過(guò)程。需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2013-11-11

