函数参数是什么?

就是函数内部无法确定的一个东西,需要外部传给函数内部的玩意儿,语法上就是写在函数括号中的东东。比如:

function test(a) {}

其中的 a 就是 test 函数的参数,在函数体内部,a 作为一个变量存在,可以修改它。

JS 的函数参数,真的是可以传入任意值,没有任何限制,可以包括 原始类型对象数组函数 等等,只要是 JS 语言支持的,都可以当做参数传入。

原始类型

JS 原始类型参数(numberstringbooleannullundefinedsymbolbigint按值传递,传入的是值的副本,在函数里面修改传入的值不会影响外部变量。

function test(a) {
a = '前端路引'; // 修改原始类型不会影响 arg 变量值
}
let arg = '微信公众号';
test(arg);
console.log(arg); // 输出 '微信公众号'(原值未改变)

引用类型

JS 引用类型参数(对象数组函数按引用地址传递,如果函数里面修改了对象属性,会影响外部变量,使用时需特别注意!!

function test (obj) {
obj.name = '前端路引' // 修改了对象属性,会影响共享的对象
obj = { // 如果直接给 obj 参数赋值,不会影响共享的对象,因为 obj 已经变成了一个新的对象
test: '测试参数'
}
}
const weChat = {
type: '微信公众号',
age: 1,
};
test(weChat);
console.log(weChat) // {type: '微信公众号', age: 1, name: '前端路引'}

默认参数

ES6 版本为 JS 注入了一大堆活性,各种花活不断,默认参数就是其中一个最常用的花活。当未传入参数或者传入的参数是 undefined,则使用默认参数。

function test(a = '前端路引') {
return a; // 将 a 变量值返回出去
} let arg = '微信公众号';
console.log(test(arg)); // 输出 '微信公众号' // 没传入参数,使用默认值
console.log(test()); // 输出 '前端路引' // 传入 undefined 也是用默认值
console.log(test(undefined)); // 输出 '前端路引'

剩余参数

ES6 的又一花活之一,允许使用 ... 语法,将多余的参数合并为数组,在箭头函数中可以代替 arguments 对象。

function sum(...numbers) {
return numbers.reduce((acc, num) => acc + num, 0);
}
sum(1, 2, 3); // 返回 6 function test(a, ...rest) {
console.log(a); // 获得传入的第一个参数,输出 '公众号'
console.log(rest); // 多余参数转为数组,输出 ['前端路引', '函数测试']
}
test('公众号', '前端路引', '函数测试');

需特别注意,剩余参数只能放在最后,否则报语法错误 SyntaxError: Rest parameter must be last formal parameter

比如:

function test(...rest, a) { // 报错  SyntaxError: Rest parameter must be last formal parameter
}

解构赋值传参

还是 ES6 的花活之一,用于解构对象或数组参数。

/**
* 对象解构
* name 为解构参数对象中的 name 属性
* rest 为解构参数对象中剩余的属性,也是对象
*/
function test1({ name, ...rest }) {
console.log(name); // 输出 '前端路引'
// rest 获得剩余为分配的对象属性
console.log(rest); // 输出 {age: 1}
}
test1({ name: '前端路引', age: 1 }); /**
* 数组解构
* first 为第一个数组值
* rest 为数组剩余值,也是一个数组
*/
function test2([first, ...rest]) {
console.log(first); // 输出 '前端路引'
console.log(rest); // 输出 [1, '微信公众号']
}
test2(['前端路引', 1, '微信公众号']);

参数使用使用解构赋值时,需特别注意,如果参数没传入参数,那么解构将会报错:

// 报错 TypeError: Cannot destructure property 'name' of 'undefined' as it is undefined.
function test({ name }) {
}
test();

原因是未传入参数时,默认便是 undefined,对 undefined 解构便会报错!!这时候可以使用函数默认参数进行解决:

// 表示未传入参数时使用空对象进行解构
function test({ name } = {}) {}
test();

解构中也可以使用默认值:

// 对象解构默认值
function test1({ name = '前端路引' } = {}) {
console.log(name);
}
test1(); // 输出 '前端路引'
test1({age: 1}); // 输出 '前端路引' // 需注意另一种写法
function test2({ name } = { name: '前端路引' }) {
console.log(name);
}
test2(); // 输出 '前端路引'
test2({age: 1}); // 输出 undefined // 数组解构默认值
function test3([first = '前端路引', ...rest] = []) {
console.log(first);
}
test3();

test2 中使用了一个默认对象,这个对象中有 name 属性,如果传入参数不存在时候,将会获得 name 属性值,但如果传入参数存在时,并且传入的对象中没有 name 属性,那么就只能是 undefined。

arguments 对象

使用 function 声明的函数,可以使用 arguments 所有参数。需注意 arguments 在箭头函数中不可用

arguments 是类数组对象,不是所有数组方法都可以使用,可以使用 ... 展开运算符转换为数组。

function test() {
// arguments.push('微信公众号'); // 报错 TypeError: arguments.push is not a function
const temp = [...arguments]; // 使用展开运算符转换为数组
temp.push('微信公众号'); // 转为数组之后可以使用 push 方法
console.log(temp);
}
test(1, '前端路引', true); // 输出 [1, '前端路引', true, '微信公众号']

函数作为参数

虽然 回调函数 参数这种方式已经被 Promise 代替,但如果要实现各种钩子函数,还是只能使用 function 作为参数传递。

使用函数作为参数的函数有一个专用名词叫做 高阶函数。多用于 事件处理、异步操作 等等。

function test1(url, callback) {
// 模拟异步操作
setTimeout(() => {
callback({ data: '前端路引' });
}, 1000);
}
test1('/api', (response) => {
console.log(response.data); // 输出 '前端路引'
}); // 使用 Promise 改写 test1 函数:
function test2(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ data: '前端路引' });
}, 1000)
})
}
test2('/api').then((response) => {
console.log(response.data); // 输出 '前端路引'
})

当需要钩子函数时候,便无法使用 Promise 替换了,比如:

function test({
url,
before, // 钩子函数,在请求开始时调用
callback,
} = {}) {
before && before('请求开始');
setTimeout(() => {
// 执行完之后回调
callback && callback({ data: '前端路引' });
}, 1000)
}
test({
url: '/api',
before(msg) {
console.log(msg);
},
callback(response) {
console.log(response.data);
}
})

函数柯里化

通过闭包返回函数,分步传递参数,这种方式称为 函数柯里化

function test(a) {
return (b) => a * b; // 函数返回值是一个函数,用于二次调用
}
const double = test(2); // 第一次传入参数,获得一个返回函数
console.log(double(5)); // 第二次传入参数,获得结果,输出 10

由于闭包中的变量一直在内存中,所以在使用时候需注意内存泄漏问题!!

bind 方法绑定参数

bind 这方法不仅可以绑定内部 this 指针,还能用于固定部分参数,生成新函数。

function test1(type, name) {
console.log(this);
return `${type}:${name}`;
}
const test2 = test1.bind({age: 1}, '微信公众号');
console.log(test2('前端路引')); // 输出 '微信公众号:前端路引'

test1.bind({age: 1}, '微信公众号') 作用是给 test1 绑定 this 指向 {age: 1} 对象,同时固定了第一个参数为 '微信公众号',返回一个新的函数,此函数只有剩下的 name 参数。

也可以使用 test1.bind(null, '微信公众号') 不绑定 this 指针,仅固定第一个参数。

隐式参数类型转换

由于 JS 的参数灵活性,在使用时,需特别注意类型转换问题。比如字符串 '5' 转为数字 5

function test(a, b) {
return a + b;
}
test('3', 5); // 返回 '35'(字符串拼接)
test(3, '5'); // 返回 '35'
test(3, 5); // 返回 8

如果无法确定传入的参数类型,那么就有必要显式转换类型(如 Number(param)):

function test(a, b) {
return Number(a) + Number(b);
}
test('3', 5); // 返回 8
test(3, '5'); // 返回 8
test(3, 5); // 返回 8

或进行参数类型校验,当输入不合法时候,抛出异常:

function test(a, b) {
if (typeof a !== 'number' || typeof b !== 'number') {
throw new Error('参数类型错误');
}
return a + b;
}
test('3', 5); // 抛出异常

写在最后

理解各种函数传参方式,灵活运用可以在编程中玩出花来。各种优雅的设计模式、易于维护的高级代码,都离不开函数的使用技巧~~

在使用函数参数时,也需特别注意参数合法性校验,尤其是提供给外部调用的函数,必须做参数类型校验,避免程序出现参数类型错误。

Web前端入门第 65 问:JavaScript 函数参数各种使用方式的更多相关文章

  1. web前端入坑第五篇:秒懂Vuejs、Angular、React原理和前端发展历史

    秒懂Vuejs.Angular.React原理和前端发展历史 2017-04-07 小北哥哥 前端你别闹 今天来说说 "前端发展历史和框架" 「前端程序发展的历史」 「 不学自知, ...

  2. JavaScript 函数参数是传值(byVal)还是传址(byRef)?

    对于“JavaScript 函数参数是传值(byVal)还是传址(byRef)”这个问题,普遍存在一个误区:number,string等“简单类型”是传值,Number, String, Object ...

  3. javascript 函数参数

    1.javascript函数参数的个数以及类型没有强制规定,调用时不必严格按照函数的参数或类型,函数的参数只是在调用函数的时候提供了便利,但不是必须的! 2.参数在javascript内部是用数组ar ...

  4. 理解JavaScript函数参数

    前面的话 javascript函数的参数与大多数其他语言的函数的参数有所不同.函数不介意传递进来多少个参数,也不在乎传进来的参数是什么数据类型,甚至可以不传参数. arguments javascri ...

  5. web前端学习(四)JavaScript学习笔记部分(3)-- JavaScript函数+异常处理+事件处理

    1.Javascript函数-了解函数的用途 1.1.函数: 函数是由事件驱动的或者当它被调用时执行的可重复使用的代码块 2.Javascript函数-定义函数 2.1.function必须小写 3. ...

  6. web前端入坑第二篇:web前端到底怎么学?干货资料! 【转】

    http://blog.csdn.net/xllily_11/article/details/52145172 版权声明:本文为博主[小北]原创文章,如要转载请评论回复.个人前端公众号:前端你别闹,J ...

  7. web前端(13)—— 了解JavaScript,JavaScript的引入方式

    从本篇博文开始,将进入web前端方便最关键最重要的部分——javascript,学到后面你就知道它真的太重要了 什么是JavaScript JavaScript一种直译式的脚本语言,是一种动态类型.弱 ...

  8. Web前端基础怎么学? JavaScript、html、css知识架构图

    以前开发者只要掌握 HTML.CSS.JavaScript 三驾马车就能胜任一份前端的工作了.而现在除了普通的编码以外,还要考虑如何性能优化,如何跨端.跨平台实现功能,尤其是 AI.5G 技术的来临, ...

  9. [转]WEB开发者必备的7个JavaScript函数

    我记得数年前,只要我们编写JavaScript,都必须用到几个常用的函数,比如,addEventListener 和 attachEvent,并不是为了很超前的技术和功能,只是一些基本的任务,原因是各 ...

  10. 初学者入门web前端:C#基础知识:函数

    入行前端对函数的掌握程度有可能直接影响以后工作的效率,使用函数可以高效的编写编码,节省时间,所以我整理了C#中最基础的函数知识点,虽然我在学习中 遇到很多问题,但是只要能够解决这些问题,都是好的. 一 ...

随机推荐

  1. Detected non-NVML platform: could not load NVML: libnvidia-ml.so.1: cannot open shared object

    前言 在 kubernetes 中配置 https://github.com/NVIDIA/k8s-device-plugin 时, 报错:Detected non-NVML platform: co ...

  2. 解决vscode"无法加载文件 ,因为在此系统上禁止运行脚本"报错

    问题 在使用 vscode 自带程序终端时,会报"无法加载文件 ,因为在此系统上禁止运行脚本",这是因为 PowerShell 执行策略的问题. > tsc --init t ...

  3. Ubuntu 下查看当前用户

    博客地址:https://www.cnblogs.com/zylyehuo/ 在终端执行以下命令 whoami

  4. 实现领域驱动设计 - 使用ABP框架 - 领域服务

    领域服务 领域服务实现领域逻辑 依赖于服务和存储库. 需要处理多个聚合,因为该逻辑不适合任何聚合. 领域服务与领域对象一起工作.它们的方法可以获取并返回实体.值对象.原始类型--但是,它们不获取/返回 ...

  5. IDEA中高效配置Python开发环境搭建

    原文地址:IDEA中高效配置Python开发环境搭建-张苹果博客 Mac用户须知:系统已预装Python 2.7,如需新版建议通过Homebrew安装. # 张苹果博客:https://zhangpi ...

  6. 卸载重装vscode

    最近工作需要长期用到python,但我的老电脑又实在拉不起pycharm那配置,干脆就用vscode了,但本来我的vscode是用来写c/c++的,安装配置一通乱搞,现在也不知道怎么配置回来了. 干脆 ...

  7. 无耳 Solon Ai MCP,发布工具服务,使用工具服务。效果预览!

    solon-ai-mcp 是 solon-ai 的扩展特性.提供 mcp 协议的支持.通过它,可以方便的发布 Tool Service,方便的使用 Tool Service. 引入依赖包 <de ...

  8. Mono与IL2CPP

    Mono: Mono是.NET Framework 的一种开源实现. Mono项目将使开发者用各种语言(C#,VB.NET等)开发的.NET应用程序,能在任何Mono支持的平台上运行, 包括Linux ...

  9. 使用Python解决Logistic方程

    引言 在数学和计算机科学中,Logistic 方程是描述人口增长.传播过程等现象的一种常见模型.它通常用于表示一种有限资源下的增长过程,比如动物种群.疾病传播等.本文将带领大家通过 Python 实现 ...

  10. JMeter+Grafana+Influxdb可视化性能监控平台搭建总结

    说明:此次搭建基于unbuntu16.04系统搭建 1.安装docker 打开终端依次输入如下命令: 卸载旧版本 sudo apt-get remove docker docker-engine do ...