函数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 2 3 4 5 console.log('start');function f1() { console.log('1');}f1();
预扫描
1 2 3 4 5 console.log('start');function f1() { console.log('1');}f1();
执行
1 2 3 4 5 console.log('start');function f1() { console.log('1');}f1();
执行时,调用函数里的代码
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 15const 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 14const 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);