JavaScript位置参数实现原理及过程解析

1.什么是位置参数?

JavaScript:为什么命名参数比位置参数更好

你一定很熟悉位置参数,即使你第一次听到这个名字。

function greet(firstName, lastName) {
 console.log(`Hello ${firstName} ${lastName}`);
} 

// 预期用法 

greet('Michael', 'Scott'); 

const fName = 'Harry';
const lName = 'Potter';
greet(fName, lName); 

// 错误用法 

const firstName = 'Erlich';
const lastName = 'Bachman';
greet(lastName, firstName); 

greet函数接受两个参数:firstName和lastName。调用者必须确保firstName是第一个参数,lastName是第二个参数。这里重要的一点是,参数的名称没有任何意义,唯一重要的是参数传递的顺序。

这种熟悉的方法称为位置参数。通常,在你传递一个或两个参数的情况下,这很好,因为它很难弄乱参数的顺序。但是如果你必须调用一个需要6个参数的函数,那就很难记住传递参数的顺序。你不希望传递密码来代替用户名参数。

2. 位置参数问题

位置参数很简单,但是你将面临一些挑战。

(1) 不能跳过中间参数 /

假设你已经更改了greet函数,使其现在需要3个参数:firstName、middleName和lastName。由于许多人没有中间名,因此你希望将MiddleName设为可选参数,仅使用firstName和lastName调用greet函数的唯一方法是此方法。

greet('Aditya', null, 'Agarwal');
// Correct
greet('Aditya', 'Agarwal');
// Incorrect 

你不能只提供firstName和lastName。当可选参数的数量增加到5个时,这个问题变得更加明显。现在,你必须提供5个null才能在这些参数之后提供参数。

(2) 将类型添加到位置参数不那么干净

如今,为你的实用程序添加类型变得非常普遍。使用位置参数,你别无选择,只能将类型与函数定义一起内联。这可能会使代码有点模糊,如果我们可以在一个块中声明所有参数的类型定义,那就更好了。

(3) 引起细微的错误

位置参数包装了很多隐性行为,这可能是造成微妙bug的原因。我们来看一个常见的JS技巧问题

const numbers = ['1', '4', '8', '10'];
console.log(numbers.map(parseInt));

// 你可能会认为结果将是:
[1, 4, 8, 10]

// 这是实际的输出:
[ 1, NaN, NaN, 3 ]

惊讶吗?这种奇怪的输出的原因隐藏在位置参数的隐性背后。你会看到map和parseInt函数在显而易见的情况下隐藏了它们的一些秘密。

让我们再次查看代码 number.map(parseInt)。

这里到底发生了什么?

  • 我们在numbers数组上运行map函数。
  • map获取数组的第一项并将其传递给parseInt。
  • 现在,对于数组中的第一项(即1),它将执行 parseInt(1)。对...?错误!!!

实际上,map将三个参数传递给其回调函数。第一个是数组中的当前项目,第二个是项目的索引,第三个是整个数组。这本身没有问题,但真正的问题在于后一部分。

numbers.map(parseInt) 与 numbers.map((item) => parseInt(item)) 不同。你可以假设,由于回调函数仅接受item参数并将其传递给parseInt,因此我们可以跳过附加步骤。但是两者是不同的:在前者中,我们将所有数据从map传递到parseInt,而在后者中,我们仅传递项。

你可能不知道,但是parseInt的第二个参数称为基数。默认情况下,基数的值为10(以10为底,因为人类遵循十进制进行计数)。该代码出了问题,就是我们将当前项目的索引作为基数值传递给parseInt。这些是发生的实际函数调用:

parseInt('1', 0, [...]);
parseInt('4', 1, [...]);
parseInt('8', 2, [...]);
parseInt('10', 3, [...]);

现在我们知道了问题,我们如何才能做得更好?

3. 位置参数的替代

如果一个函数可以通过名字就知道它期望的参数是什么呢?这样即使你误传了额外的数据给它,它也只会使用它需要的东西。

让我们对parseInt进行包装。下面是一个简单的实现。

// 实现
function myCustomParseInt(objArgs) {
 return parseInt(objArgs.item, objArgs.radix);
} 

// 使用
const num = myCustomParseInt({ item: '100', radix: 10 }); 

myCustomParseInt仅接受一个参数,它是一个对象。这个对象可以有两个键:item 和 radix。让我们使用我们的自定义函数与map。必须有一个中间步骤,将回调收到的args发送到myCustomParseInt。

const numbers = ['1', '4', '8', '10'];

const result = numbers.map((item, index) => myCustomParseInt({ item, index }));

console.log(result); // [ 1, 4, 8, 10 ]

请注意,即使我们将索引传递给myCustomParseInt也不会造成任何问题。那是因为myCustomParseInt只会忽略它。将对象传递给函数的这种模式称为命名参数,它比位置参数更明确。

要更改基数,我们必须显式传递基数键。这意味着如果要解析以2为底的字符串,则必须转到文档并查看参数(基数)的确切名称。如果我们盲目地传递任何其他键,它将无济于事。这对我们来说很棒,因为它避免了意外行为。

(1) 具有解构的命名参数

不久前,JavaScript获得了称为解构的功能,让我们在myCustomParseInt实现中使用它。

// 位置参数
function myCustomParseInt(item, radix) {
 return parseInt(item, radix);
} 

// 命名参数旧的实现
function myCustomParseInt(objArgs) {
 return parseInt(objArgs.item, objArgs.radix);
} 

// 命名参数解构
function myCustomParseInt({ item, radix }) {
 return parseInt(item, radix);
} 

你会注意到,只需添加两个花括号,我们就可以得到命名args的好处,你可以将解构视为执行 const item = objArgs.item;。

如果使用 undefined 调用myCustomParseInt,则JS将引发错误。那是因为不允许 undefined.item。为了避免这种情况,我们可以在解构结束时添加 = {}。这样,当我们传递undefined时,它将执行 {}.item 这是有效的JS。这是最终的实现:

function myCustomParseInt({ item, radix } = {}) {
 return parseInt(item, radix);
} 

通过命名参数模式,我们也可以跳过我们不想提供的参数,因为函数不再依赖于传递参数的顺序。

// 对于位置参数,我们必须在之间添加一个null
function greetPos(firstName, middleName, lastName) {}
greetPos('Aditya', null, 'Agarwal');

// 使用命名参数,你只需提供firstName和lastName。
function greetNamed({ firstName, middleName, lastName } = {}) {}
greetNamed({ firstName: 'Aditya', lastName 'Agarwal' });

总而言之,我要说的是命名参数是一种强大的模式,如今它已变得非常普遍,但是你不必总是使用它们。有时你甚至可以将两者结合在一起。浏览器中的fetch API的用法如下:

// 以url作为位置参数的请求,以及以args做命名参数的选项。
fetch('https://google.com', {
 method: 'POST',
 headers: {
  'Content-Type': 'application/json',
 },
}); 

// basic GET requests with just positional args
fetch('https://google.com'); 

这里的强制参数(API路径)是一个位置参数,然后通过命名参数接受可选的参数。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • JS如何在数组指定位置插入元素

    一.JavaScript splice() 方法 splice() 方法向/从数组中添加/删除项目,然后返回被删除的项目. 方法实例 //在数组指定位置插入 var fruits = ["Banana", "Orange", "Apple", "Mango"]; fruits.splice(2, 0, "Lemon", "Kiwi"); //输出结果 //Banana, Orange,

  • js实现随机div颜色位置 类似满天星效果

    本文实例为大家分享了js实现随机div颜色位置的具体代码,供大家参考,具体内容如下 效果如下: 代码如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>MTX</title> <style> div{width: 50px;height: 50px;position: absolute;}

  • javascript正则表达式学习之位置匹配

    一.前言 正则表达式是匹配模式,要么是匹配字符,要么匹配位置. 其实在开发中很少用到匹配位置,本篇文章主要包含: 二.什么是位置 位置:相邻字符之间的位置. 三.如何匹配位置 在ES5中,共有6个锚:^, $, \b, \B, (?=p), (?!p) 可视化形式: RegExp:/^$\b\B(?=a)(?!b)/g 3.1 ^和$ ^(脱字符)匹配开头,在多行匹配中匹配行开头. $(美元符)匹配结尾,在多行匹配中匹配行结尾. 比如:我们把字符串的开头和结尾用#替换(位置可以替换成字符的):

  • JS在Array数组中按指定位置删除或添加元素对象方法示例

    JavaScript的数据中可通过splice/slice在指定位置添加或删除元素.另外还有slice.delete等方法实现. splice简介 splice方法向/从数组中添加/删除项目,然后返回被删除的项目. 该方法会改变原始数组. arrayObject.splice(index, howmany, item1, ..., itemX) 参数 描述 index 必需.整数,规定添加/删除项目的位置,使用负数可从数组结尾处规定位置. howmany 必需.要删除的项目数量.如果设置为 0,

  • JS算法题之查找数字在数组中的索引位置

    前言 编写算法时,排序是一个非常重要的概念.它有各种各样的种类:冒泡排序.希尔排序.分块块排序,梳排序,鸡尾酒排序,侏儒排序 -- 这些可不是我瞎编的! 这个算法题能够让我们一睹精彩的世界.我们必须对数字数组进行升序排序,并找出给定数字在该数组中的位置. 算法说明 将值(第二个参数)插入到数组(第一个参数)中,并返回其在排序后的数组中的最低索引.返回的值应该是一个数字. 例如 getIndexToIns([1,2,3,4], 1.5) 应该返回 1,因为 1.5 大于 1(索引0),但小于 2(

  • JavaScript获取用户所在城市及地理位置

    下面一段代码给大家分享js 获取用户所在城市,具体代码如下所述: <!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>获取用户地理位置</title> <script type="text/javascript" src="./jquery-3.3.1.js"&g

  • js实现圆形显示鼠标单击位置

    本文实例为大家分享了js实现圆形显示鼠标单击位置的具体代码,供大家参考,具体内容如下 代码如下: <!DOCTYPE html> <head> <meta charset="UTF-8"> <title>显示鼠标单击位置</title> <style> .mouse{position:fixed;background:#ffd965;width:40px;height:40px;border-radius:20px

  • JS替换字符串中指定位置的字符(多种方法)

    假设有一个字符串,可能'Good Morning'也可能是'Hello World',我想将第五个字符,替换成'-'. 因为字符串虽然可以像数组那样获取某一位置字符'Hello World'[4],但是不能像数组那样直接修改某一位置的字符'Hello World'[4] = '-',这样是行不通的,但是可以把它切分成数组,修改某一位置的值,然后在合并回来. 方法1: const replaceStr1 = (str, index, char) => { const strAry = str.sp

  • JS中获取 DOM 元素的绝对位置实例详解

    在操作页面滚动和动画时经常会获取 DOM 元素的绝对位置,例如 本文 左侧的悬浮导航,当页面滚动到它以前会正常地渲染到文档流中,当页面滚动超过了它的位置,就会始终悬浮在左侧. 本文会详述各种获取 DOM 元素绝对位置 的方法以及对应的兼容性.关于如何获取 DOM 元素高度和滚动高度,请参考视口的宽高与滚动高度 一文. 概述 这些是本文涉及的 API 对应的文档和标准,供查阅: API 用途 文档 标准 offsetTop 相对定位容器的位置 MDN CSSOM View Module clien

  • JavaScript位置参数实现原理及过程解析

    1.什么是位置参数? JavaScript:为什么命名参数比位置参数更好 你一定很熟悉位置参数,即使你第一次听到这个名字. function greet(firstName, lastName) { console.log(`Hello ${firstName} ${lastName}`); } // 预期用法 greet('Michael', 'Scott'); const fName = 'Harry'; const lName = 'Potter'; greet(fName, lName)

  • JavaScript获取当前url路径过程解析

    这篇文章主要介绍了JavaScript获取当前url路径过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1.假设当前页完整地址是:http://localhost:61768/Home/Index?id=2&age=18 //获取当前窗口的Url var url = window.location.href; //结果:http://localhost:61768/Home/Index?id=2&age=18 //获取当前窗口的主

  • JavaScript async/await原理及实例解析

    随着Node 7的发布,越来越多的人开始研究据说是异步编程终级解决方案的 async/await. 异步编程的最高境界,就是根本不用关心它是不是异步. async 函数就是隧道尽头的亮光,很多人认为它是异步操作的终极解决方案. async 和 await 起了什么作用 async 起什么作用 这个问题的关键在于,async 函数是怎么处理它的返回值的! 我们当然希望它能直接通过return语句返回我们想要的值,但是如果真是这样,似乎就没 await 什么事了.所以,写段代码来试试,看它到底会返回

  • javascript网页随机点名实现过程解析

    主要知识点涉及if选择结构判断语句.数组的定义.定时器.清除定时器.日期对象的使用. 1.HTML结构 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>随机点名</title> <style type="text/css"> .box{ width: 200px; height: 200px; border: 1

  • JavaScript队列结构Queue实现过程解析

    一.队列简介 队列是是一种受限的线性表,特点为先进先出(FIFO:first in first out). 受限之处在于它只允许在表的前端(front)进行删除操作:在表的后端(rear)进行插入操作: 相当于排队买票,先来的先买票,后来的后买票. 队列的应用: 打印队列:计算机打印多个文件的时候,需要排队打印:线程队列:当开启多线程时,当新开启的线程所需的资源不足时就先放入线程队列,等待CPU处理: 队列类的实现: 队列的实现和栈一样,有两种方案: 基于数组实现:基于链表实现: 队列的常见操作

  • Jmeter参数化实现原理及过程解析

    背景: 在实际的测试工作中,我们经常需要对多组不同的输入数据,进行同样的测试操作步骤,以验证我们的软件的功能.这种测试方式在业界称为数据驱动测试,而在实际测试工作中,测试工具中实现不同数据输入的过程称为参数化设置. jmeter提供多种参数化设置的方式,常用的有: 1.使用 用户定义的变量元件 实现 2.使用 用户参数元件 实现 3.使用 函数助手 实现 4.使用 CSV 数据文件设置元件 实现 5.其它方式(数据库数据.beanshell等)实现 一.用户定义的变量元件实现 假如设置如下: 调

  • Python动态类型实现原理及过程解析

    在python中,我们使用变量时,并没有声明变量的存在和类型.类型是在运行过程中自动决定的. a = 3 python将会执行三步去完成上面这个请求. 1.创建一个对象代表3 2.创建一个变量a,如果a未创建. 3.将变量a与对象3相连接. 可以将变量a看作对象3的一个引用. a = 3 b = a 多个变量可以指向同一个对象,在Python中叫共享引用. Python在每个对象中保持了一个计数器,用于记录当前指向该对象的引用的数目,一旦计数器被设置为0,该对象的内存空间就会自动回收. 原处修改

  • Python生命游戏实现原理及过程解析(附源代码)

    1. 生命游戏是什么 生命游戏是英国数学家约翰·何顿·康威在1970年发明的细胞自动机.它包括一个二维矩形世界,这个世界中的每个方格居住着一个活着的或死了的细胞.一个细胞在下一个时刻生死取决于相邻八个方格中活着的或死了的细胞的数量.如果相邻方格活着的细胞数量过多,这个细胞会因为资源匮乏而在下一个时刻死去:相反,如果周围活细胞过少,这个细胞会因太孤单而死去. 规则看起来很简单,但却能演绎出无穷无尽的内容. 滑翔者:每4个回合"它"会向右下角走一格.虽然细胞早就是不同的细胞了,但它能保持原

  • Jenkins节点配置实现原理及过程解析

    1.配置代理 系统管理---configure Global Security(全局安全设置)---Tcp port for inbound agents---指定端口 服务器防火墙中开放此端口(linux版) 关闭电脑防火墙(windows版) 点击 agent protocols---勾选Java Web Start Agent Protocol4 2.添加节点 系统管理---管理节点 新建节点---输入节点名称(node2)---勾选Permanent Agent 添加节点(可以理解为是一

  • TCP性能调优实现原理及过程解析

    三次握手阶段 客户端SYN包的重试次数 sysctl -w net.ipv4.tcp_syn_retries=6 相关介绍 第 1 次重试发生在 1 秒钟后,接着会以翻倍的方式在第 2.4.8.16.32 秒共做 6 次重试,最后一次重试会等待 64 秒,如果仍然没有返回 ACK,才会终止三次握手.所以,总耗时是 1+2+4+8+16+32+64=127 秒,超过 2 分钟. 服务端半连接池大小 sysctl -w net.ipv4.tcp_max_syn_backlog=16384 服务端半连

随机推荐