Arguments对象

Arguments 对象

arguments 基本定义

首先arguments是以内置对象出现的。换句话说:你不能够直接的去访问arguments对象,所以你会返现在浏览器中直接访问arguments对象是不存在的。
image.png
特别重要:
那么arguments对象本质上是什么东西呢?其实arguments是一个对应于传递给函数参数的类数组对象,arguments对象是所有(非箭头)函数中都可用的局部变量**。为什么箭头函数中不存在arguments对象呢?这个问题,我们等会再去讨论。
我们通过console.dir(Function.prototype)的方式发现arguments属性存在于Function.prototype对象上。为什么我们不能够直接通过Function.arguments的方式进行访问arguments呢?因为我们说过,arguments对象是内置对象,所以你不能够直接去调用访问。
image.png
那么arguments对象本质上是什么东西呢?其实arguments是一个对应于传递给函数参数的类数组对象,arguments对象是所有(非箭头)函数中都可用的局部变量
。**为什么箭头函数中不存在arguments对象呢?这个问题,我们等会再去讨论。
我们通过console.dir(Function.prototype)的方式发现arguments属性存在于Function.prototype对象上。为什么我们不能够直接通过Function.arguments的方式进行访问arguments呢?因为我们说过,arguments对象是内置对象,所以你不能够直接去调用访问。
image.png
如果说,我想要访问arguments的话,我该如何操作呢?因为arguments是所有(非箭头)函数中的局部变量,所以在函数内部中可以通过arguments直接对其访问。例如下面的例子:
我们终于揭开了arguments对象的面纱,现在来分析一下arguments对象内部的属性:

function fn() {console.log(arguments);
};
fn(1, 2, 3);

image.png

  1. 先看arguments对象的形式,arguments对象很显然是类数组对象Array-like。为什么说arguments对象是类数组对象呢?首先arguments对象存在length属性,其次arguments对象存在有顺序依据的属性0,1,2,最后arguments对象本质上并不是数组对象。因为arguments[[prototype]]属性是由Object构造器构造出来的对象。
  2. callee属性:callee属性其实也比较简单,这个callee属性会指向宿主函数。什么是宿主函数呢?也就是指代当前正在执行的函数。比如说下面例子中,通过arguments.callee能够访问到当前function fn,其实我们也可以直接通过fn函数名称进行访问。为什么ES5严格模式要移除callee属性,这个问题我们也等会解释。
function fn() {console.log(arguments.callee); // function fn(){}console.log(fn); // function fn(){}
}
fn();
  1. Symbol.iterator属性:出现Symbol.iterator属性的话,说明该数据是可以迭代的。比如说:我通过generator生成器函数手动的对arguments对象进行迭代,我也可以用for...of语句直接对其arguments对象进行迭代。
function fn() {const iterator = generator(arguments);console.log(iterator.next()); // { value: 1, done: false }console.log(iterator.next()); // { value: 2, done: false }console.log(iterator.next()); // { value: 3, done: false }console.log(iterator.next()); // { value: undefined, done: true }
};
fn(1, 2, 3);
// 生成器函数
function * generator(args) {for (let i = 0; i < args.length; i++) {yield args[i];}
}
function fn() {for (let value of arguments) {console.log(value); // 1, 2, 3}
};
fn(1, 2, 3);
特别重要:

分析完arguments对象的属性之后,我们来证明一下arguments对象是不是数组形式的。我们分别通过Array.isArray方法、toString方法去证明arguments不是数组的形式。所以arguments对象是不可以直接继承到Array.prototype上的方法。

// 方式一
function fn() {console.log(Array.isArray(arguments)); // false
}
fn(1, 2, 3);// 方式二
function fn() {console.log(arguments.toString()); // [object Arguments]
}
fn(1, 2, 3);
为什么箭头函数中不存在arguments

如果我们在箭头函数中访问arguments对象,那么此时程序会抛出异常:Uncaught ReferenceError: arguments is not defined。这说明什么问题呢?说明箭头函数中是不存在arguments对象的。为什么箭头函数中不存在arguments对象呢?如果说我想在箭头函数中获取实际参数列表,我又该如何去操作呢?
首先如果你想在箭头函数中获取实际参数列表的话,此时你可以通过ES6的剩余语法来处理,例如:你会发现args变量此时是数组的形式,并且数组内部的元素对应实际参数的值。这说明什么问题呢?这说明箭头函数中剩余语法代替了原本的arguments对象。
image.png

const fn = (...args) => {console.log(args);
}
fn(1, 2, 3);

ES6中为什么有一个argumentsargs的演变过程呢?其实也很简单,我们先从arguments中分析,首先arguments对象保存的是实际参数列表,我们通过拿到实际参数列表之后一般都是要将arguments对象转为数组,然后再去对数组进行操作。而现在args本身就是数组的形式,所以能够直接调用Array.prototype上的方法。
其次是arguments.callee属性,为什么args中不存在arguments.callee属性呢?因为callee属性指向当前正在执行的函数,而我们完全可以通过调用函数名称的方式去直接获取函数。所以此时callee属性存在与不存在的意义并不是很大。当然之后,我们会说为什么arguments.calleeES5严格模式下被移除的原因。
最后是arguments[Symbol.iterator]属性,因为arguments对象存在Symbol.iterator属性,所以arguments对象可以进行迭代。但是需要注意,args数组虽然自身不存在Symbol.iterator属性,但是Array.prototype对象是存在Symbol.iterator属性,所以args自然也可以进行迭代操作。所以在箭头函数中,arguments对象的代替方式是通过剩余语法的方式。
上面的原因只是针对于argumentsargs演变的过程总结的原因。但是最重要的原因在于形式参数与实际参数对应的关系,这点我们在接下来重点叙述。

arguments 对象转为数组的问题

说到arguments对象转为数组的问题,可能我们一下子能够想到很多种。但是针对于ES5来说,我们通过使用的是下面的方式:
我们通过[].slice.call(arguments)的方式,将arguments对象转换为数组的形式。因为slice方法能够返回一个新数组,而我们改变slice方法内部的this指向。使arguments对象从类数组对象转换为数组对象。
这是ES5中比较常见的arguments对象转为数组方式,但是这种方式对于V8引擎优化非常不友好。在这篇文档中(Optimization-killer),说明了针对于V8引擎优化不友好的方式,其中下面的这种方式就囊括其中。

function fn() {const argArr = [].slice.call(arguments);console.log(argArr); // [1, 2, 3]
};
fn(1, 2, 3);

当然MDN上也指出了解决的方式:

function fn() {var args = (arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments));console.log(args); // [1, 2, 3]
};
fn(1, 2, 3);

形式参数与实际参数的对应关系、arguments对象的行为

ES5

我们在ES5学习函数的时候,我们知道函数实际参数与形式参数一一对应,比如说:
调用fn(1, 2, 3)函数,1,2,3fn函数调用的时候作为实际参数传入到函数内部。而a,b,c作为形式参数来与其对应。也就是说,现在形式参数a,b,c相当于fn函数内部的临时变量,而a,b,c变量内部存储的值与实际参数一一对应,所以a,b,c的值分别是1,2,3

function fn(a, b, c) {console.log(a, b, c); // 1, 2, 3
};
fn(1, 2, 3);

形式参数与实际参数的对应关系很好理解,我们下面来看看比较奇怪的arguments对象行为:
arguments对象表示的是实际参数列表,所以打印arguments对象内部的元素是1,2,3

function fn(a, b, c) {console.log(a, b, c); // 1, 2, 3console.log(arguments); // Arguments[1, 2, 3]
};
fn(1, 2, 3);

如果说我们现在,更改形式参数变量a的值,那么arguments对象内部的元素会受影响吗?
实际上是会发生变化的,我们可以看到此时arguments对象内部的元素变为了100,2,3。这是什么原因导致的呢?不是说arguments对象是实际参数列表吗?arguments对象内部的元素不应该是1,2,3吗?
特别重要:其实这是由于形式参数与arguments在内存中存在映射关系,实际上会使形式参数与arguments对象的元素产生对应关系(共享关系)。注意一下,映射关系是指在内存中,比如a映射b``a <=> b,此时a、b的值可以不相等。而对应关系是a对应b``a <-> b,此时a、b值是相等的。
其实也就是说,arguments对象内部元素与形式参数存在对应关系,这也是arguments对象的行为。所以当形式参数a发生变化的时候,arguments对象中对应的元素也会发生变化。

function fn(a, b, c) {a = 100;console.log(arguments); // Arguments[100, 2, 3]
};
fn(1, 2, 3);

当然,如果你改变arguments对象内部的值,此时对应的形式参数也会发生变化。这就是函数内部形式参数与arguments对象的特殊关系。

function fn(a, b, c) {arguments[0] = 100;console.log(a); // 100
};
fn(1, 2, 3);
ES6

如果arguments对象遇见某些ES6的语法时,此时arguments会被弱化。换句话说,在ES6中,不希望你使用arguments对象,而是利用剩余语法进行代替。比如说:
下面的两个例子中存在ES6中的函数参数默认值的语法,此时我们会发现形式参数与arguments之间对应关系似乎消失了。当我们手动去修改形式参数a的值,或者是手动修改arguments对象的元素的时候,与其对应的形式参数或者是arguments内部元素并不会与其对应。换句话说:当在默认参数的存在,导致arguments对象中的元素并不会再与形式参数的值对应。

function fn(a = 1, b) {a = 100;console.log(arguments); // Arguments[1, 2, 3]
};
fn(1, 2, 3);function fn(a = 1, b) {arguments[0] = 100;console.log(a); // 1
}
fn(1, 2, 3);

通过下面的几个例子:我们发现在非严格模式下ES6的剩余参数、默认参数、解构赋值语法存在时,arguments对象中的值是不会追踪形式参数的值。也就是说arguments对象内部的值是不会对应形式参数的值。注意是非严格模式,严格模式我们之后再进行讨论。
为什么ES6会出现这种现象呢?其实这和arguments对象有关系,之前我们在ES5讨论arguments对象的时候。我们发现arguments对象是存在与形式参数对应的行为,但是这种行为很怪异。arguments对象本质上就是保存实际参数的类数组对象,而更改形式参数的值或者是手动修改arguments对象中的值,都会影响到与之对应的形式参数或者是arguments对象。
而这种行为伴随着剩余参数、默认参数、解构赋值语法出现,ES6在慢慢的弱化arguments对象的能力,我们在下面剩余参数、默认参数、解构赋值的例子中能够很明显的感受到。尤其是剩余参数的出现,如果函数接收不定数量的参数时,我们会发现形式参数已经完全的列表化,形式参数通过...的语法进行收集,形式参数在函数内部中显得及其重要,并且arguments对象的行为也失去了效果。所以在ES6中,它并不希望你使用arguments对象,而是推荐你使用剩余参数的方式去代替arguments

// ES5
function fn(a, b, c) {arguments[0] = 100;arguments[1] = 200;arguments[2] = 300;console.log(a, arguments[0]); // 100 100console.log(b, arguments[1]); // 200 200console.log(c, arguments[2]); // 300 300
}
fn(1, 2, 3);// ES6参数默认值
function fn(a, b, c = 1) {arguments[0] = 100;arguments[1] = 200;arguments[2] = 300;console.log(a, arguments[0]); // 1 100console.log(b, arguments[1]); // 2 200console.log(c, arguments[2]); // 3 300
}
fn(1, 2, 3);// ES6剩余参数语法
function fn(...args) {arguments[0] = 100;arguments[1] = 200;arguments[2] = 300;console.log(args[0], arguments[0]); // 1 100console.log(args[1], arguments[1]); // 2 200console.log(args[2], arguments[2]); // 3 300
};
fn(1, 2, 3);// ES6对象化解构
function fn({ a, b, c }) {arguments[0] = 100;arguments[1] = 200;arguments[2] = 300;console.log(a, arguments[0]); // 1 100console.log(b, arguments[1]); // 2 200console.log(c, arguments[2]); // 3 300
};
fn({a:1,b:2,c:3
});

ES5 严格模式中的arguments

我们上面讨论的例子都是处于非严格模式下的情况,现在我们探讨一下如果在严格模式下的话,arguments对象与形式参数之间的关系会不会受到影响。
观察下面的例子,我们发现处于严格模式下的话,arguments对象内部的元素并不会与形式参数对应,实际上的效果与ES6相同。其实这真的很好理解,arguments本质上就是保存实际参数的,你更改形式参数的值,本来就不应该去影响arguments对象内部的值。并且如果我手动修改arguments对象内部的值也是不应该去修改形式参数的值才对。
所以我们发现在严格模式下,arguments对象失去了原本上对应的行为能力。而在非严格模式下,arguments对象依旧保持着原本的对应行为能力。
特别重要:严格模式下,不仅仅移除了arguments对象的对应能力,还将arguments对象中的callee、caller属性一并移除,在严格模式下是不能够使用callee、caller属性。

// 非严格模式
function fn(a, b, c) {a = 10;b = 20;c = 30;console.log(a, arguments[0]); // 10 10console.log(b, arguments[1]); // 20 20console.log(c, arguments[2]); // 30 30
};
fn(1, 2, 3);// 严格模式
function fn(a, b, c) {'use strict';a = 10;b = 20;c = 30;console.log(a, arguments[0]); // 10 1console.log(b, arguments[1]); // 20 2console.log(c, arguments[2]); // 30 3
};
fn(1, 2, 3);

那么为什么arguments.calleeES5严格模式中删除了呢?
原因:在早期版本的javascript不允许使用命名函数表达式,处于这样的原因,你不能创建一个递归函数表达式:

function factorial (n) {return !(n > 1) ? 1 : factorial(n - 1) * n;
}[1,2,3,4,5].map(factorial);
[1,2,3,4,5].map(function (n) {return !(n > 1) ? 1 : /* what goes here? */ (n - 1) * n;
});

针对于不可行的方式,为了解决这个问题,arguments.callee添加进来以后。然后你就可以这样去做:

[1,2,3,4,5].map(function (n) {return !(n > 1) ? 1 : arguments.callee(n - 1) * n;
});

然而,这实际上是一个非常糟糕的解决方案,因为这(以及其它的argumentscalleecaller)使得在通常的情况(你可以通过调试一些个别的例子去实现它,但即使最好的代码也是次优选择,因为JS引擎做了不必要的解释)不可能实现内联和尾递归。另外一个原因是递归调用会获取到一个不同的this值。

var global = this;var sillyFunction = function (recursed) {if (!recursed) { return arguments.callee(true); }if (this !== global) {alert("This is: " + this);} else {alert("This is the global");}
}sillyFunction();

ECMAScript3通过允许命名函数表达式解决这些问题。例如:

[1,2,3,4,5].map(function factorial (n) {return !(n > 1) ? 1 : factorial(n-1)*n;
});

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/158107.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java零基础——Spring篇

1.Spring框架的介绍 1.1 传统的项目的架构 在传统的项目中&#xff0c;一般遵循MVC开发模型。 (1) view层与用户进行交互&#xff0c;显示数据或者将数据传输给view层。 (2) 在controller层创建service层对象&#xff0c;调用service层中业务方法。 (3) 在service层创建dao…

Linux docker安装RStudio Server结合内网穿透实现公网访问内网服务

&#x1f4f7; 江池俊&#xff1a; 个人主页 &#x1f525;个人专栏&#xff1a; ✅数据结构探索 ✅cpolar &#x1f305; 有航道的人&#xff0c;再渺小也不会迷途。 文章目录 前言1. 安装RStudio Server2. 本地访问3. Linux 安装cpolar4. 配置RStudio server公网访问地址5…

kibana 7安装

手动安装 下载 wget https://artifacts.elastic.co/downloads/kibana/kibana-7.17.15-linux-x86_64.tar.gz 解压 mv kibana-7.17.15-linux-x86_64.tar.gz /usr/local tar -zxvf kibana-7.17.15-linux-x86_64.tar.gz chown -R es:es kibana-7.17.15-linux-x86_64修改配置 s…

解决VSCode运行时自动保存问题【图文解析】

用VSCode写前端时老是自动保存&#xff0c;代码还没写完就开始 刷新页面 调用接口 出现报错之类的&#xff0c;很烦人&#xff0c;所以就写一篇修改VSCode自动保存文件的文章&#xff0c;以免自己忘记在哪设置。 同事总是用不自动保存&#xff0c;每次写完都要ctrls一下&#x…

电脑文件夹加密怎么操作?保护数据4个方法分享!

“大家平常是怎么给电脑文件夹加密的呀&#xff1f;我有一些比较重要的数据&#xff0c;为了不让别人看到&#xff0c;我想把它们加密&#xff0c;应该怎么操作呢&#xff1f;” 平常使用电脑时&#xff0c;可能很多朋友会将很多重要的数据保存在电脑上。但是有些文件对用户来说…

私有化敏感词检测API服务wordscheck

之前有网友在找敏感词检测的应用&#xff0c;这个应该能满足他的需求&#xff1b; 什么是 wordscheck &#xff1f; wordscheck 是敏感词检测 API&#xff0c;提供文本识别、智能鉴黄、涉政检测、谩骂等等敏感词检测过滤服务。 简介 敏感词库从大量样本库整理出来&#xff0c;…

bootstarp+springboot基于Java的教学仪器设备商城销售网站_o9b00

1、商品分类功能 商品分类是教学仪器设备销售网站中十分重要的一部分&#xff0c;它能够提高用户在网站上的浏览速度&#xff0c;并方便用户快速找到自己需要的商品。因此&#xff0c;需要对该功能进行分析和设计&#xff0c;确保其体验性、可用性和易用性。可以将商品分为多个…

Apache服务Rwrite功能使用

Rewrite也称为规则重写&#xff0c;主要功能是实现浏览器访问时&#xff0c;URL的跳转。其正则表达式是基于Perl语言。要使用rewrite功能&#xff0c;Apache服务器需要添加rewrite模块。如果使用源码编译安装&#xff0c;–enable-rewrite。有了rewrite模块后&#xff0c;需要在…

驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接

参考&#xff1a;https://www.cnblogs.com/sam-snow-v/p/15917898.html eclipse链接SQL Server出现问题 笔者使用Open JDK 17&#xff0c;SQL Server 2016&#xff0c;项目中使用JPA操作数据库。测试环境没问题&#xff0c;生产环境出现如题所示“驱动程序无法通过使用安全套接…

无痛迁移:图解 Kubernetes 集群升级步骤

本文探究了 Kubeadm 集群升级工作流程&#xff0c;并以可视化方式展现。着重介绍了控制平面节点和工作节点的升级步骤&#xff0c;涵盖了 kubeadm 升级、节点清空、kubelet 和 kubectl 升级&#xff0c;以及解除节点封锁的关键步骤。 这个简明扼要的指南可帮助用户理解和执行 K…

苹果Siri怎么打开?教你两招轻松唤醒!

苹果Siri助手是苹果公司开发的智能语音助手。作为智能语音助手&#xff0c;Siri可以理解用户的指令&#xff0c;并给出相应的回答或执行相应的操作&#xff0c;帮助大家完成各种任务&#xff0c;比如发送短信、查询天气、播放音乐、设置提醒等等。 然而&#xff0c;还有一些小…

几个西门子PLC常见通讯问题的解决方法

1台200SMART 如何控制2台步进电机&#xff1f; S7-200SMART CPU最多可输出3路高速脉冲&#xff08;除ST20外&#xff09;&#xff0c;这意味着可同时控制最多3个步进电机&#xff0c;通过运动向导可配置相应的运动控制子程序&#xff0c;然后通过调用子程序编程可实现对步进电…

数据分析思维与模型:相关分析法

相关分析法是一种用于研究两个或多个变量之间关系强度和方向的统计方法。这种方法在多个领域&#xff0c;如经济学、心理学、社会科学和自然科学中都有广泛应用。其核心是通过计算相关系数来量化变量之间的相关性。以下是相关分析法的一些基本概念和步骤&#xff1a; 选择变量…

【Mysql系列】LAG与LEAD开窗函数

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

用合成数据训练语义分割模型【裂缝检测】

最近&#xff0c;我们推出了合成裂缝分割数据集&#xff0c;在本文中&#xff0c;我们将深入探讨应用于合成数据生成过程的改进和启发式方法。 阅读完这篇文章后&#xff0c;你将了解我们如何设法创建一个数据集&#xff0c;该数据集可以像使用真实数据一样高效地训练模型。 在…

【Python】【Torch】神经网络中各层输出的特征图可视化详解和示例

本文对神经网络各层特征图可视化的过程进行运行示例&#xff0c;方便大家使用&#xff0c;有助于更好的理解深度学习的过程&#xff0c;尤其是每层的结果。 神经网络各层特征图可视化的好处和特点如下&#xff1a; 可视化过程可以了解网络对图像像素的权重分布&#xff0c;可…

PyCharm玩转ESP32

想必玩ESP32的童鞋都知道Thonny&#xff0c;当然学Python的童鞋用的更多的可能是PyCharm和VsCode Thonny和PyCharm的对比 对于PyCharm和VsCode今天不做比较&#xff0c;今天重点说一下用PyCharm玩转ESP32&#xff0c;在这之前我们先对比下Thonny和PyCharm的优缺点 1、使用Tho…

【HarmonyOS】低代码平台组件拖拽使用技巧之常用基础组件(下)

【关键字】 HarmonyOS、低代码平台、组件拖拽、代码编辑器 1、写在前面 本篇接着上一篇的内容来介绍&#xff0c;继续来看我们的登录页面的下半部分的使用&#xff0c;本篇会介绍按钮组件、单选框组件的拖拽使用&#xff0c;还会介绍代码编辑器的使用&#xff0c;最后会完成登…

【云栖 2023】姜伟华:Hologres Serverless 之路——揭秘弹性计算组

云布道师 本文根据 2023 云栖大会演讲实录整理而成&#xff0c;演讲信息如下&#xff1a; 演讲人&#xff1a;姜伟华 | 阿里云计算平台事业部资深技术专家、阿里云实时数仓 Hologres 研发负责人 演讲主题&#xff1a;Hologres Serverless 之路——揭秘弹性计算组 实时化成为…

PDF文件无密码,如何解密?

PDF文件有两种密码&#xff0c;一个打开密码、一个限制编辑密码&#xff0c;因为PDF文件设置了密码&#xff0c;那么打开、编辑PDF文件就会受到限制。想要解密&#xff0c;我们需要输入正确的密码&#xff0c;但是有时候我们可能会出现忘记密码的情况&#xff0c;或者网上下载P…