少侠,这多是关于 call 函数最好的文章~

image

阅读须知:这篇文章以前还有3篇相关的文章,若是少侠你没有阅读过的话,建议从第一篇开始看,否则可能会对某些地方比较困惑。es6

一、少侠,这多是目前为止关于this最好的文章~数组

二、少侠,prototype前传,了解一下?缓存

三、少侠, JS基础知识产生的蝴蝶效应能有多大?~函数


少侠们好~性能

又是一段时间没见了,测试

此次时间比较长,应该很想我吧,哈哈。this

最近肺炎疫情严重,但愿各位少侠都还安好,prototype

记得老老实实待在家,不要出去玩。3d

上次结束前给你们留了两个未完成的问题,cdn

image

少侠们都完成了没?

不过我估计,大多数少侠应该都是习惯性看完就关掉了,真正认真对待并动手的应该是极少数,另外还有少部分可能会等着此次看答案。

首先要给少侠们说一下,其实上次不是故意吊你们胃口,最后这两种状况,看着没有太大区别,可是却须要咱们掌握一个重要的知识,也就是:

若是我有一个带有 this 函数,如何彻底控制它被调用时的 this 指向?

image

少侠你可能会说,

天辰你最近都反复提到了,你将它放在哪一个对象上调用,它里面的 this 就指向它,因此咱们能够经过将它放在一个对象上调用。

image

好比这里的例子中,咱们将 fn 放在 obj1 上,而后调用 ob1.fn ,获得的结果就是 obj1 中的 name,obj2 也是同样的道理。

初识 call 函数

为了方便,

咱们能够先试着将这个过程单独放进一个函数里,

同时咱们给它取一个名字,叫作 call :

image

这个函数接受一个对象,和一个fn 函数,它会将 fn 函数放置在 obj 上调用,因此能够实现 fn 中的 this 指向 obj 的效果,而且返回最终的结果 result。

下面是它的使用方式

image

不过,目前的 call 函数,实际上会有反作用,调用 call 函数后,obj 中会多了一个 fn 属性,也就是咱们的 fn 函数,

而最开始的状况下,它是没有的。

image

因此,咱们得想办法清除掉这个反作用,

在这里,一些少侠的第一直觉想法多是,

完过后直接删除掉 obj 上的 fn 属性

image

若是少侠你是这样想的话,我只能说,

青涩!

若是 obj 上以前自己就有一个 fn 属性呢?

image

因此如今就比较尴尬,

不删除的话,咱们的 call 函数会额外增长一个 fn 属性,删除的话,又可能删除掉原先对象上自己就存在的 fn 属性。

怎么办呢?

有些少侠可能会说,

那能不能先检测一下,若是obj上有一个 fn 函数,咱们就临时放在另一个属性上,好比 fn2呢?

固然能够,

前提是你也必须保证 obj1 上没有一个叫作 fn2 的属性,否则你可能又得尝试 fn3,fn4, fn5。。。

看着就感受很 low !

“那我不能找个稀有的属性名称吗? 好比用一个很长的不容易冲突的字符串,好比 UUID。”

“对,或者就放 '天辰dreamer' 属性上,除了你,应该没有人会取这么奇怪的名称!”

image

呵呵,我代码中的变量名全是 XXXdreamer 的格式难道我会告诉你?

而相似 UUID 这种很长的随机字符串的话,

按照道理来讲几乎不可能冲突,可是,

万一少侠你是万中无一的随机天才呢?

image

因此!

更好的办法是什么呢?

更好的办法是将 obj 上原来的 fn 属性用一个变量保存好,事后再放回去:

image

这里,咱们利用了一个 savedFn 变量先缓存以前的 obj.fn,事后再根据 savedFn 是否存在决定是否要从新放回去。

image

好了,如今咱们的 call 函数比以前好多了,

可是,等等!!!

若是咱们的 fn 函数还须要参数怎么办?

image

因此咱们须要一种方式来经过 call 函数将参数传递到 fn 中。

好比,给 call 函数增长一个额外的参数:

image

好了,

这样是能够的,

咱们能够经过给 call 增长额外的参数来传递到 fn 内部,

可是!问题又来了,

这里咱们只增长了一个固定参数,

当咱们碰见其余函数时,它可能须要接受多个参数。

咱们如何知道 fn 函数会接受多少参数呢?

这个问题咱们能够先使用 ES6 的新语法,

展开运算符:

image

这样的话,咱们的 call 函数就基本成型了,

咱们来试一试:

image

少侠你能够看到,咱们的 addTitle 函数接受一个前缀和一个后缀,而后咱们经过 call 函数调用 将其中的 this 指向 user,并传递一个 🍄 当作前缀,一个 'dreamer' 当作后缀, 而后返回最终的字符串 "🍄天辰dreamer"

彻底ok~

少侠你可能会猜到我要说什么了。

没错!

你也不须要手动实现 call 函数, JS 中有一个现成的 call 函数,

点击这里查看JS中的call

全部的函数默认都有一个叫作 call 的方法,你能够直接使用它:

image

注意调用方式的区别:

image

好了,少侠你已经知道 JS 中的 call 函数是怎么一回事了,也知道了如何手动模拟一个相似的 call 函数。

通常来讲,文章到这里应该就要结束了,

可是,我若是到这里就结束了,有点对不起个人标题!

由于咱们确实还有一些问题没有考虑到,

好比:

展开运算符是 ES6 的语法,而系统自带的 call 函数是 ES5 的语法,能不能不用ES6 的语法实现它呢?

没错,展开运算符是 ES6 的语法,它时间上比 call 函数出现得更晚,也就是说,若是都能使用展开运算符的话,咱们也应该直接可使用 call 函数。

因此用 ES6 的语法实现 ES5 的语法等因而使用了将来的技术。

那么不用展开运算符怎么解决参数不肯定问题呢?

其实也不是很难,

好比。。。。

少侠你手动多写一些参数。。。。

写10个参数,应该够用了吧?

image

哈哈哈哈,没想到吧!

若是你问我碰见 10 个以上参数的函数怎么办?

100 个?

若是这样的话,

对不起,这么 low 的函数不配使用个人 call 函数!

“你本身的 call 函数还使用了 12 个参数呢!”

“就你话多!”

固然,实际上少侠你也能够从另外的层面解决问题,

好比若是函数是少侠你本身写的话,你能够选择全部函数都只采用一个对象来接受全部参数。

这样就只须要一个额外参数了,并且更方便,可读性也更高。

image

或者,若是实在要实现任意参数的话,

能够选择使用 eval 来动态解析代码:

JS中的eval

image

这个函数要稍微复杂一些,

并且可能也有一些须要少侠你额外了解的内容,

好比在函数内部能够经过 arguments[0] 获取第一个传递进来的参数,arguments[1] 获取第二个参数等等, 数组 [1,2] 转化成字符串后是 '1,2',以及 eval 能够动态解析代码等等。

若是少侠你不是很清楚的话,能够花一些时间额外了解一下。

并且,少侠你可能也听过,永远不要使用 eval 这句话,由于在 JS 中 使用 eval 是比较危险的事,并且性能也比较低。

因此, 10 个参数的函数其实也不是很坏,对吧?

好了,

在最后的 call 函数中,咱们没有使用任何 es6 新语法,所有换成了老的语法,同时也解决了不定参数的问题。

那么,还有漏掉的地方吗?

好比,

咱们目前实现的 call 函数,和系统自带的 call 函数,除了使用方式不同以外,还有什么区别没有?

能直接替代它吗?

不完美的 call 函数?

为了弄清楚这个问题,

咱们先对比看看咱们的 call 函数和系统内置的 call 函数:

* 内置的 call 函数 咱们的 call 函数
调用方式 对象方法方式调用 普通函数方式调用
会产生反作用吗 不会,不会无心中删除或覆盖obj对象原有属性 不会,不会无心中删除或覆盖obj对象原有属性
能不能接受任意参数 能够接受任意参数 能够实现接受任意参数

主要就上面几个关键的点,

少侠你能够看到,除了调用方式稍微有点区别之外,彷佛没有区别了。

到这里,

少侠你也许认为咱们的 call 函数和系统内置的 call 函数功能已经如出一辙了。

实际上,

上面最后的 call 函数,确实还算比较完善,

若是少侠你网上搜一下其余关于 call 函数的实现,可能还不必定比咱们这里实现的更好。

“装什么逼呢,我立刻就去搜一下。”

“我搜了一下,果真是这样,天辰你真牛逼!”

------ 一位口口声声说不是天辰小号的人说道。

好了,仍是继续认真说 call 函数的事。

真相是,咱们上面最后的 call 函数,看似没有什么问题了,可是在一些少见的边缘状况,仍是会有问题。

主要是咱们实现方式中的这一行代码:

image

这里咱们将 fn 函数赋值给了 obj 中,做为一个 fn 属性。

有什么问题呢?

问题就是,

事实上咱们不必定老是可以成功将 fn 函数赋值给 obj.fn 属性,也就是说,

有些状况下,给一个对象属性赋值可能会失败

好比,若是一个对象是冻结后的对象的话,咱们是不能给它添加任何属性的,也不能改变它的任何属性

image

点击查看JS中的Object.freeze()

这个时候,若是使用咱们的 call 函数的话,就会出错:

image
缘由就是,

咱们 call 函数内部试图向 obj 上添加 fn 属性,可是对于一个冻结对象来讲,这个过程会静默的失败,因此最后调用 obj.fn 时就会出错,

可是使用系统内置的 call 函数却不会有这个问题:

image

很惋惜吧?

好像就差一点。

少侠你可能会想,有没有什么办法,让咱们的 call 函数,也可以解决这个问题呢?

毕竟,咱们一步一步走了这么远,

有些事,在最后时刻放弃,可能比一开始就放弃更加遗憾。

若是少侠你生活中有这样的遗憾的话,

那么在 JS 的世界里,

是否会有这么一丝曙光,

让咱们的 call 函数再也不遗憾呢?

这个问题暂时留给少侠你当作练习题~

在下一次的故事中,咱们再一块儿想办法解决这个问题~

好了,

少侠,

江湖路远,有缘再见!


试炼山谷

若是少侠你看到了这里,

你应该也知道了,目前咱们的 call 函数比起系统的 call 函数仍是有一些差距的,

就能不能解决特殊对象好比冻结对象的话题,

下面有 4 个 dreamer 发表了他们的见解,

少侠你认为,哪一个会是最终的正确答案呢?

“我认为能够,可是也许须要使用到咱们目前还没碰见的技能,好比 prototype。”

———— 乌云dreamer

“嗯,应该能够,但也许咱们以前碰见的技能以及足够解决它了,好比 mixin。”

———— 凉风dreamer

“嗯。。。不肯定,不过看起来好像不能够呢。”

———— 秋月dreamer

“换其余人应该不能够,可是天辰dreamer必定能够!”

———— 不是天辰dreamer


小酒馆

实际上若是可使用将来语法的话,咱们也可使用 Symbol 来保证对象属性名称不冲突,

可是在最后咱们已经不使用将来语法了,因此也就不考虑这种方式了~

一些你可能关心的问题

一、为何能够用 savedFn 保存以前的 obj.fn? 在后面给 obj.fn 从新赋值时,savedFn 不会改变吗?

我想少侠你困惑的地方多是这种操做:

image

正确答案是会返回一个土豆,

若是你不知道为何的话,

请从新复习一下上一节的内容!

少侠, JS基础知识产生的蝴蝶效应能有多大?~

二、为何代码要用图片呢?

这个问题我隔一段时间要回答一次。。。

首先,我认为个人图片要好看一些,

而后就是若是少侠你是手机访问的话,直接放大查看图片比手动左右滑来滑去看代码要方便一些。

最后就是若是你想测试的话,亲自动手敲一遍代码比单纯地复制粘贴也要好一些!

一箭三雕!

三、上次留的坑还没解释呢,结果此次结尾又留一个坑?

哈哈,上次留的坑还没到填的时候,此次留坑是由于剩余内容加上的话,文章会过于长了,因此下次单独用一篇来讲。

我保证,下次,下次必定,必定先把此次的坑先填了!


声明:本文仅限于潇洒有趣又很酷的天辰dreamer装逼使用,转载请注明原做者和出处,商业转载请联系我(若是真有的话)。。。

image
相关文章
相关标签/搜索