8.函数(总) - JS

基本语法

  1. function 关键字;
  2. 函数名,应简明扼要且具有描述性,没有函数名就是匿名函数
  3. 参数列表,个数≥0,小括号不能省略;
  4. 函数体。
/* 基本语法 */
function 函数名(参数) { 函数体 }

声明与调用

  1. 通过 function 声明创建一个函数,基本语法如上所示;
  2. 通过 函数名(相应参数) 进行调用。
function isPrime(n) {
  for (let i = 2; i < n / 2; i++) {
    if ( n % i == 0) return false;
  }
  return true;
}

isPrime(11)
isPrime(111)

返回值

  1. 通过 return needed 返回需要的数据;
  2. 通过 return ; 返回空,空也是一个数据,null/undefined,严格来说是未定义undefined
  3. 函数体执行完 return 语句后,立即结束调用并返回数据。
function one() { return 1; }
function nothing() { return ; }		// 亦或是 function nothing() {  }
one() === 1				// true
nothing() == null		// true
nothing() === undefined	// true 严格等于

匿名函数

  1. 没有函数名的函数,一般声明后立即调用;
  2. 通过函数表达式赋值给一个变量,后续调用;
  3. 常用于响应事件触发(如按钮点击事件)。
myButton.onclick = function () { alert('Push'); }	// myButton 需先建立

函数表达式 - 另一种函数的创建方式

  1. 通过 function 引导的函数表达式,
    • 没有函数名,同时也就是匿名函数;
    • 有函数名,是具名函数表达式,其名只能在函数体内部使用;
  2. 箭头函数是一个函数表达式,也是匿名函数;
  3. 将函数表达式赋值给一个变量,这个变量就成为了可以被调用的函数了;
  4. 无论哪一种函数创建的方式,得到的函数,都是一个“值”:
    • 既可以被赋值等操作;
    • 也可以作为函数的参数,称为回调函数(回调)。
/* 下面三种函数的创建是等效的 */
function f() { return 1; }
let f = function () { return 1; }
lst f = () => { return 1; }
f()		// 1

let g = f;

/* 具名函数表达式 */
let factorial = function fac(n) { 
	if(n <= 1) return 1; 
	else return n * fac(n - 1); 
}
factorial(4)	// 24
fac(4)			// 报错,fac只能在内部使用
factorial.name  // 'fac', 函数名

/* 函数作为参数 */
function showYourFunc(n, fn) {
	if (fn !== undefined) alert(fn);
	else alert(n)
}
showYourFunc(111, )		// 弹窗显示111
showYourFunc(111, function() { /* 这是一个回调,但是没有内容只有注释 */ })	// 弹窗显示回调函数

箭头函数 - 一个特别的函数表达式

一个创建函数的更加简洁的方式,基本语法如下:

表达式可执行,所以是一个语句;
语句有值(undefined),所以也是一个表达式。

/* 函数体只有一个语句 */
() => 单个语句
单个参数 => 单个语句
(单个参数) => 单个语句
(参数1, 参数2) => 单个语句
/* 函数体有多个语句,使用{} */
() => { 多个语句 }
单个参数 => { 多个语句 }
(单个参数) => { 多个语句 }
(参数1, 参数2) => { 多个语句 }
  1. 箭头函数也是一个函数表达式,只能通过赋值给一个变量,后续才能被调用;
  2. 不需要 function 的引导,关键部分是箭头 =>,这是一个复合符号,中间不能有空格;
  3. 注意点:
    • 箭头前不能换行;
    • 参数只有一个时,() 可以省略;
    • 参数零个或多个时,()不能省略;
    • 函数体只有一个语句时,{} 可以省略;
    • 函数体有多个语句时,{} 不能省略;
    • 不能用作方法,没有参数对象,this 指向外层的对象;
    • 不能用作构造函数;
    • 不能用作生成器。
let f1 = () => alert('这是一个箭头函数');
let f2 = n => n * 2;
let f3 = (n) => n * 2;
let f4 = (...[a, b, c]) => a + b + c	// 扩展 Spread: ...[a, b, c] == a, b, c
f1() === undefined	// true
f2(5) == 10 			// = f3(5)
f4(1, 2, 3)

let g1 = () => { alert('111'); console.log('111'); }
let g2 = n => { if (n <= 2) return 1; else return g2(n-1) + g2(n-2); }	// 斐波那契数列
let g3 = (n) => { if (n <= 2) return 1; else return g2(n-1) + g2(n-2); }// 斐波那契数列
let g4 = (a, b) => {
	if (a == b) return a;
	else if (a < b) return g4(a, b - a);
	else return g4(a - b, b)											// 辗转相除法
}																		// 求最大公约数
g1() === undefined	// true
g2(4) == 3			// = g3(4)
g4(121, 11)			// 11

函数声明 VS 函数表达式

  1. 函数声明存在变量提升,所以可先调用,再声明,但不能重复声明,可以赋值给其他变量;
  2. 函数表达式必须创建后才能开始被调用,一般是即刻调用的场景,可以进行不同的或重复的赋值操作。

函数参数

  1. 在声明中,称 parameter,在调用时,称 argument
  2. 加入参数可以实现更复杂的逻辑功能;
  3. 参数可以是任何数据,包括对象、数组、函数等;
  4. 可以将函数体简化。
function calc(a, b, operator) {
	const ops = ['+', '-', '*', '/', '**', '%']
	if (ops.includes(operator)) {
		return eval(`${a}${operator}${b}`);
	}
	return a + b;
}

默认值

function name(parameter1=默认值1, parameter2=默认值2) { 函数体 }
  1. 旧 JS 需要在函数体里提供默认值,新 JS 可以在参数列表中直接给出;
  2. 没有自定义的话, 也有默认值,是 undefined
/* 旧 JS 的手段 */
function f(p) {
	if (p === undefined) p = 1;	// 以下两种亦可以
	// p = p || 1
	// p = p ?? 1
}

/* 新 JS 的手段 */
function f(p = 1) {
}

剩余参数

  1. 语法:...restParameter,放在参数列表的最后一个;
  2. 含义:将剩余的参数以数组的形式放在 restParameter
  3. 注意:...扩展(Spread)作用的优先级高,所以 ...[c, d] 会被扩展c, d,这样就失去意义了。
function f(a, b, ...restParameter) {	// restParameter只是一个名字,也可以叫其他的
	let s = 0;
 	for (const rp of restParameter) {
    	s += rp;
  	}
	return a + b + s;
}
f(1, 2)			// 3
f(1, 2, 3)		// 6
f(1, 2, 3, 4)	// 10

function g(a, b, ...[c, d]) {		// 等效于 function g(a, b, c, d)
	return a + b + c + d;
}

解构参数

  1. 解构参数的含义,就是把接收到的数据进行解构提取,提取指定的数据,再进行指定传参;
  2. 首先要简要认识解构和解构赋值。
解构与解构赋值
  1. 解构与组构、解包与打包等成双成对的概念,举一反三是很简单的;
  2. 解构是一个动作,是一个函数;
  3. 解构赋值主要是对数组和对象进行解构并赋值,这是一个提取数据的过程;
    • 数组,根据位置提取数据;
    • 对象,根据属性名匹配提取数据。
  4. 下面是常见的场景案例。
/* 简单的解构赋值 */
let [a, b] = [1, 2]			// a==1	b==2

/* 挑选式的解构赋值 */
let [a, b] = [1, 2, 3]		// a==1	b==2	挑取前两个
let [a, , b] = [1, 2, 3]	// a==1	b==3	挑第一、三个

/* 有默认值的解构赋值 */
let [a=1, b] = [, 2, 3]		// a==1	b==2	a有默认值
let [a=1, b] = [null, 2, 3]	// a==1	b==2	a有默认值
let [a=1, b] = [undefined,2]// a==1	b==2	a有默认值

/* 带有剩余的解构赋值 */
let [a, b, ...rest] = [1, 2, 3, 4, 5]	// rest=[3, 4, 5]
let [a, ,b, ...rest] = [1, 2, 3, 4, 5]	// rest=[4, 5]

/* 带有参数解构的解构赋值:...[c, d]先被解构成 c,d */
let [a, b, ...[c, d]] = [1, 2, 3, 4, 5] // c==3, d==4
let [a, ,b, ...[c, d]] = [1, 2, 3, 4, 5]// c==4, d==5

/* 一般对象的解构赋值(数组是一个特别的对象) */
let {a, b} = { a: 1, b: 2 }			// a==1, b==2, 名字相同则取值,否则undefined
let {a1, b1} = { a: 1, b: 2 }		// a1==b1==undefined
解构数组
  1. 指定位置上有数据,将其提取并指定传参;
  2. 指定位置上没有数据,将 undefined 传参。
function firstPlusThird([first, , third]) {
	return first + third;
}

firstPlusThird([1, 2, 10, 20])	// 1 和 10 提取,作为参数
解构对象
  1. 匹配成功,将对应属性值提取并指定传参;
  2. 匹配失败,将 undefined 传参。
let user = { id: 1, name: 'Lily' }, user2 = {};
function getUserId({ id=0 }) { return id; }

getUserId(user)		// 1
getUserId(user2)	// 0,默认值

作用域

  1. 局部变量:局部作用域里的变量;
  2. 全局变量:全局作用域里的变量,一般只有项目级的、公共的变量才会定义在全局;
  1. 内部变量:块内部的变量,像函数等一些块的内部变量,只能作用在块内;
  2. 外部变量:不被块限制的变量,常常是全局变量,也可以是从其他地方导入的变量,一般可以在块内部被操作,如循环体、函数体等都可以对外部变量进行操作。

给函数注释

  1. 函数专用的注释方法;
  2. 记录用法、参数和返回值。
/**
 * 返回 x 的 n 次幂的值。
 *
 * @param {number} x 要改变的值。
 * @param {number} n 幂数,必须是一个自然数。
 * @return {number} x 的 n 次幂的值。
 */
function pow(x, n) {
  ...
}
  • 11
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值