【15.0】JavaScript之函数

【一】函数

  • 函数function,也叫做功能,方法 ,函数可以将一段代码封装起来,函数就具备了特定的功能

  • 函数的作用就是封装一段代码,将来可以重复使用

  • 在Python中定义函数需要用 def

  • 在JavaScript中定义函数需要用 function

【二】函数声明

【1】先声明再调用

  • 函数必须先声明,才能使用

【2】函数声明的语法

function 函数名 (参数) {代码体}
function 函数名(形参,形参,形参,形参...){
    函数体代码
}
  • 函数声明的时候函数不会立即执行,只有调用的时候才会执行

【三】函数调用

  • 函数名加()
  • 函数调用时,会立即执行,代码体里面的代码
  • 可以多次调用,每次调用是独立不相关的
  • 函数的执行跟定义位置无关,只与调用的位置有关

【1】无参函数

// 无参函数
function func1(){
    console.log("我是无参函数")
}

func1()

【2】有参函数

// 有参函数
function func2(a,b){
    console.log(a,b)
};

func2(1,2)
function func2(a,b){
    console.log(a,b)
};

func2(1,2)
// VM3577:2 1 2

func2(1,2,3,4,5,6,7)
// VM3577:2 1 2

func2(1)
//VM3577:2 1 undefined
  • 在js中传多了只拿对应的数据
  • 在js中传少了也不会报错

【四】函数的参数

【1】接口

  • 函数预留了一个接口,专门用于用户自定义内容,让函数的执行结果发生变化
  • 接口:指的就是函数的参数

【2】参数类型

  • 函数的阐述可以没有,可以是多个,多个参数 之间用逗号分隔
  • 函数的参数根据书写位置的不同,名称也不同

(1)形式参数

  • 是定义在函数参数体力的参数

(2)实际参数

  • 在函数调用的时候传入的参数

【3】小结

  • 实际参数跟形式参数会有一个参数传递的过程
  • 形参与实参的个数可以不一样

【五】函数的返回值

【1】返回值机制

  • 在JavaScript中,函数的主要作用之一就是处理和传递信息。
  • 函数内部通过return语句可以将计算结果或特定值返回给调用者。
function addNumbers(a, b) {
    return a + b;
}

let result = addNumbers(3, 5); // result 的值为 8,因为函数返回了 a + b 的计算结果
console.log(result);

【2】终止函数执行

  • return除了用于返回值外,还可以用来提前结束函数的执行流程。
  • return被执行后,函数会立即停止剩余的代码执行,并直接返回指定的值。
function stopExecution() {
    if (false) { // 假设某个条件不成立
        return; // 结束函数执行
    }
    console.log('This line will not be executed');
}
stopExecution(); // 不输出任何内容,函数直接结束

【3】默认返回值为 undefined

  • 如果函数没有显式地使用return语句给出一个值,那么它的默认返回值是undefined
function emptyFunction() {} // 没有return语句
let returnedValue = emptyFunction();
console.log(returnedValue); // 输出:undefined

【4】函数返回值的应用

  • 函数的返回值不仅可以用作其他变量的赋值,还可以作为其他函数的参数。
  • 例如,在排序算法中,我们可以创建一个比较函数并将其作为Array.prototype.sort()方法的参数,利用其返回值来决定数组元素的排列顺序
function compareNumbers(a, b) {
    return a - b; // 返回a与b的差值,sort()会根据这个差值决定元素的顺序
}

const numbers = [5, 2, 9, 1, 5, 6];
numbers.sort(compareNumbers); // 排序后的数组:[1, 2, 5, 5, 6, 9]

【六】函数表达式

【1】概述

  • 函数表达式是函数定义的另一种形式,就是将函数的定义,匿名函数赋值给一个变量
  • 函数定义赋值给一个变量,相当于将函数的整体矮化成了一个表达式
  • 调用函数表达式方法是给变量名加()执行,不能使用函数名()执行
  • 通常函数表达式都会使用匿名函数

【2】示例

let multiplyByTwo = function(x) {
    return x * 2;
};

console.log(multiplyByTwo(4)); // 输出: 8
  • 当我们将上面的函数赋值给变量 multiplyByTwo
  • 我们可以通过该变量名 multiplyByTwo() 来调用这个匿名函数
  • 而不能直接使用 function(x) 这样的函数名来调用。

【七】函数的数据类型

【1】概述

  • 函数是一种数据类型Function
  • 由于函数是一种数据类型,可以参与其他程序
  • 可以把函数作为另一个函数的参数,也可以把函数作为返回值

【2】示例

  • 函数可以与其他数据类型一样进行赋值、比较和传递等操作
function sayHello(name) {
    console.log(`Hello, ${name}!`);
}

let greet = sayHello; // greet 变量现在引用了 sayHello 函数
greet("Alice"); // 输出: Hello, Alice!

// 函数作为参数传递
function executeFunction(func, arg) {
    func(arg);
}

executeFunction(sayHello, "Bob"); // 输出: Hello, Bob!
  • 函数还可以作为其他函数的返回值,从而实现更高阶函数的功能。例如,一个工厂函数可以生成特定类型的函数
function createAdder(n) {
    return function(x) {
        return x + n;
    };
}

let addFive = createAdder(5);
console.log(addFive(3)); // 输出: 8

【八】arguments对象

【1】概述

  • js中arguments对象是一个特殊的对象,实际上是当前函数的一个内置属性
  • arguments对象会接收所有的实参
  • length属性可以获取参数的个数

【2】示例

function func3(a,b,c){
    console.log(arguments)
    console.log(a,b,c)
};

func3(1,2,3,4,5,6,7)

/*
VM3668:2 Arguments(7) [1, 2, 3, 4, 5, 6, 7, callee: ƒ, Symbol(Symbol.iterator): ƒ]
VM3668:3 1 2 3
*/
  • 能够获取到函数接收到的所有参数

【3】用途

function func4(a,b,c){
    if (arguments.length > 3){
        console.log("传多了")
    }else if (arguments.length < 3){
        console.log("传少了")
    }else{
        console.log(a,b,c)
    }
};

【4】扩展

function sum(...args) {
    let total = 0;
    for (let i = 0; i < arguments.length; i++) {
        total += arguments[i];
    }
    return total;
}

console.log(sum(1, 2, 3, 4, 5)); // 输出: 15
  • 在这个例子中,arguments 就是一个包含了所有传入参数的数组-like 对象,其 length 属性表示参数的实际数量。
  • 需要注意的是,虽然 arguments 在语法上看起来像一个数组,但它并不是真正的数组类型,因此不支持数组的所有方法。
  • 如果我们需要像数组那样操作参数,可以将其转换为真实的数组(如 Array.from(arguments) 或者使用扩展运算符 ...)。

【九】函数的递归

  • 函数的内部可以调用函数自己本身,这种做法称为递归
  • 函数递归需要考虑好结束递归的条件,不然会出现死递归导致程序崩溃,内存溢出
  • 递归一般是解决数学中的问题

【十】作用域

  • 变量起作用的范围
  • 函数内部是一个作用域
  • 作用域也可以用一对大括号包裹起来,在es6以前是没有块级作用域这个概念,在后续es6中补充
  • 变量退出作用域会自动销毁,全局变量只有关闭浏览器才会销毁

【十一】参数和函数的作用域

  • 函数的参数是局部变量,只能在函数内部使用
  • 函数内部的变量也是局部变量
  • 可以理解为函数体本身是一个独立的作用域

【十二】作用域链与遮蔽效应

【1】作用链

作用域链.drawio

【2】遮蔽效应

  • 程序在遇到一个变量时,使用时作用域查找顺序,不同层次的函数内都有可能定义相同名字的变量名,会优先从自己所在的层查找,如果没有查找到会依次往上找。
  • 整个过程中会发生内层变量遮蔽外层变量的效果叫做遮蔽效应

【3】补充

  • 例如: a=2 不写var声明会被转化成全局变量,可以理解为这个是个js的bug
  • 建议定义变量时必须使用声明关键字

【4】小结

  • 把变量的声明提升到最前面,只会提升声明,不会提升赋值
  • 把函数的生命提升到当前作用域的最前面,只会提升声明,不会提升调用
  • 先提升var,再提升function
  • 在预解析后,根据新的代码顺序,从上往下按照既定的规律执行

【十三】函数的全局变量与局部变量

var city = "BeiJing";
function f() {
  var city = "ShangHai";
  function inner(){
    var city = "ShenZhen";
    console.log(city);
  }
  inner();
}

f();  //输出结果是?
  • 输出结果是:"ShenZhen"。

  • 在函数 inner 内部,变量 city 的值被赋为 "ShenZhen"

    • 因此 console.log(city) 会输出 "ShenZhen"。
  • 由于函数 inner 是在函数 f 中定义的,并且在函数 f 中调用了 inner

    • 所以同样的规则适用于函数 f
  • 即使在函数 f 内部有一个与全局变量 city 同名的局部变量 city

    • 但在执行 console.log(city) 时,它将访问最内层的局部变量 city 的值
    • 也就是 "ShenZhen"。
var city = "BeiJing";
function Bar() {
  console.log(city);
}
function f() {
  var city = "ShangHai";
  return Bar;
}
var ret = f();
ret();  // 打印结果是?
  • 在你提供的代码中,输出结果将是:"BeiJing"。

  • 首先,全局变量 city 被赋值为 "BeiJing"。

  • 然后,在函数 f 内部定义了一个名为 city 的局部变量,将其值设置为 "ShangHai"。

  • 接下来,f 函数返回了函数 Bar 的引用。

  • 在执行 var ret = f() 后,ret 变量被赋值为 Bar 函数的引用

    • ret 现在指向函数 Bar
  • 最后,执行 ret()

    此时由于 ret 指向 Bar 函数

    • 所以会执行 Bar 函数,并打印当前作用域内的变量 city 的值,即输出 "BeiJing"。
  • 注意,这是因为 Bar 函数在执行时访问的是其定义时所处的作用域,即全局作用域,而非 f 函数内部的作用域。

var city = "BeiJing";
function f(){
    var city = "ShangHai";
    function inner(){
        console.log(city);
    }
    return inner;
}
var ret = f();
ret();
  • 输出结果将是:"ShangHai"。

  • 首先,全局变量 city 被赋值为 "BeiJing"。

    • 然后,在函数 f 内部定义了一个名为 city 的局部变量,将其值设置为 "ShangHai"。
    • 接下来,f 函数返回了函数 inner 的引用。
  • 在执行 var ret = f() 后,

    • ret 变量被赋值为 inner 函数的引用
    • ret 现在指向函数 inner
  • 最后,执行 ret()

    • 此时由于 ret 指向 inner 函数,所以会执行 inner 函数,并打印当前作用域内的变量 city 的值,即输出 "ShangHai"。
  • 注意,这是因为 inner 函数在执行时访问的是其定义时所处的作用域,即函数 f 内部的作用域。在该作用域中,变量 city 的值为 "ShangHai"。

【补充】匿名函数

【0】匿名函数介绍

  • 匿名函数,也称为无名函数或者lambda表达式,是一种没有名称的JavaScript函数定义形式。
  • 它们在编程中广泛用于一次性使用的简单计算、事件处理、高阶函数等场景,因为它们不需要被赋给特定变量或者作为对象的方法来使用。

【1】基本语法

(1)基本形式

function() { /* 函数体 */ }

(2)自动执行的匿名函数

(function() {
  console.log("This is an anonymous function.");
})();

(3)将匿名函数赋值给变量

var a = function() {
  console.log("Variable 'a' holds an anonymous function.");
}();

(4)通过立即调用表达式

  • IIFE, Immediately Invoked Function Expression
var b = (function() {
  console.log("Variable 'b' holds an immediately invoked anonymous function.");
})();

【2】特点与用途

  • 无名/临时作用域:由于匿名函数没有名字,它们的变量和参数只在其所在的作用域内有效,不会污染全局命名空间,适用于一些短暂或局部需求的场合。

  • 闭包:当匿名函数作为另一个函数的参数传递或返回时,它可以访问到外部函数的局部变量和参数,形成闭包。这对于实现数据封装、模块化编程以及实现私有变量等功能非常有用。

  • 简便的函数创建:在某些情况下,如高阶函数应用、事件处理器等,使用匿名函数可以避免额外的声明和赋值操作,使得代码更加简洁高效。

  • 立即执行:通过使用立即调用表达式(IIFE),匿名函数可以在定义的同时就被执行,比如初始化数据、防止变量污染全局命名空间等场景。

【补充】箭头函数

【0】什么是箭头函数

  • 箭头函数(Arrow Function)是JavaScript ES6引入的一种新的函数定义语法,相比于传统函数表达式,它具有更简洁和易读的特点。

【1】基本语法

(1)单个参数

  • 箭头函数用于表示接受一个参数并直接返回该参数值的情况
// 使用 =>
var func1 = (v) => v;
// 或者等价的传统写法
var func1 = function(v) {
  return v;
};
  • 当只有一个参数时,可以省略括号(), 但为了代码可读性,推荐保留。

(2)多个参数

  • 如果需要处理多个参数,括号()不能省略,参数之间用逗号,分隔,返回值后面同样跟=>
// 处理两个参数
var func2 = (args1, args2) => args1 + args2;
// 或者等价的传统写法
var func2 = function(args1, args2) {
  return args1 + args2;
};

【2】特殊情况

(1)没有参数

  • 如果函数体为空或为 return 关键字后的表达式,可以省略花括号{}return 关键字。

    // 没有参数,返回 undefined
    var func3 = () => {};
    
    // 没有参数,返回一个简单的值
    var func4 = () => "Hello World";
    

(2)只有一条语句

  • 如果函数体只包含一条语句且不涉及复杂逻辑,可以将这条语句后的分号;去掉,并使用=代替return关键字,使表达式更加紧凑。

    // 返回数字10
    var func5 = value => value * 10;
    
    // 相当于
    var func5 = value => value * 10; // 注意这里去掉了分号
    

(3)this绑定

  • 在箭头函数中,this的值是在创建函数的时候确定的,而不是在调用时动态绑定,通常指向定义函数的对象(即上下文环境)。
  • 这意味着,箭头函数不会像普通函数那样拥有的this值,这在处理事件回调、类方法等情况时需要注意区别。
posted @ 2024-02-27 09:54  Chimengmeng  阅读(18)  评论(0编辑  收藏  举报