函数
函数定义
- 语法
function [函数名]\(参数列表\){
函数体;
return 返回值;
}
- 函数定义,会声明提升;函数表达式不会。
注意:虽然js有此特性,但这是bug的来源,务必先定义后使用。
console.log(add2);
function add2(x,y){ //x,y为形参定义,不需要使用关键字声明
console.log('add3','-'.repeat(10));
return x / y;
};
函数表达式
- 匿名函数表达式
注意:js中的匿名函数表达式,可以写多条执行语句
// console.log(add); // 匿名函数表达式--抛异常,ReferenceError: Cannot access 'add' before initialization
const add = function (x,y){
return x + y;
};
console.log(add(3,5))
- 有函数名的函数表达式
// 有函数名的函数表达式
// console.log(add1); // 函数表达式 -- 抛异常,ReferenceError: Cannot access 'add1' before initialization
const add1 = function fn(x,y){ //注意,此时函数名fn只能在函数调用
console.log(add1 === fn); // true,两者指向同一个函数对象
return x * y;
};
console.log(add1(4,5));
// console.log(fn(4,5)); // 抛ReferenceError: fn is not defined
高阶函数
- 不要受x++和++x的影响,每次迭代取一个元素传入单参匿名函数,++的只是当权元素,不会影响其他迭代的元素。
- return x++,先引用变量x,后加1;即返回x对应的值,加1之后的值没人应用,舍弃了。
const map=function (arr,fn){
let new_arr = new Array(); // 注意:js中数组不用和python中预先生成[0,0,0],直接通过属性遍历赋值即可
for (let a in arr){
new_arr[a] = fn(arr[a])
};
return new_arr; // js中没有显示的调用return语句,隐式返回undefined
};
arr = [1,2,3,4,5]
console.log(map(arr,x => x+1));
console.log(map(arr,x => {return x++})); // 1,2,3,4,5
console.log(map(arr,x => ++x)); // 2,3,4,5,6
console.log(map(arr,function (x){return x+=1})); // x += 1 --> x = x + 1,表达式的值
- 斐波那契数列
function fn(x){ // fibonacci,递归
let f1 = 0;
let f2 = 1;
switch (x){
case 0:
return 0
break
case 1:
case 2:
return 1
break
default:
return fn(x-1) + fn(x-2)
};
}
for (let i=0;i<=10;i++){
console.log(fn(i))
}
箭头函数
- 箭头函数就是匿名函数,它是一种更加精简的格式
- 去掉关键字function;
- 参数列表和函数执行块中间加符号 =>
- 箭头函数参数
- 函数没有参数,必须使用();
- 函数有多个参数,使用逗号间隔,且必须使用();
- 特例:函数只有一个参数,参数列表的小括号可以省略
- 箭头函数返回值
-
函数有多个执行语句
- 执行语句之间使用分号间隔(逗号也可以),必须使用{};
- 如果有返回值,显示使用return语句,且执行语句和return语句之间和使用;间隔
大括号没有显示的return语句,即大括号内隐式调用return语句,返回值为undefined
-
函数只有一个执行语句
- 执行语句为显示的return语句,大括号可以省略,且返回return值
-
举例:
console.log((x => {return 1})(1))
,返回值为1;console.log((x => {1})(1))
,返回值为undefined
-
注意:箭头函数返回值中,只要有return语句,就不能去除大括号
// 匿名函数
console.log((function (x,y){return x+y})(1,2));
// 箭头函数
console.log((() => {let x = 4; return x })()); // 无参、多执行语句
console.log(((x,y) => {x++ y--; return x+y })(2,3)); // 多参、多执行语句
// 函数只有一行执行语句,
console.log((() => 6)()); // 无参、单个return语句
console.log(((x,y) => x+y)(3,4)); // 多参,单个执行语句
// 参数列表中只有一个参数,参数列表可以省略小括号
console.log((x => {x += 8; return 8})(0)); // 注意:x变量在形参定义的时候,已经声明为函数的局部变量
console.log((x => {x += 8})(1)); // undifined,等价x => {x += 8;return undifined}
console.log((x => --x)(10)); // 等价 x => {retrun --x}
函数参数
形参定义和传参
- 形参定义
- 一个参数占一个位置,支持默认参数;
- js中不限制默认参数的位置
- 形参定义中,…形参变量格式称为剩余变量
- 注意:形参定义相当于使用 let 定义每个形参;
- 函数内部,不可以使用 let / const覆盖;
- 函数内部,可以使用 var / 隐式定义覆盖;
- 举例如下
函数内定义
function add(x,y){
// let x = 1; // SyntaxError: Identifier 'x' has already been declared
// var y = 2; // 覆盖成功
var y; // 声明成功,了解
// y = 3; // 覆盖成功,注意:此处不是隐式定义,仅仅是局部变量x的覆盖
// const y = 10; // SyntaxError: Identifier 'y' has already been declared
return x+y;
}
// 函数外定义
console.log(add(4,5))
let x = 10;
// var x = 15; // SyntaxError: Identifier 'x' has already been declared
// const x = 15; // SyntaxError: Identifier 'x' has already been declared
// x = 15; // 覆盖成功,注意:此处不是隐式定义
console.log(x)
注意: 1. 虽然不限制默认参数的位置,但是好的书写规范是 位置参数 -> 默认参数 -> 剩余参数; 2. 形参定义之后,不要再使用var定义覆盖(使用let / const抛异常),直接使用形参变量即可
函数传参
- JS中没有Python中的关键字传参;
- JS中的实参传参,只做参数位置的对应(相当于只有Python中的位置传参)
- 实参个数少于形参个数,未传入实参的形参等价undefined,故返回值为NaN;
- 实参个数多于形参个数
- 形参中有剩余参数,从左至右一一对应,多余的实参被剩余参数收集
- 形参中没有剩余参数,从左至右一一对应,多余的实参舍弃
function add(x=1,y){
return x + y;
}
console.log(add(1)); // Nan,等价add(1,undefined)
console.log(add(1,1)); // 2,等价add(1,1)
console.log(add(y=2)); // NaN,等价add(2,undefined)
console.log(add(y=1,x=2)); // 3,等价add(1,2)
console.log(add(x=2,2)); // 4,等价add(2,2)
console.log(add(2,3,7,9,8)); // 5,等价add(2,3),多余元素舍弃
console.log(add(a=10,b=5,x=3,y=3)); // 15,等价add(10,5),多余元素舍弃
可变参数
- JS使用…表示可变形参,也称为rest parameters剩余参数;
- JS使用…表示实参解构,使用原则同Python
const sum = function (...args){ // 形参定义, ...变量的格式,为剩余参数
let count = 0;
for (let i of iterable){
count += i;
};
return count;
}
var arr = new Array(1,2,3,4,5,6)
// 注意,剩余参数收集的逗号分割的多余实参,如果实参为iterabel,只会将其视作一个整体
console.log(sum(...arr)) // 实参传参, ...变量的格式,为参数解构
arguments对象
- 函数的所有参数会被保存在一个arguments的键值对对象中
- ES6之前,arguments是唯一可变参数的实现;
- ES6之后,不推荐,建议使用可变参数,只是为了兼容而保留
注意,使用箭头函数,取到的arguments不是我们想要的
function test(p1,...args){
console.log(p1);
console.log(args);
console.log('---'.repeat(15));
console.log(arguments) // 键值对对象,iterable
for (let x of arguments){ // 遍历值
console.log(x)
};
}
console.log(test('test',1,2,3,4,5,6))
const test = (p1,...args) =>
{console.log(p1);
console.log(args);
console.log('---'.repeat(15));
console.log(arguments); // 不是想要的结果
return undefined
}
console.log(test(...['test',1,2,3,4,5,6])) // 参数结构
返回值
- 类C的语言,都有一个概念:表达式的值
- 赋值表达式的值:等号右边的值
- 逗号表达式:使用逗号运算符分隔的表达式
- 类C语言,都支持逗号表达式
- 逗号表达式的值:最后一个表达式的值
- 函数返回值
- python和js的函数返回值均为单值
- python:return 1,2,貌似是多值,本质是一个值,封装成元组
- js:reutnr 1,2, 不会封装成一个容器返回,返回值为逗号表达式的最后一个表达式的值,即2
注意:不仅仅是函数返回值为逗号表达式的最后一个表达式的值,只要出现逗号表达式,返回值都是最后一个表达式的值
function test(){
return x=1,y=2,z=x+y
};// 3
console.log(test());
var a = function (){
return 4,5,10
};
console.log(a()); // 10
a = () => {return 4,5,'a'};
console.log(a()); // 'a'
a = () => (x=1,y=2,z=x+y); // 3
console.log(a());
const b = (x=5,y=6,true); // true
console.log(b);
const c = (123,true,z='test'); // test
console.log(c);
作用域
- function是函数的定义,一个对立的块作用域
- 函数内严格定义的变量,对外不可见;
let、const不能提升声明,且不能突破任何块作用域,推荐使用
- var可以提升声明,但是仅限于函数块作用域,不能突破函数作用域
- 函数内隐式定义的变量,为全局变量,对外可见;严格模式下,隐式定义会抛异常,会造成全局变量污染,建议少用
- if、while、for块作用域
- let、const严格定义的变量,不能突破块作用域,对外不可见
- var、隐式定义的变量,可以突破块作用域,对外可见
块作用域外对块作用域可见,效果同Python
// 作用域测试
x = 500;
var j = 'jjj';
var k = 'kkk';
function fn(){
let z = 'zzz';
{
var o = 'ooo';
show(1,x); // 500
t = 'ttt';
let p = 'ppp';
}
var y = 300;
show(2,z); // zzz
show(3,x); // 500
show(4,o); // ooo,var定义的变量,可以突破块作用域
show(5,t); // ttt
// show(6,p); // let定义的变量,突破不了块作用域
{
show(7,y); // 300
show(11,t); // ttt
show(12,z); // zzz
}
j = 'aaa'; // 全局变量覆盖
var k = 'bbb'; // 定义一个新的局部全局变量k
show(20,j); // aaa
show(21,k); // bbb
}
fn();
show(22,j); // aaa
show(23,k); // kkk
// show(13,y); // var定义的变量,突破不了函数块作用域
show(14,t); // ttt
// show(15,o); // var定义的变量,突破不了函数块作用域
show(16,z); // var变量提升,undefined
var z = 10;
const m = 1
// m = 2 // 常量不能重新赋值
严格模式
“use strict”; --> 放到函数首行,或者js脚本首行
"use strict";
test = 100; 无法定义,抛ReferenceError: test is not defined
异常
抛出异常
- js的异常语法和Java相同,使用关键字throw(抛出)。
js中,使用关键字throw可以抛出任意对象的异常
// throw Error; // 不是想要的,请使用new关键字,实例化对象
// throw Error('test errror'); // 不是想要的,不推荐
// throw ReferenceError('Ref Error Test'); // 不是想要的,不推荐
// throw new Error('new error'); // 推荐,异常抛出的地方,终止执行语句
// throw new ReferenceError('Ref Error Test'); // 推荐
// throw 1; // Error:1
// throw "not ok"; // Error
// throw [1,2,3]; // Error
// throw {'a':1}; // Error
// throw () => {}; // Error
捕获异常
- 捕获语句
- try…catch捕获异常;
- tyr…catch…finally捕获异常,finally最终一定执行;
- 特例: try…finally不捕获异常
- 注意:
- try不能单独使用;
- catch不支持类型,即至多一个catch语句;
- 可以在catch语句内,自行处理异常
- catch语句后面的()中,可以写任意变量名(常用error),指向捕获的异常对象
try {
// throw new Error('new Error');
// throw new ReferenceError("Ref Error")
throw ReferenceError('Ref Error') // 效果同上,不建议使用,请使用上面的语法格式
// throw 1; // Error:1
// throw "not ok"; // Error
// throw [1,2,3]; // Error
// throw {'a':1}; // Error
// throw () => {}; // Error
} catch (error){
console.log(e);
console.log(typeof e);
console.log('---'.repeat(15));
console.log(e.constructor.name);
console.log(e.message);
} finally {
console.log('=====finished======')
}
生成器
生成器函数
- 语法
- function * 变量()
- 函数中使用yield关键字
- 注意
- inc().next()返回值为对象。value为值;done为对象的属性是否执行遍历完毕,遍历完为true,否则为true
- 生成器函数只是定义,调用后才是生成器,才能驱动
function * inc(){
let i = 0,j = 8
while (true){
yield i++ /* 生成器函数关键字yield,原则同python */
if (!j--) return 100 /* !j--,相当于!=j--,即当j为0时,退出循环 */
/* 注意,js中的生成器可以拿到return语句的返回值,注意和pyton的区别 */
}
};
// 生成器函数使用next()方法驱动,和pythong有区别,此处存疑
let gen = inc();
for (let i=0;i<12;i++){
console.log(gen.next())
};
console.log(gen.next()) // 生成器迭代完,返回undefiend,不会抛异常
// 注意和python的区别
计数器
// 普通函数计数器
function count(){
let i = 0;
function inner(){
return i++;
}
return inner
};
var test = count()
for (let i=0;i<10;i++){
console.log(test())
};
// 生成器计数器
function * count(){
let i = 0;
while (1){
yield i++
}
};
var test = count();
for (let i=0;i<10;i++){
console.log(test.next())
};
- 生成器迭代完,返回undefined,不会抛异常;注意和python的区别