基础函数及一些常用的函数方法1
js 函数的概念
JavaScript 使用关键字 function 定义函数。
函数可以通过声明定义,也可以是一个表达式。
js的参数(形参 实参 剩余参数 默认参数 参数规则)及 函数提升
形参和实参如下,下面中的a,b就是形参,形参是虚的,而1,2是实参,实参是实的,是我们自己传入进去的。
function foo(a, b) {
console.log([a, b]);
}
foo(1, 2); // 输出 [1, 2]
1.默认参数
如果调用函数时缺少提供实参,那么形参默认值为 undefined,函数sayHi调用时,没有传入实参,而输出确实everyone,所以everyone是它的默认参数,它要在函数中声明。
function sayHi(name) { name = name || 'everyone'; console.log( 'Hello ' + name + '!'); } sayHi(); // 输出 'Hello everyone!'
上面代码虽然简单明了,但缺点在于如果传入的实参对应布尔值为false,就不起作用了。所以需要更精准的话可以用 if
语句或者三元表达式,判断参数是否等于 undefined
,
// if 语句判断
function sayHi(name) {
if (name === undefined) {
name = 'everyone';
}
console.log( 'Hello ' + name + '!');
}
// 三元表达式判断
function sayHi(name) {
name = (name !== undefined) ? name : 'everyone';
console.log( 'Hello ' + name + '!');
}
默认值不但可以是一个值,它还可以是任意合法的表达式,甚至是函数调用:
function sayHi(name = 'every'+'one') {
console.log( 'Hello ' + name + '!');
}
sayHi(); // 输出 'Hello everyone!'
//--------------------------------------
function foo() {
console.log('调用foo');
return 'Tony';
}
function sayHi(name = foo()) {
console.log( 'Hello ' + name + '!');
}
sayHi(); // 输出 '调用foo'
// 输出 'Hello Tony!'
sayHi(undefined); // 输出 '调用foo'
// 输出 'Hello Tony!'
sayHi('John'); // 输出 'Hello John!'
参数默认值的位置
通常我们给参数设置默认值,是为了调用函数时可以适当省略参数的传入,这里要注意的是,有多个参数时,设置了默认值的参数如果不是放在尾部,实际上它是无法省略的。
function fn(x = 1, y) {
console.log([x, y]);
}
fn(); // 输出 [1, undefined]
fn(2); // 输出 [2, undefined]
fn(, 2); // 报错,语法错误(这里不支持像数组那样的空槽)
fn(undefined, 2); // 输出 [1, 2] (那还不如传个 1 方便呢!)
上面例子中,给形参 x 设置的默认值就显得没有任何意义了。因此,设置默认值的参数放在尾部是最好的做法:
function fn(x, y = 2) {
console.log([x, y]);
}
fn(); // 输出 [undefined, 2]
fn(1); // 输出 [1, 2]
fn(1, 1) // 输出 [1, 1]
参数的省略问题
在多个参数设置了默认值的情况下,那么问题又来了,你并不能省略比较靠前的参数,而只给最后的一个参数传入实参。
function fn(x, y = 2, z = 3) {
console.log([x, y, z]);
}
fn(1, , 10) // 报错
||
if
function fn(obj={}){
let defaultobj={
x:undefined,
y:5,
z:2
}
//object.ass是拷贝是意思object.assign(target,source)
//把source中的源对象拷贝到target目标对象中
let result = Object.assign(defaultobj,obj);
console.log([result.x,result.y,result.z])
}
fn({x:4,z:10})//输出[4,5,10]
上面的例子之中,defaultObj 中的属性会被 obj 的相同属性覆盖,obj 中如果有其他属性会分配给 defaultObj 。这里用一个变量接收返回的合并对象。
参数默认值和解构赋值结合使用
函数调用时,实参和形参的匹配实际上是一个隐式的赋值过程,所以,参数传递也可以进行解构赋值:
function fn({ x, y = 2, z = 3 }) {
console.log([x, y, z]);
}
fn({}); // 输出 [undefined, 2, 3]
fn({ x: 1, z: 10 }); // 输出 [1, 2, 10]
在上面的例子中,如果函数调用时不传任何参数,也会产生报错,因为这导致了参数初始化时解构赋值失败,相当于执行了 {x, y = 2, z = 3} = undefined
这样的代码。你可以利用参数默认值的语法,给 {x, y = 2, z = 3}
设置一个默认的解构对象,使得不传参函数也能够顺利执行:
function fn({ x, y = 2, z = 3 } = {}) {
console.log([x, y, z]);
}
fn(); // 输出 [undefined, 2, 3]
参数默认值的作用域与暂时性死区
还有一个小细节,一旦有参数设置了默认值,那么它们会形成自己的作用域(包裹在(...)
中),因此不能引用函数体中的变量:
function foo(a = b) {
let b = 1;
}
foo(); // 报错,b 未定义
它也符合普通作用域的规则:
let b = 2;
function foo(a = b) {
let b = 1;
return a;
}
foo(); // 2
上面的例子中,形参a先获取到了全局变量b的值。当然,如果形参作用域中存在一个形参 b 的话,它优先获取到的是当前作用域的:
let b = 2;
function foo(b = 3 ,a = b) {
return a;
}
foo(); // 3
给多个参数设置默认值,它们会按顺序初始化的,遵循“暂时性死区”的规则,即前面的参数不能引用后面的参数:
function foo(a = b, b = 2) {
return a + b;
}
foo(); // 报错,b 在初始化之前不能访问
剩余参数
ES6 提供了**剩余参数(rest)**的语法(...变量名
),它可以收集函数多余的实参(即没有对应形参的实参),这样就不再需要使用 arguments
对象来获取了。形参使用了 ...
操作符会变成一个数组,多余的实参都会被放进这个数组中。
剩余参数基本用法:
function sum(a, ...values) {
for (let val of values) {
a += val;
}
return a;
}
sum(0, 1, 2, 3); // 6
上面例子中,在参数初始化时,首先根据参数位置进行匹配,把 0 赋值给 a ,然后剩余的参数 1、2、3 都会被放进数组 values 中。这个东西刚接触的话不是能马上理解,多看几遍发现是a的累加
剩余参数的位置
剩余参数必须是最后一个形参,否则会报错。
// 报错
function fn1(a, ...rest, b) {
console.log([a, b, rest]);
}
// 正确写法
function fn2(a, b, ...rest) {
console.log([a, b, rest]);
}
fn2(1, 2, 3, 4) // 输出 [1, 2, [3, 4]]
展开语法
前面我们知道了如何把多余的参数收集为一个数组,但有时候我们需要做一些相反的事,例如要把一个数组中的元素分别传入给某个函数,而不是传入一个数组,像这样:
function sum(...values) {
let sum = 0;
for (let val of values) {
sum += val;
}
return sum;
}
let arr = [1, 2, 3, 4];
sum(arr); // "01,2,3,4"
上面例子的函数会把所有传进来的数值累加,如果直接传入一个数组,就得不到我们想要的结果。例子中传入一个数组, values 的值会变成 [[1, 2, 3, 4]],导致数组 values 中只有一个元素,而这个元素的类型是数组。那么函数返回值就是数值 0 和数组 [1, 2, 3, 4]相加的结果了,两者各自进行了类型的隐式转换变成字符串,然后再相加,是一个字符串拼接的效果。
比较可行的是借助 apply() 方法
sum.apply(null, arr); // 10
它甚至可以随意搭配常规值使用,没有前后位置限制,还可以同时传入多个可迭代对象:
sum(-1, ...arr); // 9
sum(...arr, 5); // 15
sum(-1, ...arr, 5); // 14
sum(-1, ...arr, ...[5, 6, 7]); // 27
函数提升
函数提升只针对具名函数,而对于赋值的匿名函数,并不会存在函数提升。
console.log(a); // f a()
console.log(b); //undefined
function a(){
console.log('hello')
}
var b=function(){
console.log('world')
}
变量提升与函数提升的优先级
即函数提升只会提升函数声明(函数提升是可以直接在函数声明之前调用该函数,并能成功执行它),而不会提升函数表达式(函数表达式就可以看作成变量提升)。
console.log(foo1); // [Function: foo1]
foo1(); // foo1
console.log(foo2); // undefined
foo2(); // TypeError: foo2 is not a function
function foo1 () {
console.log("foo1");
};
var foo2 = function () {
console.log("foo2");
};
函数提升优先级高于变量提升,且不会被同名变量声明覆盖,但是会被变量赋值后覆盖。而且存在同名函数与同名变量时,优先执行函数。
console.log(foo);
function foo(){
console.log("foo");
}
var foo = 1;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现