JS 入门章节目录
第六章:函数
文章の目录
正文
JavaScript 函数是被设计为执行特定任务的代码块。
JavaScript 函数会在某代码调用它时被执行。
函数
函数的定义
函数(function),也可以被称之为方法(method)。
是一段预定义好,并且可以被反复使用的代码块,其中可以包含多条可执行语句。
注:
- 预定义好:事先声明好,但不被执行
- 反复使用:允许被多个地方(元素,函数中)所应用
- 代码块:允许包含多条可执行的代码
函数本质上是功能完整的对象。
为何使用函数?
能够对代码进行复用:只要定义一次代码,就可以多次使用它。
能够多次向同一函数传递不同的参数,以产生不同的结果。
函数的声明
JavaScript 函数通过 function 关键词进行定义,其后是函数名和括号。
函数名可包含字母、数字、下划线和美元符号(规则与变量名相同)。
语法:
function 函数名(){
// 可执行语句;
}
// 也可以通过函数表达式来创建函数
var 函数名 = function(){
// 可执行语句;
};
函数的调用
任何 JS 的合法位置处,都允许调用函数。
函数中的代码将在其他代码调用该函数时执行:
- 当事件发生时
- 当 JavaScript 代码调用时
- 自动的(自调用)
语法:
函数名();
注:
- 访问没有 () 的函数将返回函数定义。
带参数的函数
语法:
function 函数名(参数列表声明){
// 代码块(函数体,功能体,方法体)
}
// 参数列表:由逗号分隔的参数,由一个或多个变量名称组成
形参和实参
声明函数时定义的参数,可以称之为“形参”(形式参数)。
调用函数时实际传递的参数值,被称之为“实参”(实际参数)。
// 实参和形参需要一一对应
function sum(num1,num2){
console.log(num1);
console.log(num2);
console.log(num1+num2);
};
sum(5,6);
// 特殊情况:
// 实参可以传入任意数据类型
sum(5,true); // 6 (做 + 运算时,true 会被默认当作 1 参加运算)
sum(5,"li"); // "5li" (字符串拼接)
// 实参个数多于形参个数
sum(5,6,7,8); // 11 (多余的参数不参与函数的执行)
// 形参个数多于实参个数
sum(5); // NaN (num1 = 5,num2 没有赋值,为 undefined。所以结果为:NaN)(5 + undefined 的结果为 NaN)
实参变量的传递
传参时,实际上是将实参复制了一份副本传给了函数。
- 当实参为基本数据类型时:将实参变量保存的值复制了一份副本传递给形参,在函数体内对变量进行修改,实际上是不会影响到外部的实参变量的。
- 当实参为引用数据类型时:将实参变量保存的引用类型的地址复制了一份副本传递给形参,它们本质上指向同一块堆内存中存储的变量,所以对变量进行修改时,会影响到实参变量的。
(如需了解基本数据类型和引用数据类型的变量在内存中的存储方式,请阅读笔者另一篇文章:JavaScript 中变量在内存中的存储方式)
var n = 100; // 全局变量 n
function fun(n){ // 参数变量(形参)也是局部变量
n-=3; // 修改的是局部变量 n
console.log(n); // 输出的是局部变量 n
}
fun(n); // 按值传递,方法内输出 97
console.log(n); // 输出全局变量的值:100
//
var arr1 = [2,6,4,8,1,9,5,3,7];
function bubbleSort(arr){
for(var i = 0;i < arr.length-1;i++){
for(var j = 0;j < arr.length-1-i;j++){
if(arr[j] > arr[j+1]){
var temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
console.log(arr);
}
bubbleSort(arr1); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log(arr1); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
// 传参时,将全局变量 arr1 的地址赋给了 arr (实参,局部变量),所以函数内的 arr 和 全局变量 arr1 指向同一块堆内存中的数据,所以函数内对数组进行冒泡排序,也会改变全局变量 arr1 的值。
带返回值的函数
如需返回一个返回值给调用者,请使用 return 关键词。
声明:
function 函数名( 0 或多个参数){
// 代码块;
return 值;
}
注:
- 当 JavaScript 到达 return 语句,函数将停止执行(return; 语句没有返回值(undefined),但是会立即停止执行函数)。
用作变量值的函数
函数的使用方法与变量一致,在所有类型的公式,赋值和计算中。
// 使用变量来存储函数的值:
var num = sum(1,2,3);
// 把函数当做变量值直接使用:
console.log("和 = " + sum(1,2,3));
变量的作用域
作用域就是变量或函数的可访问范围。它控制着变量或函数的可见性和生命周期。
在 JS 中,变量或函数的作用域可分为:
- 函数作用域 —— 只在当前函数内可访问
- 全局作用域 —— 一经定义,代码的任何位置都可以访问
局部变量
在 JavaScript 函数中声明的变量,会成为函数的局部变量(拥有函数作用域)。
函数作用域中的变量(局部变量)只在当前函数内可访问到,离开此范围就无法访问了。
局部变量在函数开始时创建,在函数完成时被删除。
全局变量
全局(在函数之外)声明的变量,会成为全局变量(拥有全局作用域)。
全局作用域中的变量,称之为“全局变量”,在代码的任何位置都能访问。
声明提升
JS 在正式执行之前,会将所有 var 声明的变量和 function 声明的函数,预读到所在作用域的顶部。
但是,对变量的赋值,还保留在原来的位置处。
console.log(a);//不会出错,输出undefined
var a = 100;
console.log(a);//100
//等同于
var a;//仅声明提前
console.log(a);//undefined
a=100;//赋值仍保留在原位置
console.log(a);//100
递归函数
什么是递归函数
如果一个函数在内部可以调用其本身,那么这个函数就是递归函数。
简单理解:函数内部自己调用自己。
注:
- 递归函数的作用和循环效果类似。能用循环解决的问题,尽量不用递归。
- 递归很容易发生“栈溢出”(stack overflow)错误,所以必须要有退出条件 return。
利用递归实现 n 的阶乘
// n! = n * (n-1)!
function factorial(n){
if(n == 1){
return 1;
}else{
return n * factorial(n-1);
}
}
factorial(5);
匿名函数和立即执行函数
匿名函数
没有函数名的函数。
匿名函数不能单独定义与使用。
匿名函数的应用场景:
- 用于函数表达式
- 作为返回值
- 用于定义对象方法
- 作为回调函数
- 用于立即执行函数
- 用于DOM元素注册事件
- 其他 ...
立即执行函数
立即执行函数是一种在定义后就会立即执行的函数,其实质是一种语法。
立即执行函数的形式:
- 常用形式一:将匿名函数包裹在一个括号运算符中,后面再跟一个括号。
- 常用形式二:匿名函数后面跟一个括号,再将整个包裹在一个括号运算符中。
- 其他形式:可以用 !、+、-、~ 运算符替代常用形式一中的第一个括号。
立即执行函数的作用:
立即执行函数最本质的作用是:创建一个独立的作用域。利用这一功能,可以:
- 初始化数据和页面(只执行一次)
- 模块化开发中,定义私有变量,防止污染全局(独立作用域)
- 解决闭包中的状态保存问题(常见的一个函数内部返回多个函数,调用这些函数,打印父函数内部变量的问题)
Arguments 对象
描述
arguments 是一个对应于传递给函数的参数的类数组对象。
如果调用的参数多于正式声明接受的参数,则可以使用 arguments 对象。这种技术对于可以传递可变数量的参数的函数很有用。使用 arguments.length 来确定传递给函数参数的个数,然后使用 arguments 对象来处理每个参数。
arguments 对象是所有(非箭头)函数中都可用的局部变量(arguments 对象只能在函数内使用)。你可以使用 arguments 对象在函数中引用函数的参数。此对象包含传递给函数的每个参数,第一个参数在索引 0 处。
注:
- “类数组”(不是一个 Array 但类似于 Array)意味着 arguments 有长度(length)属性,并且属性的索引是从零开始的,但是它没有 Array 的内置方法。
- typeof arguments 返回 'object'。
遍历参数求和
// 求 n 个数相加的和
function add(){
var sum = 0;
for(var i=0; i<arguments.length; i++){
sum += arguments[i];
}
return sum;
}
函数的注释
使用 JSDoc
(如需了解有关 JSDoc 更多内容,请阅读笔者另一篇文章:JSDoc)
/**
* @author li
* @version 1.01.1
* @description 求两数之和
* @param {Number} num1 操作数1
* @param {Number} num2 操作数2
* @return {Number} num1 和 num2 的和
* @example add(5,6)
*/
function add(num1,num2){
var sum = num1 + num2;
return sum;
}
闭包
(如需了解闭包相关内容,请阅读笔者另一篇文章:闭包)