(十五)call()方法及其实现

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

(⼗五)call()⽅法及其实现
1. call()⽅法
MDN给出的解释为: call()⽅法使⽤⼀个指定的this值和单独给出的⼀个或多个参数来调⽤⼀个函数。

function.call(thisArg, arg1, arg2, ...) , 其接受的参数如下:
第⼀个参数是 thisArg, 即:在function函数运⾏时使⽤的this值
arg1, arg2, ... 为指定的参数列表
也就是: 让 function执⾏, 让function中的this指向thisArg
⽰例
var obj = {
name: '猫',
age: 13,
}
function showInfo() {
console.log('我的个⼈信息是: ', + this.age);
}
showInfo() // 我的个⼈信息是: undefined
showInfo.call(obj) // 我的个⼈信息是: 猫13
个⼈对于call()⽅法的理解是: 它将showInfo(也就是函数)暂时的作为了obj的属性
showInfo.call(obj)
// 就相当于
var obj = {
name: '猫',
age: 13,
showInfo: function(){
console.log('我的个⼈信息是: ', + this.age);
}
}
obj.showInfo()
//只不过这个过程是⼀次性的
2. 利⽤原⽣js模拟call()⽅法
具体的思路就是: 根据上⾯对于call()⽅法的理解
将function作为obj的属性
让obj调⽤这个函数
调⽤完将这个属性删除
除了这些, 我们还应该考虑到像函数的返回值, 参数列表, thisArg的类型等问题
// 定义在Function的原型上
Function.prototype.myCall = function (context) {
// 1. 将function作为obj的属性
context.fn = this
// 2. 让obj调⽤这个函数
context.fn()
// 3. 调⽤完删除这个属性
delete context.fn
}
var obj = {
name: '猫',
age: 13,
}
function showInfo() {
console.log('我的个⼈信息是: ', + this.age);
}
showInfo.myCall(obj) // 我的个⼈信息是: 猫13
解释⼀下: context.fn = this
这⾥主要是this指向以及原型链的问题, 我们将myCall这个函数当作了Function.prototype的属性, ⽽Function.prototype⼜是所有函数的顶级构造函数, 为什么这么说 ? 因为对象的__proto__指向其构造函数的prototype, ⽽函数也是对象, 所以Function.__proto__ = Function.prototype
既然mycal是Function.prototype上的属性, 那么所有的函数都会共享这个属性, 我们刚刚也说了.函数也是对象, showInfo.myCall()是不是相当于⼀个对象在调⽤内部的⽅法, 那么当函数作为对象的⽅法被调⽤时, 谁调⽤, this就指向谁
我们可以在myCall内部来打印看⼀下是不是这样
果然, myCall内部的this是指向其调⽤的function, 这也就解释了为什么可以通过context.fn = this来完成将function作为obj的属性这⼀步
接下来解决参数与返回值的问题
// 定义在Function的原型上
Function.prototype.myCall = function (context) {
// 1. 将function作为obj的属性
context.fn = this
// 4. 参数与返回值问题
let args = [], res
for (let i = 1, l = arguments.length; i < l; i++) {
args.push(arguments[i])
}
// 2. 让obj调⽤这个函数, 并传递参数,利⽤...扩展运算符
res = context.fn(...args)
// 3. 调⽤完删除这个属性
delete context.fn
// 5. 将参数返回出去
return res
}
var obj = {
name: '猫',
age: 13,
}
function showInfo(num1, num2) {
console.log('我的个⼈信息是: ', + this.age);
return num1 + num2
}
var a = showInfo.myCall(obj, 10, 20) // 我的个⼈信息是: 猫13
console.log(a); // 30
最后就是对于thisArg类型的问题, 对于像基本类型以及其中两个特殊的类型null和undefined的处理
终极实现⽅案
Function.prototype.myCall = function (context) {
// 6.判断 context 的类型
if (typeof context !== 'object') {
// 如果不是对象类型, 就创建⼀个空对象
context = Object.create(null)
} else {
// 如果是null或不传则返回window, 否则返回其本⾝
context = context || window
}
// 1. 将function作为obj的属性
context.fn = this
// 4. 参数与返回值问题
let args = [], res
for (let i = 1, l = arguments.length; i < l; i++) {
args.push(arguments[i])
}
// 2. 让obj调⽤这个函数, 并传递参数
res = context.fn(...args)
// 3. 调⽤完删除这个属性
delete context.fn
// 5. 将参数返回除去
return res
}
var obj = {
name: '猫',
age: 13,
}
function showInfo(num1, num2) {
console.log('我的个⼈信息是: ', + this.age);
return num1 + num2
}
var a = showInfo.myCall(obj, 10, 20) // 我的个⼈信息是: 猫13
console.log(a); // 30
showInfo.myCall(100) // 我的个⼈信息是: NaN
3. 关于call()的经典⾯试题
function fn1() { console.log(1) }
function fn2() { console.log(2) }
fn1.call(fn2)
fn1.call.call(fn2)
Function.prototype.call(fn1)
Function.prototype.call.call(fn1)
我们先把实现的myCall()⽅法拿过来, 对于该题⽬⽤不到的代码进⾏移除掉了, 以免混淆
还有⼀句重要的话: 当函数作为对象的⽅法被调⽤时, 谁调⽤, this就指向谁哪⾥不明⽩就来看这⼀句话Function.prototype.call = function (context) {
if (typeof context !== 'object') {
context = Object.create(null)
} else {
context = context || window
}
// 核⼼代码
context.fn = this
context.fn()
}
好, 接下来我们就以⾃⼰实现的call()⽅法来对上⾯的题⽬进⾏分析, 所有的分析都是在call()⽅法内部进⾏的fn1.call(fn2):
context = fn2
context.fn = this => fn2.fn = fn1
context.fn() => fn2.fn() => fn1() // 最后fn1执⾏, 输出 1
fn1.call.call(fn2):
⾸先, 要明⽩是哪个函数在执⾏, 谁调⽤的
很明显, 是粉⾊部分fn1.call.call(fn2)在执⾏, fn1.call.call(fn2) 在调⽤
context = fn2
context.fn = this => fn2.fn = fn1.call
context.fn() => fn2.fn() => fn2.fn1.call()
此时会再次执⾏fn1.call(), 调⽤者是 fn2
因为这次没有传this指向, 第⼀个参数为空, 会⾛else判断
context = window
context.fn = this => window.fn = fn2
context.fn() => window.fn2() // 最终fn2执⾏, 输出2
Function.prototype.call(fn1):
context = fn1
context.fn = this => fn1.fn = Function.prototype
context.fn() => fn1.fn() => fn1.Function.prototype()
此时执⾏ fn1.Function.prototype() 调⽤者是fn1 this指向fn1
执⾏Function.prototype() // 它是⼀个空的顶级构造函数, 最终什么也不会输出
Function.prototype.call.call(fn1):
context = fn1
context.fn = this => fn1.fn = Function.prototype.call
context.fn() => fn1.fn() => fn1.Function.prototype.call()
Function.prototype.call()执⾏, 调⽤者是fn1 this指向fn1
context = window
context.fn = this => window.fn = fn1
context.fn() => window.fn() => windown.fn1() // 最终fn1执⾏, 输出 1
总结:
当只有⼀个call时, 执⾏call左边调⽤它的函数
当有两个或两个以上的call时, 执⾏call的参数函数。

相关文档
最新文档