在javascript中, call和apply都是为了改变某个函数运行时的上下文而存在的,换句话说,就是为了改变函数体内部的this的指向
javascript的一大特点是,函数存在[定义时上下文]和[运行时上下文]以及[上下文是可以改变的]这样的概念
function fruits() { }
fruits.prototype = {
color: 'red',
say: function () {
console.log('My color is ' + this.color)
}
}
var apple = new fruits
apple.say() // My color is red
但是如果我们有一个对象banana = { color: “yellow” }, 我们不想对它重新定义 say 方法,那么我们可以通过 call 或 apply 用 apple 的 say 方法:
var banana = {
color: 'yellow'
}
apple.say.call(banana) // My color is yellow
apple.say.apply(banana) // My color is yellow
所以, 可以看出 call 和 apply 是为了动态改变 this 而出现的, 当一个 object 没有某个方法( 本栗子中banana没有say方法), 但是其他的有( 本栗子中apple有say方法), 我们可以借助call或apply用其它对象的方法来操作。
apply、 call 区别
对于 apply、 call 二者而言, 作用完全一样, 只是接受参数的方式不太一样。apply接受一个数组或者一个类数组,而call接受一个个单独的参数。
例子:
1) 数组之间追加
var array1 = [12, 'foo', { name: 'joe' }, -123]
var array2 = ['Doe', 123, 100]
Array.prototype.push.apply(array1, array2)
console.log('array1: ', array1)
console.log('array2: ', array2)
2)获取数组中的最大值和最小值
var numbers = [1, 3, 4, 9, 45, 28]
var min = Math.min.apply(Math, numbers)
var max = Math.max.apply(Math, numbers)
console.log('最大值: ', max)
console.log('最小值: ', min)
3) 验证是否是数组
function isArray(obj) {
return Object.prototype.toString.call(obj) === '[object Array]'
}
4)写一个log方法来代替console.log
function log() {
return console.log.apply(console, arguments)
}
bind 绑定函数, bind()最简单的用法是创建一个函数,使这个函数不论怎么调用都有同样的this值。
num = 9
var mymodule = {
num: 81,
getNum: function () {
console.log(this.num)
}
}
mymodule.getNum()
var getNum = mymodule.getNum
getNum()
getNum.bind(mymodule)()
bind()方法与apply与call很相似,也是可以改变函数体内this的指向。
MDN的解释是:bind()方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入bind()方法的第一个参数作为this,传入bind()方法的第二个以及以后的参数加上绑定函数运行地本身的参数按照顺序作为原函数的参数来调用原函数
var foo = {
bar: 1,
eventBind: function () {
var _this = this
return function () {
console.log(_this.bar)
}
}
}
foo.eventBind()()
// 使用bind来解决
var foo = {
bar: 2,
eventBind: function () {
return function () {
console.log(this.bar)
}.bind(this)
}
}
foo.eventBind()()
偏函数
这是一个很好的特性,使用bind()我们设定函数的预定义参数,然后调用时候传入其他参数即可:
function list() {
return Array.prototype.slice.call(arguments)
}
var list1 = list(1, 2, 3) // [1, 2, 3]
var leadingList = list.bind(null, 10)
var list2 = leadingList(2, 3, 4)
console.log(list2)
function Bloomer() {
this.petalCount = Math.ceil(Math.random() * 12 ) + 1
}
Bloomer.prototype.bloom = function () {
setTimeout(this.declare.bind(this), 100);
}
Bloomer.prototype.declare = function () {
console.log(`我有${this.petalCount}朵花瓣`)
}
var bloo = new Bloomer()
bloo.bloom()
区别是,当你希望改变上下文环境之后并非立即执行,而是回调执行的时候,使用 bind() 方法。而 apply / call 则会立即执行函数。
再总结一下:
apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;
apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;
apply 、 call 、bind 三者都可以利用后续参数传参;
bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。
var obj = {
x: 81
}
var foo = {
getX: function () {
return this.x
}
}
console.log(foo.getX.apply(obj))
console.log(foo.getX.call(obj))
console.log(foo.getX.bind(obj)())