阅读 3037
【笔试题👋】分享一道有意思的arguments笔试题

【笔试题👋】分享一道有意思的arguments笔试题

前言

你盼世界,我盼望你无bug。Hello 大家好!我是霖呆呆!

这篇文章很短...但绝对不是一篇水文...

主要是在评论区看到了,觉得是我们平常会忽略的一个小知识点而且笔试也有可能碰到,所以单独作为一篇文章来写,还请花个2分钟来看看吧,万一真给碰上了呢 😁。

(题目来源:掘友Lazzw,感谢该小伙伴的提出)

正题

题目是这样的:

var obj = {
age: 18,
foo: function (func) {
func()
arguments[0]()
}
}
var age = 10
function temp () {
console.log(this.age)
}
obj.foo(temp)
复制代码

不卖关子,咱先上答案:

10
undefined
复制代码

之前我们一直只在意函数中最常用的隐藏参数this,而这道题其实就考到了函数中隐藏的另一个参数arguments

先简单说明一下arguments的作用再来看这道题吧。

arguments参数是传递给函数的所有参数的集合(类数组结构),例如下面这两个例子:

function test1 () {
console.log(arguments)
}

function test2 (a) {
console.log(a)
console.log(arguments)
}

test1(1, 2)
test2(1, 2)
复制代码

执行结果为:

[1, 2]
1
[1, 2]
复制代码

可以看到,不管你的函数中有没有形参,arguments都会获取到你调用函数时传递的所有实参。

(而且arguments只存在于普通函数中,window全局下和箭头函数中都不存在)

另外我们知道,传递给函数的参数也可以是一个函数,那么只要是参数,arguments就可以捕获到,就算你是一个函数也逃不了,比如这样:

function foo () {
arguments[0]()
}
function temp () {
console.log(this)
}
foo(temp) // Arguments [ƒ temp()]
复制代码

如上题,向foo()中传递了temp函数,并且使用arguments[0]()调用它是可以执行的。

比较有意思的就是这里了,如果你是将temp当参数传递进去并且调用的话,就会发生隐式绑定丢失问题(类似《【建议👍】再来40道this面试题酸爽继续(1.2w字用手整理)》中的题目3.3),从而使函数内的this指向window

function foo (fn) {
fn()
}
function temp () {
console.log(this)
}
foo(temp) // Window
复制代码

而对于题目中的arguments[0]()

(非常抱歉之前对于arguments理解错了,以下是更正了之后的内容,再次感谢掘友Will__能更正我的错误)

而如果你即将调用的方法是在数组(类数组)中的,那此时调用函数内的this是调用的这个数组。

比如这里:

var test = [function () { return this }]
console.log(test[0]() === test) // true
复制代码

可以看到,数组的第一项是一个函数,并且返回this。而调用它的方式是test[0](),此时函数内的thistest这个数组是相等的。

那么我们是不是就可以理解为数组取下标就像是对象取属性一样,也就是谁最后调用的该方法,方法内的this就指向谁(在没有显式绑定以及函数是箭头函数的情况下)

所以回到原题中:

var obj = { 
age: 18,
foo: function (func) {
func()
arguments[0]()
}
}
var age = 10
function temp () {
console.log(this.age)
}
obj.foo(temp)
复制代码

题目解析:

  • 在使用obj.foo(temp)时,将temp函数当成了参数传递到foo中,这里我们知道func是发生了隐式绑定丢失的,所以导致第一次调用temp函数(也就是执行func()这段代码)的时候,temp内的this是指向window的,因此会打印出10
  • 而传入进去的temp会被arguments所搜集,所以可以使用arguments[0]()这样的形式调用。而arguments是一个类数组,刚刚已经提到了,数组或者类数组的调用下标就像是对象调用属性一样,因此此时temp内的this就是arguments了,而在arguments内是没有age这个属性的,所以会打印出undefined

为了验证这个想法💡,我们可以将题目改造一下:

var obj = { 
age: 18,
foo: function (func) {
func()
arguments.age = 66 // 新增代码1
arguments[0]()
}
}
var age = 10
function temp () {
console.log(this) // 新增代码2
console.log(this.age)
}
obj.foo(temp)
复制代码

我在题目中加了2行代码,一行是给arguments中添加了一个age属性,一行是将temp中的this打印出来。

现在来看看答案:

Window
10
Arguments [ƒ temp(), age: 66]
66
复制代码

果然执行结果和我们设想的一样,第二次调用temp时,tempthis的指向是arguments,并且我们给arguments里塞了个age,它也可以成功打印出来。

附加题

OK👌,霖呆呆最喜欢的事就是...

抢答时间!开始!

"买一送一"

没错啦,碰上了这道有意思的题你难道不想把它改造一下,增加增加难度吗?

[阴笑~]

上题!

var obj = {
age: 10,
foo: function (fn) {
fn.call(this)()
arguments[0]()()
arguments[0].call(this)()
arguments[0]().call(this)
}
}
var age = 20
function temp () {
return function () {
console.log(this.age)
}
}
obj.foo(temp)
复制代码

我在temp中返回了一个匿名函数,并且将foo()接收到的参数用不同的方式调用它。

题目解析:

  • fn.call(this)()是最简单的了,看过呆呆this那篇文章的小伙伴应该都知道,fn.call(this)()使用.call(this)改变的是temp第一层函数中的this,但是最终调用那个匿名函数的还是window,所以会打印出20。(类似于this篇中的题目4.9
  • arguments[0]()(),通过上面👆的正题我们知道arguments[0]()中,也就是temp函数的第一层指向的是arguments对象,而调用temp返回的那个匿名函数的也还是window,就像是fn.call(this)()一样,所以打印出的也还是20。(做这类题你就想着一个函数中返回另一个函数,如果没用call/apply进行显式绑定的话,调用它的都是window
  • arguments[0].call(this)()和第二个一样,它使用.call(this)改变的是temp函数的第一层this指向,而调用匿名函数的也还是window,因此还是打印出20
  • arguments[0]().call(this),这里使用.call(this)改变的就是匿名函数内的this指向了,将它绑定为foo函数内的this,而我们知道,此时foo函数内的this是指向的obj,因为调用foo的是obj,所以会打印出10

(以上👆提到的this篇链接为:《【建议👍】再来40道this面试题酸爽继续(1.2w字用手整理)》

后语

知识无价,支持原创。

参考文章:

你盼世界,我盼望你无bug。这篇文章就介绍到这里。

好像没什么问题嘛~

都是之前看到过的知识点 😁。

不过如果第一眼看着感觉做不来的小伙伴也别觉得自己不行🙅‍♂️,实话实话,就算是我自己写的文章不回头看的话也会忘掉 😂,所以我这不是经常搞搞有意思的题来帮我们一起巩固巩固嘛 😁,如果你都做的来的话,那很棒棒哦 👍。

(这个时间点写文章别和我老大举报我啊,我这是为了提高全中国前端工程师的JS水平)

"呸!臭不要脸!"

喜欢霖呆呆的小伙还希望可以关注霖呆呆的公众号 LinDaiDai 或者扫一扫下面的二维码👇👇👇.

我会不定时的更新一些前端方面的知识内容以及自己的原创文章🎉

你的鼓励就是我持续创作的主要动力 😊.

相关推荐:

《全网最详bpmn.js教材》

《建议改成: 读完这篇你还不懂Babel我给你寄口罩》

《你的掘金文章本可以这么炫(博客美化工具一波带走)》

《【建议星星】要就来45道Promise面试题一次爽到底(1.1w字用心整理)》

本文使用 mdnice 排版

文章分类
前端
文章标签