call,apply and bind in JavaScript
在ECMAScript中,每一个函数都包括两个继续而来的要领:apply() 和 call(),这两个要领的用处都是在特定的作用域中挪用函数,重要作用跟bind一样,用来转变函数体内this的指向,或许说是在函数挪用时转变上下文。
文章只管运用大批实例举行解说,它们的运用场景。同时,也会由浅入深的引导出一些理论,毕竟这几个经常使用要领,在MDN上都能找到合理的诠释
基本功用
转变this的指向
var fruit = {
fruitName:"apple"
}
function getFruit() {
console.log("I like "+this.fruitName)
}
getFruit(); // log I like undefined
getFruit.call(fruit) // log I like apple
getFruit.apply(fruit) // log I like apple
var newBind = getFruit.bind(fruit)
newBind(); // log I like apple
当 getFruit 并不是作为一个对象的属性,而是直接当作一个函数来挪用,内里的this
就会被绑定到全局对象上,即window上, 所以直接挪用 getFruit
,内里的this
指向了全局对象上,返回 undefined
。
在严厉情势下,函数被挪用后,内里的this默许是 undefined
背面,经由过程挪用函数上的call
和apply
要领,该变this
指向,函数内里的this
指向fruit
。
区分:bind
一样完成了转变this
指向的功用,然则它不会马上实行,而是会从新建立一个绑定函数,新函数被挪用时,运用bind()
要领内里的第一个参数作为this
接收参数
这三个要领,从接收的第二参数最先,都直接通报给函数,然则接收参数的要领却很大的差别。
call,从第二个参数最先,以参数列表的情势展现,
apply,则把通报的函数参数,放在一个数组内里作为第二个参数。
fn.call(obj,arg1,arg2);
fn.apply(obj,[arg1,arg2])
bind,从第二个参数最先,一样以参数列表的情势,然则会提早放在新绑定函数的参数之前
var foo = function(name,age){
console.log("name: "+name+"- age: "+age)
}
var p1 = foo.bind(this,"popo"); // "popo" 作为新函数的第一个参数。
p1(13); // logs name: popo- age: 13
p1("bobo",14) // logs name: popo- age: bobo
运用场景
绑定事宜回调中
$('.div-class').on('click',function(event) { /*TODO*/ }.bind(this)); } }
一般,我们在转变函数上下文之前,都邑运用相似
that = this
,或许self,_this
,来把this赋值给一个变量。应用.bind()
,能够传入外层的上下文。轮回回调
轮回中应用闭包来处置惩罚回调
for(var i = 0;i < 10;i++){ (function(j){ setTimeout(function(){ console.log(j); },600); })(i) }
每次轮回,都邑发生一个马上实行的函数,函数内部的局部变量j保留差别时代i的值,轮回过程当中,setTimeout回调按递次放入音讯行列中,等for轮回完毕后,客栈中没有同步的代码,就去音讯行列中,实行对应的回调,打印出j的值。
同理,能够应用
bind
,每次都建立新的函数,而且已预先设置了参数,传入差别的指针function func(i) { console.log(i) } for(var i =0 ;i< 10;i++) { setTimeout(func.bind(null,i),600) }
完成继续
var Person = function(name,age) { this.name = name; this.age = age; } var P1 = function(name,age) { // 借用组织函数的体式格局完成继续 // 应用call 继续了Person Person.call(this,name,age) } P1.prototype.getName = function() { console.log("name: "+this.name+", age: "+this.age); } var newPerson = new P1("popo",20); // logs name: popo, age: 20 newPerson.getName();
实质上,能够算作经由过程
call()
或许apply()
要领,在行将新建的对象,即这里的newPerson
上,实行超范例的组织函数,离别在当前上下文this
上增加name
和age
属性。数组考证的最终要领
function isArray(value) { return Object.prototype.toString.call(value) == "[object Array]" }
借用了Object原生的toString()要领,打印出对应变量的组织函数名,
类数组转换为数组
// 完成一个简朴的数组 'unshift'要领 Array.prototype.unshift = function(){ this.splice.apply(this, [0,0].concat(Array.prototype.slice.apply(arguments))); return this.length; }
起首,应用
this.splice.apply()
,个中splice
,能够直接从数组中移除或许插进去变量。apply()
则以数组的情势通报参数,须要应用concat
拼接数组。当函数被挪用时,在函数内部会获得类数组
arguments
,它具有一个length属性,然则没有任何数组的要领。所以,将slice
要领中的this
指向arguments
,获取到arguments
的长度,从而肯定要领的start
和end
下标,获得一个数组变量。一样实用的另有,DOM内里的NodeList对象,它也是一品种数组对象。
深切明白
完成bind 要领
bind
要领在ECMAScript5内里被引入,前面提到过,挪用该要领时,返回一个新的函数,能够简朴运用下面要领完成其转变this
指向的功用。
Function.prototype.bind = function(scope) {
var fn = this;
return function() {
return fn.apply(scope)
}
}
接着,就能够应用concat
把bind通报的预置参数拼接到新函数的参数列表中。
Function.prototype.bind = function(scope) {
var args = Array.prototype.slice.call(arguments,1)
var fn = this
return function() {
return fn.apply(scope,args.concat(Array.prototype.slice.call(arguments)))
}
}
参考链接