函数Funtions

基本使用

样本

1 2 3 4 5
function add(v1, v2) { return v1 + v2;} add(1, 2)
分析

无参数类型校验,

无返回值类型声明,

文件里直接写,写了就能调

上下均可调用

1 2 3 4 5 < <
f1();function f1() { console.log('1');}f1();22

函数重载 与 默认参数

业务

业务 1

写一个函数初始化矩阵,期待之后调用时,可以使用尺寸创建,或者宽高创建。

1 2
const m1 = matrix(4) // 4 x 4;const m2 = matrix(4, 3) // 4 x 3;
业务 2

叠加一个参数,这个参数用来确定,初始化的时候填充什么

1 2
const m1 = matrix(4, 0) // 4 x 4; 里面全是 0const m2 = matrix(4, 3, true) // 4 x 3; 里面全是 true

重复声明

函数可以重复声明,声明后,在下方的函数生效,上方的失效

1 2 3 4 5 6 7 <
function f1() { console.log('1');}function f1(value) { console.log('2');}f1();2

相当于 js 中的函数 同名的只能出现一个

也就是说 js 不支持函数重载

改名

1 2
const m1 = matrixSquare(4) // 4 x 4;const m2 = matrixRect(4, 3) // 4 x 3;

超出传递

参数可以超出传递,不会报错

1 2 3 4 5 < <
function f(a, b) { console.log(a); console.log(b);}f(1, 2, 3);12
arguments

使用 arguments 依然可以找到超出传递的参数

1 2 3 4+5 6 < < <
function f(a, b) { console.log(a); console.log(b); console.log(arguments[2]);}f(1, 2, 3);123

传递不足

参数可以传递不足,不会报错

函数里会得到 undefined

1 2 3 4 5 < <
function f(a, b) { console.log(a); console.log(b);}f(1);1undefined

默认参数值Default Parameter Values

ECMAScript 6

当传递不足时,使用默认值

1 2 3 4 5 < <
function f(a = 3, b = 4) { console.log(a); console.log(b);}f(1);14

补充

剩余参数Rest Parameters

ECMAScript 6

... 开头的参数,可以接收传参时,剩余的参数,做为一个数组

1 2 3 4 5 < <
function f(a, ...rests) { console.log(a); console.log(rests);}f(1, 2, 3);1[2, 3]

剩余参数 x 打印

使用前
1 2 3
const width = 2;const height = 3;console.log(`${width}, ${height}`);
使用后
1 2 3
const width = 2;const height = 3;console.log(width, height);

数组展开在参数上

ECMAScript 6

数组展开,也可以用在参数传参上

将数组内的每个元素,依次传到函数的多个参数里

使用前
1 2 3 4 5 6 7 8
const numbers = [6, 7, 3, 8, 4]const capsule = [];capsule.push(0);for (const item of number) { capsule.push(item);}capsule.push(0);console.log(capsule);
使用后
1 2 3*4
const numbers = [6, 7, 3, 8, 4]const capsule = [];capsule.push(0, ...numbers, 0);console.log(capsule);

接收但没有返回

如果接收的函数,在调用时没有返回值

会接收到 undefined

1 2 3 4 5 6 =7 =
function f(a) { if (a === 1) { return 'returned'; }}var result1 = f(1);'returned'var result2 = f(2);undefined

对象作为参数

问题

1
const m2 = matrixRect(4, 3, false) // 4 x 3; 全是 false

可读性极低

解决方案

使用 对象构建语法 并将对象直接传到参数里

1 2 3 4 5
const m2 = matrixRect({ width: 4, height: 3, initialValue: false}); // 4 x 3; 全是 false
收益

可读性高,

无需记忆参数的顺序

实现代码

使用 对象拆解语法

1 2 3
function metricRect({width, height, initialValue}) { ...}
调用隐患

metricRect();

metricRect(null);

解决方案
1 2 3
function metricRect({width, height, initialValue} = {}) { ...}
追加问题

内部参数默认值

打印对象

使用前
1 2 3
function metricRect({width, height, initialValue} = {}) { console.log(width, height, initialValue);}
使用后
1 2 3
function metricRect({width, height, initialValue} = {}) { console.log({width, height, initialValue});}

对象作为返回值

1 2 3 4 5 6
function init() { return {x: 0, y: 0};} const zero = init();const {x, y} = init();

随堂练习

1 2 3 4 5 6
function partition(values) { return ...} const {evens, odds} = partition([6, 7, 3, 8, 4]);console.log({evens, odds})

函数作为变量

作用

函数可以作为变量,存储在

案例

使用前
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
const v1 = 3;const v2 = 4;const operator = "+" // + - * / let result;switch (operator) { case "+": result = v1 + v2; break; case "-" : result = v1 - v2; break; case "*": result = v1 * v2; break; case "/": result = v1 / v2; break;}console.log({result});
使用后
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
const v1 = 3;const v2 = 4;const operator = "+" // + - * / function add(v1, v2) { return v1 + v2;} function substract(v1, v2) { return v1 - v2;} function multiply(v1, v2) { return v1 * v2;} function divide(v1, v2) { return v1 / v2;} const operations = { "+": add, "-": substract, "*": multiply, "/": divide,} const result = operations[operator](v1, v2); console.log({result});
拆解
1 2
const operatorFunc = operations[operator];const result = operatorFunc(v1, v2);

函数表达式Function Expression

时机

当一个函数 只在代码里 只被引用 1 次时

案例

使用前
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
const v1 = 3;const v2 = 4;const operator = "+" // + - * / function add(v1, v2) { return v1 + v2;} function substract(v1, v2) { return v1 - v2;} function multiply(v1, v2) { return v1 * v2;} function divide(v1, v2) { return v1 / v2;} const operations = { "+": add, "-": substract, "*": multiply, "/": divide,} const result = operations[operator](v1, v2); console.log({result});
使用后
1 2 3 4 5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20 21 22*23*24*25*26 27 28 29 30
const v1 = 3;const v2 = 4;const operator = "+" // + - * / function add(v1, v2) { return v1 + v2;} function substract(v1, v2) { return v1 - v2;} function multiply(v1, v2) { return v1 * v2;} function divide(v1, v2) { return v1 / v2;} const operations = { "+": function add(v1, v2) { return v1 + v2; }, "-": function substract(v1, v2) { return v1 - v2; }, "*": function multiply(v1, v2) { return v1 * v2; }, "/": function divide(v1, v2) { return v1 / v2; },} const result = operations[operator](v1, v2); console.log({result});

收益

函数 可以 跟所存储的位置离得更近

二次扫描

js 在执行代码时,会产生两次扫描

第一次,预扫描

扫后,function 的 名字指向最后一次出现的函数执行代码

第二次,执行

再开始逐一执行代码

案例

1.

代码

1 2 3 4 5
console.log('start');function f1() { console.log('1');}f1();
2.

预扫描

1 2 3 4 5
console.log('start');function f1() { console.log('1');}f1();
3.

执行

1 2 3 4 5
console.log('start');function f1() { console.log('1');}f1();
4.

执行时,调用函数里的代码

1 2 3 4 5 6
console.log('start');function f1() { console.log('1');}var a = 1;f1();

函数表达式 vs 函数声明

1 2 3 4 5 6 7
function f1() { console.log("函数声明");} const v2 = function f2() { console.log("函数表达式");}

只要没有一开头就写 function 的,就可以认为是函数表达式

函数表达式 x 作用域

函数表达式 存储在变量里,执行之前不可以调用。

1 2 3 4 5 6 7!8 9 10 11
f1();function f1() { console.log("函数声明");}f1(); v2();const v2 = function f2() { console.log("函数表达式");}v2();

函数表达式 x 二次扫描

预扫描

1 2 3 4 5 6 7 8 9 10 11
f1();function f1() { console.log("函数声明");}f1(); v2();const v2 = function f2() { console.log("函数表达式");}v2();

执行

1 2 3 4 5 6 7 8 9 10
f1();function f1() { console.log("函数声明");}f1(); const v2 = function f2() { console.log("函数表达式");}v2();

没有人知道 f2 这个名字,f2 不可以在代码别的地方使用。

匿名函数表达式

因为函数表达式的函数名,不会被预扫描,且函数被存储在变量里,所以函数名没有任何意义。可以省略。

版型

1-2+3 4 5
const v2 = function f2() {const v2 = function () { console.log("函数表达式");}v2();

案例

使用前
1 2 3 4 5 6 7 8 9 10 11 12 13 14
const v1 = 3;const v2 = 4;const operator = "+" // + - * / const operations = { "+": function add(v1, v2) { return v1 + v2; }, "-": function substract(v1, v2) { return v1 - v2; }, "*": function multiply(v1, v2) { return v1 * v2; }, "/": function divide(v1, v2) { return v1 / v2; },} const result = operations[operator](v1, v2); console.log({result});
使用后
1 2 3 4 5 6*7*8*9*10 11 12 13 14
const v1 = 3;const v2 = 4;const operator = "+" // + - * / const operations = { "+": function (v1, v2) { return v1 + v2; }, "-": function (v1, v2) { return v1 - v2; }, "*": function (v1, v2) { return v1 * v2; }, "/": function (v1, v2) { return v1 / v2; },} const result = operations[operator](v1, v2); console.log({result});

收益

不用想函数起什么名字

箭头函数Arrow Function

是什么

函数表达式的简写

基本语法

1
const add = function( v1, v2 ) { return v1 + v2; }

可以简写为

1
const add = ( v1, v2 ) => { return v1 + v2; }

返回值额外简写规则

当函数内只有一行语句,并且是返回值时,可以继续简写

1
const add = ( v1, v2 ) => { return v1 + v2; }

可以简写为

1
const add = ( v1, v2 ) => v1 + v2;

单参额外简写规则

当参数只有一个时,可以继续简写

1
const isPositive = ( value ) => value > 0;

可以简写为

1
const isPositive = value => value > 0;

案例

使用前
1 2 3 4 5 6 7 8 9 10 11 12 13 14
const v1 = 3;const v2 = 4;const operator = "+" // + - * / const operations = { "+": function (v1, v2) { return v1 + v2; }, "-": function (v1, v2) { return v1 - v2; }, "*": function (v1, v2) { return v1 * v2; }, "/": function (v1, v2) { return v1 / v2; },} const result = operations[operator](v1, v2); console.log({result});
使用后
1 2 3 4 5 6*7*8*9*10 11 12 13 14
const v1 = 3;const v2 = 4;const operator = "+" // + - * / const operations = { "+": (v1, v2) => v1 + v2, "-": (v1, v2) => v1 - v2, "*": (v1, v2) => v1 * v2, "/": (v1, v2) => v1 / v2,} const result = operations[operator](v1, v2); console.log({result});

函数作为参数

既然函数可以存储在变量里,函数就可以叠加变量的规则,比如变量可以传输到参数里

案例

1 2 3 4 5 6 7 8 9
function onlyPositive(items) { const result = []; for (const item of items) { if (item > 0) { result.push(item); } } return result;}
1 2 3 4 5 6 7 8 9
function onlyEvens(items) { const result = []; for (const item of items) { if (item % 2 === 0) { result.push(item); } } return result;}

只有判断依据不一样

更改后
1 2 3 4 5 6 7 8 9 10 11 12 13
function filter(items, accept) { const result = []; for (const item of items) { if (accept(item)) { result.push(item); } } return result;} const items = [6, 7, 3, 8, 4];const r1 = filter(items, v => v > 0);const r2 = filter(items, v => v % 2 === 0);

回调函数Callback Functions

accept 就是回调函数,因为 甲方 将一个函数 传给乙方后,乙方会在内部执行时,调用甲方提供的函数 辅助运行。

TS 函数类型声明

1 2 3 4 5
type AcceptFunc = (v) => boolean; function filter(items, accept: AcceptFunc) { ...}

横版编程思维

filter, map, some

系统提供的便捷函数

MDN Array filter

MDN Array some

MDN Array map

MDN Array reduce

MDN Array forEach

横版编程思维

业务

基于以下文本,求出年龄在 18 - 24 岁之间的存款总额

25 | $2,000
32 | $3,500
19 | $1,000
45 | $5,000
21 | $2,500
27 | $3,000
17 | $1,500
41 | $4,500
29 | $3,500
39 | $4,000
以前
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
const text = ""; const lines = text.split("\n); let sum = 0;for (const line of lines) { const parts = line.split(" | "); const age = parseInt(parts[0]); if (age < 18 || age > 24) { continue; } const amount = parseInt(parts[1].substring(1)); sum += amount;}
使用横版思维后
1 2 3 4 5 6 7 8 9 10 11 12 13 14
const text = ""; const lines = text.split("\n); const sum = lines .map(line => { const parts = line.split(" | "); return { age: parseInt(parts[0]), amount: parseInt(parts[1].substring(1)), } }) .filter(info => info.age >= 18 && info.age <= 18) .reduce((next, current) => current + next, 0);

函数作为返回值

ZZAX 微信公众

文档一更新,立刻告诉你