-
在本章节中,我们会来进行手写显式调用的三个函数
-
在实现的过程中,我们既要知道如何实现,又需要知道为什么这么做,才能够在将来进行运用的时候有一定的思考空间
-
并且通过考虑为什么这么做,可以知晓后推出的bind函数方法,解决了之前call、apply的哪些痛点问题
-
-
在学习了这些内容后,我们会来了解前面章节所提到的arguments,到底是用来做什么的,以及为什么现在已经不再流行的原因
-
箭头函数中为什么没有arguments,为什么不加上?以及arguments如何进行使用,它到底是数组还是对象,我们都会探索到
-
一、手写apply、call、bind函数
实现apply、call、bind函数
-
注意:我们的实现是练习函数、this、调用关系,不会过度考虑一些边界情况
-
边界情况在实际需求之中是需要的, 未正确处理边界情况可能会导致程序崩溃、执行错误、数据损坏或安全漏洞。因为需要防止一些恶意用户和小白用户的错误使用,所以要对不规范不正确的内容进行处理,以此提高函数方法的通用性和健壮性,很多的代码和精力都不得不用在了防范上面。很多的边界情况还需要针对业务去专门考虑,而我们是以学习这些函数的思想为主,所以不需要考虑边界情况
-
-
真正实际的这三个函数是使用C++进行实现的,我们采用JS来进行模拟实现
-
在这里,我们先来复习一下三个函数的语法,方便我们等下的实现,尤其注意call和bind的差别
func.call(thisArg, arg1, arg2, ...)
func.apply(thisArg, [argsArray])
const newFunc = func.bind(thisArg, arg1, arg2, ...)
1.1. call函数的手写实现
-
给函数上面添加一个叫做hycall方法的方式:
-
Funtion.prototype.hycall = function(){},我们使用hycall来作为我们的函数实现,和call函数进行区分,防止覆盖问题
-
而这是通过原型链进行实现的,我们会在面向对象的部分学到原型链的核心知识。目前只需要先清楚,定义在函数原型链上的方法可以用在所有的函数身上
-
foo.hycall = function(){}
-
在之前我们的做法只能给某个函数添加方法,用于比较专一的场景下
-
而像call这类函数都是所有地方都可以调用的,相当于给所有的函数添加一个call的方法
-
Function.prototype.hycall = function(){
console.log("原型链调用了");
}
function foo(){
console.log("foo函数调用了");
}
foo.hycall()//原型链调用了
----------------------------------------------------------
//我们在执行foo.hycall的时候会发现只执行了hycall函数,但是却把原本foo函数自带的信息给掩盖掉了,这肯定是不合理的,我们是要在原有的基础上进行调用,而不是另起大厦
-
我们的目标对象就是foo函数,在这函数身上试验我们hycall的this改变效果
-
首先我们需要能够在调用
hycall函数
的时候,也能够执行foo函数,因为call函数能够做到这一步 -
这是因为
call
的设计初衷是允许我们立即调用一个函数(也被称为立即执行函数),如图10-1,并能够改变这个函数执行时的this
上下文,改变的this只会在该执行阶段生效,这是它的核心功能之一 -
直接控制函数的执行时机的设计方式,让我们在调试的时候不需要额外的调用一遍foo函数去检测this到底变没变,在foo的执行体身上可以直接打印一下this就能立马看到效果。在代码简洁度上有一定的提升,而且更灵活,用途也更明确
-
作为一个方法而言,是要执行步骤的,而绑定this是在执行时去顺便做到的,call是作为功能上的补充,而非独立。这也是我们为什么往往采用链式调用的原因,因为如果不进行使用该函数的话,this也不会被用到,而需要用到this的时候,也是调用该函数的时候,则这时候call函数将包揽这些作用,绑定后立刻执行函数,简化我们的步骤
function foo(){
console.log('foo被调用');
}
foo.call('我是小余')
图10-1 call调用会执行函数
-
我们在实现调用函数本身的时候,有两种方式
-
直接固定调用我们想要的函数
-
根据调用该方法的函数去针对调用
-
-
毫无疑问的,我们应该要选择第二种方式。第一种方式,我们只能调用foo函数,这样太死板了,相当于这hycall只能用在foo函数身上
-
而在调用hycall方法的时候,是属于隐式调用,此时的this就绑定到我们所需要的函数上了,所以我们可以利用这点使用方式2拿到this,直接调用执行
-
var fn = this
获取对应的函数上下文,fn()
执行其函数上下文。谁调用了hycall,this就指向于谁,也就执行于谁 -
虽然在这个情况下直接调用
this()
也是OK的,但将this
赋值给fn
对于代码的清晰性或者在复杂的情况下能避免关于this
的混淆,提高阅读性
-
//方式1:差的写法,缺乏复用性
Function.prototype.hycall = function(){
console.log("原型链调用了");
foo()
}
function foo(){
console.log("foo函数调用了");
}
foo.hycall()
//方式2:好的写法,可以多次复用
//本身我们在进行调用的时候,就相当于已经是隐式绑定了,foo.hycall()的时候,this的绑定就已经绑到foo上面了
Function.prototype.hycall = function(){
console.log("原型链调用了");
var fn = this
fn()
}
function foo(){
console.log("foo函数调用了");
}
foo.hycall()
1.2. 怎么实现给函数传递参数
而call方法的核心功能就是函数立即调用
以及改变this指向内容
-
我们已经能够拿到正常情况下的this了,实现了函数调用的效果
-
那如何来改变this指向效果呢?我们需要先解决一件事情,那就是怎么给hycall方法传递参数,因为改变this的指向效果,我们想改变成什么,取决于我们传递进来什么内容。需要先有参数内容,才有内容可以去指向。先有目标才能做出改变,人生也是如此
-
在我们这个案例中,我对
hyca
-