在JavaScript中,call
、apply
和bind
是Function
對象自帶的三個方法,本文將經過幾個場景的應用,來詳細理解三個方法。數組
call()
方法在使用一個指定的this值和若干個指定的參數值的前提下調用某個函數或方法。app
當調用一個函數時,能夠賦值一個不一樣的 this
對象。this
引用當前對象,即 call
方法的第一個參數。函數
經過 call
方法,你能夠在一個對象上借用另外一個對象上的方法,好比Object.prototype.toString.call([])
,就是一個Array
對象借用了Object
對象上的方法。ui
語法
fun.call(thisArg[, arg1[, arg2[, ...]]])
this
在fun函數運行時指定的this
值。須要注意的是下面幾種狀況spa
(1)不傳,或者傳null
,undefined
, 函數中的this
指向window對象
(2)傳遞另外一個函數的函數名,函數中的this
指向這個函數的引用,並不必定是該函數執行時真正的this
值 (3)值爲原始值(數字,字符串,布爾值)的this
會指向該原始值的自動包裝對象,如 String
、Number
、Boolean
(4)傳遞一個對象,函數中的this指向這個對象 prototype
指定的參數列表。code
初級應用例子 對象
function a(){
//輸出函數a中的this對象
console.log(this);
}
//定義函數b
function b(){}
var obj = {name:'這是一個屌絲'}; //定義對象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方法調用匿名函數而且指定上下文的this
在下面的例子中,當調用 greet
方法的時候,該方法的 this
值會綁定到 i
對象。
function greet() {
var reply = [this.person, '是一個輕量的', this.role].join(' ');
console.log(reply);
}
var i = {
person: 'JSLite.io', role: 'Javascript 庫。'
};
greet.call(i);
// JSLite.io 是一個輕量的 Javascript 庫。複製代碼
使用call方法調用匿名函數
在下例中的for
循環體內,咱們建立了一個匿名函數,而後經過調用該函數的call
方法,將每一個數組元素做爲指定的this
值執行了那個匿名函數。這個匿名函數的主要目的是給每一個數組元素對象添加一個print
方法,這個print方法能夠打印出各元素在數組中的正確索引號。固然,這裏不是必須得讓數組元素做爲this
值傳入那個匿名函數(普通參數就能夠),目的是爲了演示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方法調用函數傳參數
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 JSLite.io
//I'm function a!複製代碼
語法與 call()
方法的語法幾乎徹底相同,惟一的區別在於,apply的第二個參數必須是一個包含多個參數的數組(或類數組對象)。apply
的這個特性很重要,
在調用一個存在的函數時,你能夠爲其指定一個 this
對象。 this
指當前對象,也就是正在調用這個函數的對象。 使用 apply, 你能夠只寫一次這個方法而後在另外一個對象中繼承它,而不用在新對象中重複寫該方法。
語法:
fun.apply(thisArg[, argsArray])
注意: 須要注意:Chrome 14
以及 Internet Explorer 9
仍然不接受類數組對象。若是傳入類數組對象,它們會拋出異常。
thisArg
同上call
的thisArg
參數。
argsArray
一個數組或者類數組對象,其中的數組元素將做爲單獨的參數傳給 fun
函數。若是該參數的值爲null
或 undefined
,則表示不須要傳入任何參數。從ECMAScript 5
開始能夠使用類數組對象。
function jsy(x,y,z){
console.log(x,y,z);
}
jsy.apply(null,[1,2,3]);
// 1 2 3複製代碼
使用apply來連接構造器的例子
你能夠使用apply
來給一個對象連接構造器,相似於Java
. 在接下來的例子中咱們會建立一個叫作construct
的全局的Function
函數,來使你可以在構造器中使用一個類數組對象而非參數列表。
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和內置函數
聰明的apply用法容許你在某些原本須要寫成遍歷數組變量的任務中使用內建的函數。在接下里的例子中咱們會使用Math.max
/Math.min
來找出一個數組中的最大/最小
值。
//裏面有最大最小數字值的一個數組對象
var numbers = [5, 6, 2, 3, 7];
/* 使用 Math.min/Math.max 在 apply 中應用 */
var max = Math.max.apply(null, numbers);
// 通常狀況是用 Math.max(5, 6, ..) 或者 Math.max(numbers[0], ...) 來找最大值
var min = Math.min.apply(null, numbers);
//一般狀況咱們會這樣來找到數字的最大或者最小值
//比對上面的栗子,是否是下面的看起來沒有上面的舒服呢?
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];
}複製代碼
參數數組切塊後循環傳入
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() 函數會建立一個新函數(稱爲綁定函數)
語法
fun.bind(thisArg[, arg1[, arg2[, ...]]])
下面例子:當點擊網頁時,EventClick
被觸發執行,輸出JSLite.io p1 p2
, 說明EventClick
中的this
被bind
改變成了obj
對象。若是你將EventClick.bind(obj,'p1','p2')
變成 EventClick.call(obj,'p1','p2')
的話,頁面會直接輸出 JSLite.io p1 p2
var obj = {name:'JSLite.io'};
/** * 給document添加click事件監聽,並綁定EventClick函數 * 經過bind方法設置EventClick的this爲obj,並傳遞參數p1,p2 */
document.addEventListener('click',EventClick.bind(obj,'p1','p2'),false);
//當點擊網頁時觸發並執行
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在這裏指向的是目標函數
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fNOP
? this //此時的this就是new出的obj
: oThis || this,//若是傳遞的oThis無效,就將fBound的調用者做爲this
//將經過bind傳遞的參數和調用時傳遞的參數進行合併,並做爲最終的參數傳遞
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
//將目標函數的原型對象拷貝到新函數中,由於目標函數有可能被看成構造函數使用
fBound.prototype = new fNOP();
//返回fBond的引用,由外部按需調用
return fBound;
};
}複製代碼
兼容例子來源於:developer.mozilla.org/zh-CN/docs/…
function Animal(name,weight){
this.name = name;
this.weight = weight;
}
function Cat(){
// 在call中將this做爲thisArgs參數傳遞
// Animal方法中的this就指向了Cat中的this
// 因此Animal中的this指向的就是cat對象
// 在Animal中定義了name和weight屬性,就至關於在cat中定義了這些屬性
// cat對象便擁有了Animal中定義的屬性,從而達到了繼承的目的
Animal.call(this,'cat','50');
//Animal.apply(this,['cat','50']);
this.say = function(){
console.log("I am " + this.name+",my weight is " + this.weight);
}
}
//當經過new運算符產生了cat時,Cat中的this就指向了cat對象
var cat = new Cat();
cat.say();
//輸出=> I am cat,my weight is 50複製代碼
在原型函數上擴展和自定義方法,從而不污染原生函數。例如:咱們在 Array
上擴展一個 forEach
function test(){
// 檢測arguments是否爲Array的實例
console.log(
arguments instanceof Array, //false
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
});
}
test(1,2,3,4);複製代碼