【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】作用链
【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
值,这在处理事件回调、类方法等情况时需要注意区别。
本文来自博客园,作者:Chimengmeng,转载请注明原文链接:https://www.cnblogs.com/dream-ze/p/18036249