call
MDN中的定义:
call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
先看看call方法如何使用:
function foo(age) {
console.log(this.name, age)
}
var obj = {
name: "faker"
}
foo.call(obj, 18) // faker 18
透过现象看本质,我们会发现:
- call在函数foo上调用,而foo是
Function的实例,它的所有实例都拥有这个方法,说明call存在于foo的原型(Function.prototype)中
- foo函数调用了,并且foo中的this指向了我们传入的对象obj,想一想,this要指向某个对象只有在我们以对象方法的形式调用(
o.fn())时才成立
- 参数的传递
根据我们的发现,写一个理想状态下(至少传入一个对象参数)的call的模拟实现:
Function.prototype.call1 = function(context) {
var args = []
var res
// this指向foo,foo函数成为了context的一个属性,所以context.fn()会使this指向context
context.fn = this
for (var i = 1; i < arguments.length; i++) {
// 为什么不直接args.push(arguments[i])?
args.push('arguments[' + i + ']')
}
// 之所以不args.push(arguments[i]),因为此步骤会发生类型转换,想一想当参数是引用类型会发生什么
res = eval('context.fn(' + args + ')') // 等同于context.fn(arguments[1], arguments[2])
// context和全局中的o引用同一个对象,context的修改也会在o中反映出来,所以我们在使用之后应该删除添加的方法
delete context.fn
return res
}
function foo(info) {
console.log(this.name, info)
}
let o = {
name: "faker"
}
foo.call1(o, 18) // faker 18
foo.call1(o, { age: 18 }) // faker { age: 18 }
apply
apply的作用和call一样,唯一不同的是传递的参数是一个数组,同样考虑理想状态下(至少传入一个对象)的代码:
Function.prototype.apply1 = function(context, arr) {
var args = []
var res
context.fn = this
for (var i = 0; i < (arr || []).length; i++) {
args.push('arr[' + i + ']')
}
res = eval('context.fn(' + args + ')')
delete context.fn
return res
}
function foo(info) {
console.log(this.name, info)
}
let o = {
name: "faker"
}
foo.apply1(o, [18]) // faker 18
foo.apply1(o, [{ age: 18 }]) // faker { age: 18 }
ES6实现call和apply
Function.prototype.call1 = function(context, ...args) {
context.fn = this
var res = context.fn(...args)
delete context.fn
return res
}
function foo(info) {
console.log(this.name, info)
}
let o = {
name: "faker"
}
foo.call1(o, 18) // faker 18
foo.call1(o, { age: 18 }) // faker { age: 18 }
Function.prototype.apply1 = function(context, args) {
context.fn = this
const res = context.fn(...args)
delete context.fn
return res
}
function foo(info) {
console.log(this.name, info)
}
const o = {
name: "faker"
}
foo.apply1(o, [18]) // faker 18
foo.apply1(o, [{ age: 18 }]) // faker { age: 18 }
有扩展运算符的加成确实简单很多
call
MDN中的定义:
先看看call方法如何使用:
透过现象看本质,我们会发现:
Function的实例,它的所有实例都拥有这个方法,说明call存在于foo的原型(Function.prototype)中o.fn())时才成立根据我们的发现,写一个理想状态下(至少传入一个对象参数)的call的模拟实现:
apply
apply的作用和call一样,唯一不同的是传递的参数是一个数组,同样考虑理想状态下(至少传入一个对象)的代码:
ES6实现call和apply
有扩展运算符的加成确实简单很多