曾经忽略的arguments,重新捋一捋,看有没有新收获【玩转前端】

举报
叶一一 发表于 2023/02/15 19:37:50 2023/02/15
【摘要】 曾经忽略的arguments,重新捋一捋,看有没有新收获。

前言

众所周知,前端知识体系繁杂,需要掌握的内容瀚如星海。然而星河璀璨,学习和实践的过程很有趣,但是我自身体会,不知道为何有些知识点比较容易被忽略。

今日分享的arguments对象,就是曾经被我忽略的内容之一,当我再次遇见它,发现它的有趣之处时,我决定把它写下来。

它是一个对应于传递给函数的参数的类数组对象。什么情况下会用到它?怎么用?不着急,我们先从一道面试题说起。

一道看似简单的面试题

一道题

最早发现我忽略了它,是从一道面试题开始的。

var length = 10
function fn() {
  console.log(this.length)
}
var obj = {
  length: 5,
  method: function(fn) {
    arguments[0](); 
  }
}

obj.method(fn,1);

这道题的主要干扰是两处声明的length的变量,当你第一反应是全局的length变量,还是obj的属性length时,你已经离正确答案越来越远了。我们不妨先把正确答案打印出来,答案是2。

image.jpeg

一个答案

这个答案,熟悉的人不难,不熟的人会有点困惑。解题的关键点在于arguments对象。

arguments是一个类数组对象,它包含传递给函数的每个参数,可以通过索引取出函数的每一个参数,比如上面的题目我们打印一下arguments[0]。

var length = 10;
function fn() {
  console.log(this.length);
}
var obj = {
  length: 5,
  method: function (fn) {
    console.log(arguments[0]); // [Function: fn]
  },
};

obj.method(fn, 1);

arguments[0]打印出来是fn函数,也就是obj.method的第一个参数。arguments[0]()也就相当于fn(),而fn函数执行的结果是打印length,是谁的length属性,是全局变量length?还是obj的length?

很显然都不是,因为2≠10并且2≠5。这两个问句也正是我前面提到的干扰点,这题很巧妙的设置了一个迷雾阵,让人很容易忽略解题的关键点。

一种技巧

我们先打印一下this,会发现结果和直接打印arguments是一致的,所以此处的this指向的是arguments对象。我们知道,在函数中,this指向的是调用它的函数,所以此处调用的正是arguments。

var length = 10;
function fn() {
  console.log(this); // [Arguments] { '0': [Function: fn], '1': 1 }
  // console.log(this.length);
}
var obj = {
  length: 5,
  method: function (fn) {
    arguments[0]();
    console.log(arguments); //[Arguments] { '0': [Function: fn], '1': 1 }
  },
};

obj.method(fn, 1);

arguments的length属性指向传递给当前函数的参数数量。obj.method传递的参数有两个,所以arguments.length打印出来是2。

我们重新看这道题目,题目看似简单,但是可能没办法立刻想到结果是什么。这个时候,莫慌,试着将问题分解,将问题分解成知识点,思路也就清晰了。

面试题分析完,也就大致了解了arguments是什么以及怎么用了。


arguments对象

下面,我们来详细的了解一下arguments对象。以下内容大部分来自MDN

语法

arguments 是一个对应于传递给函数的参数的类数组对象它包含传递给函数的每个参数,可以根据索引值引用需要的参数,例如:

function fn () {
  console.log(arguments[0]) // arg1
  console.log(arguments[1]) // arg2
  console.log(arguments[2]) // arg3
}

fn('arg1','arg2','arg3')


属性

arguments.callee

指向参数所属的当前执行的函数。

function fn () {
  console.log(arguments.callee) // [Function: fn]
}

fn('arg1','arg2','arg3')


arguments.length

传递给函数的参数数量。

function fn () {
  console.log(arguments.length); // 3
}

fn('arg1','arg2','arg3');


arguments[@@iterator]

返回一个新的Array 迭代器 对象,该对象包含参数中每个索引的值。

function fn () {
  console.log(arguments[Symbol.iterator]); // [Function: values]
}

fn('arg1','arg2','arg3');


实用例子

通过arguments的属性,处理函数入参,尤其参数不定时,是很好用的。且可以通过控制默认参数和实参,达到我们想要的结果。

遍历参数求和

arguments是一个对应于传递给函数的参数的类数组对象,所以可以通过遍历它的所有值,进行求值操作。

function add() {
  var sum = 0,
    len = arguments.length;
  for (var i = 0; i < len; i++) {
    sum += arguments[i];
  }
  return sum;
}
add(); // 0
add(1); // 1
add(1, 2, 3, 4); // 10

定义连接字符串的函数

这个例子定义了一个函数来连接字符串。这个函数唯一正式声明了的参数是一个字符串,该参数指定一个字符作为衔接点来连接字符串。

可以传递任意数量的参数到该函数,并使用每个参数作为列表中的项创建列表。

function myConcat(separator) {
  var args = Array.prototype.slice.call(arguments, 1);
  return args.join(separator);
}

// returns "red, orange, blue"
myConcat(", ", "red", "orange", "blue");

// returns "elephant; giraffe; lion; cheetah"
myConcat("; ", "elephant", "giraffe", "lion", "cheetah");

// returns "sage. basil. oregano. pepper. parsley"
myConcat(". ", "sage", "basil", "oregano", "pepper", "parsley");


剩余参数、默认参数和解构赋值参数

arguments对象可以与剩余参数、默认参数和解构赋值参数结合使用。

在严格模式下,剩余参数、默认参数和解构赋值参数的存在不会改变 arguments对象的行为,但是在非严格模式下就有所不同了。

当非严格模式中的函数没有包含剩余参数、默认参数和解构赋值,那么arguments对象中的值会跟踪参数的值(反之亦然)。

// 没有包含剩余参数、默认参数和解构赋值,更新了arguments[0],同样更新了a
function func(a) {
  arguments[0] = 99;
  console.log(a);
}
func(10); // 99

// 没有包含剩余参数、默认参数和解构赋值,更新了a,同样更新了arguments[0]
function func(a) {
  a = 99;
  console.log(arguments[0]);
}
func(10); // 99

// 包含解构赋值参数,更新了arguments[0],没有更新a
function func(a = 55) {
  arguments[0] = 99; 
  console.log(a);
}
func(10); // 10

// 包含默认参数,更新了a的值,没有改变arguments[0]的值
function func(a = 55) {
  arguments[0] = 99; // updating arguments[0] does not also update a
  console.log(a);
}
func(10); // 10


总结

无论是面试题,还是日常工作中,我们时常会有这样的困惑,实际的结果和我们预想的结果不一致,于是我们回过头来梳理,我们到底遗漏了什么。在我过往的经验中,半数以上的此类情况,皆因我对某块知识的掌握的不全面,导致自己的代码结果出现偏差。

一花一世界,一叶一菩提。通过一个个知识点去梳理整个功能开发走向,每次将问题解决,我都会把知识点补全。因而也让我在后续的工作中,无论是开发还是日常解决问题,都是快速且高质的。

坚持做好一件事不容易,但是最终得到的收获却是超值的。

作者:非职业「传道授业解惑」的开发者叶一一
简介:「趣学前端」、「CSS畅想」系列作者,华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。
如果看完文章有所收获,欢迎点赞👍 | 收藏⭐️ | 留言📝。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。