JS函数

函数

函数定义

  • 语法
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))
}

箭头函数

  • 箭头函数就是匿名函数,它是一种更加精简的格式
    1. 去掉关键字function;
    2. 参数列表和函数执行块中间加符号 =>
  • 箭头函数参数
    1. 函数没有参数,必须使用();
    2. 函数有多个参数,使用逗号间隔,且必须使用();
    3. 特例:函数只有一个参数,参数列表的小括号可以省略
  • 箭头函数返回值
    1. 函数有多个执行语句

      • 执行语句之间使用分号间隔(逗号也可以),必须使用{};
      • 如果有返回值,显示使用return语句,且执行语句和return语句之间和使用;间隔

      大括号没有显示的return语句,即大括号内隐式调用return语句,返回值为undefined

    2. 函数只有一个执行语句

      • 执行语句为显示的return语句,大括号可以省略,且返回return值
    3. 举例: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抛异常),直接使用形参变量即可

函数传参

  1. JS中没有Python中的关键字传参;
  2. JS中的实参传参,只做参数位置的对应(相当于只有Python中的位置传参)
    1. 实参个数少于形参个数,未传入实参的形参等价undefined,故返回值为NaN;
    2. 实参个数多于形参个数
      • 形参中有剩余参数,从左至右一一对应,多余的实参被剩余参数收集
      • 形参中没有剩余参数,从左至右一一对应,多余的实参舍弃
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的语言,都有一个概念:表达式的值
    1. 赋值表达式的值:等号右边的值
    2. 逗号表达式:使用逗号运算符分隔的表达式
      • 类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的区别

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值