- 都是用来改变 this指向的;第一个参数都是
this
要指向的对象,也就是想指定的上下文; - call和apply是直接执行函数。call的第二部分参数要一个一个传,apply要把这些参数放到数组中;
- bind 返回的是一个新的函数,你必须调用它才会被执行。
function Fruits() {}
Fruits.prototype = {
color: 'red',
getColor: function() {
return 'color is' + this.color
}
}
var apple = new Fruits()
apple.getColor() // color is red
var banana = {
color: 'yellow'
}
apply.getColor.call(banana) // color is yellow
apply.getColor.apply(banana) // color is yellow
// 所以,可以看出 call 和 apply 是为了动态改变 this 而出现的,当一个 object 没有某个方法(本栗子中banana没有say方法),
但是其他的有(本栗子中apple有say方法),我们可以借助call或apply用其它对象的方法来操作
// 实例:
var numbers = [5, 458 , 120 , -215]
Math.max.apply(null, numbers) // 458
Math.min.call(null, 5, 458 , 120 , -215) // -215
// 面试题
定义一个 log 方法,让它可以代理 console.log 方法,常见的解决方法是:
function log(){
console.log.apply(null, arguments)
}
log(1, 'www', '&&&') // 1 "www" "&&&"
// 进阶:开头加上(app)
function log(){
var args = Array.prototype.slice.call(arguments) // 需要将伪数组转化为标准数组
args.unshift('(app)')
console.log.apply(null, args)
}
log(1, '***') // (app) 1 ***
// --- bind:
var a = document.write
// this指向global或window对象
a('test') // Uncaught TypeError: Illegal invocation
a.bind(document)('test') // success
// 伪数组转化为标准数组
1、 Array.prototype.slice.call(arguments)
2、
var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.call.bind(slice)
slice(arguments) // arguments转换成了标准数组
// 多次 bind() 是无效的。更深层次的原因, bind() 的实现,相当于使用函数在内部包了一个 call / apply ,
第二次 bind() 相当于再包住第一次 bind() ,故第二次以后的 bind 是无法生效的。
代码实现
// call
Function.prototype.newCall = function (context, ...parameter) {
if (typeof context === 'object') {
context = context || window
} else {
context = Object.create(null)
}
let fn = Symbol()
context.fn = this
context.fn(...parameter)
delete context.fn
}
// apply
Function.prototype.newApply = function (context, parameter) {
if (typeof context === 'object') {
context = context || window
} else {
context = Object.create(null)
}
let fn = Symbol()
context.fn = this
context.fn(...parameter)
delete context.fn
}
// bind
Function.prototype.newBind = function (context, ...parameter) {
let me = this
return function(...argus) {
return me.call(context, ...parameter, ...argus)
}
}
// test
let person = {
name: 'xyz'
}
function say (age, height) {
console.log(this.name, age, height)
}
// test newCall
say.newCall(person, 25, 170) // xyz 25 170
// test newApply
say.newApply(person, [25, 170]) // xyz 25 170
// test newBind
let bindSay = say.newBind(person, 25)
bindSay(170) // xyz 25 170