Function 函数
函数是 JavaScript 中的基本构建块之一。 JavaScript 中的函数类似于过程——一组执行任务或计算值的语句,但要使过程符合函数的条件,它应该接受一些输入并返回一个输出,其中两者之间有一些明显的关系。输入和输出。要使用函数,您必须在要调用它的范围内的某个地方定义它。
一个JavaScript 函数用function
关键字定义,后面跟着函数名和圆括号。
定义函数
函数声明
一个函数定义(也称为函数声明,或函数语句)由一系列的function
关键字组成,依次为:
- 函数的名称。
- 函数参数列表,包围在括号中并由逗号分隔。
- 定义函数的 JavaScript 语句,用大括号
{}
括起来。
例如,以下的代码定义了一个简单的square
函数:
function square(number) {
return number * number;
}
函数square
使用了一个参数,叫作number
。这个函数只有一个语句,它说明该函数将函数的参数(即number
)自乘后返回。函数的return
语句确定了函数的返回值
原始参数(比如一个具体的数字)被作为值传递给函数;值被传递给函数,如果被调用函数改变了这个参数的值,这样的改变不会影响到全局或调用函数。
函数表达式
虽然上面的函数声明在语法上是一个语句,但函数也可以由函数表达式创建。这样的函数可以是匿名的;它不必有一个名称。例如,函数square也可这样来定义:
const square = function (number) {
return number * number;
};
调用函数
定义一个函数并不会自动执行它。定义了函数仅仅是赋予函数以名称并明确函数被调用时该做些什么。调用
函数才会以给定的参数真正执行这些动作。例如,一旦你定义了函数square
,你可以如下这样调用它:
square(5);
函数一定要处于调用它们的域中,但是函数的声明可以被提升(出现在调用语句之后),如下例:
console.log(square(5));
function square(n) {
return n * n
}
函数域是指函数声明时的所在的地方,或者函数在顶层被声明时指整个程序。
函数提升仅适用于函数声明,而不适用于函数表达式。
函数作用域
在函数内定义的变量不能在函数之外的任何地方访问,因为变量仅仅在该函数的域的内部有定义。相对应的,一个函数可以访问定义在其范围内的任何变量和函数。换言之,定义在全局域中的函数可以访问所有定义在全局域中的变量。在另一个函数中定义的函数也可以访问在其父函数中定义的所有变量和父函数有权访问的任何其他变量。
// 下面的变量定义在全局作用域(global scope)中
var num1 = 20,
num2 = 3,
name = "Chamahk";
// 本函数定义在全局作用域
function multiply() {
return num1 * num2;
}
multiply(); // 返回 60
// 嵌套函数的例子
function getScore() {
var num1 = 2,
num2 = 3;
function add() {
return name + " scored " + (num1 + num2);
}
return add();
}
getScore(); // 返回 "Chamahk scored 5"
闭包
闭包是 JavaScript 中最强大的特性之一。JavaScript 允许函数嵌套,并且内部函数可以访问定义在外部函数中的所有变量和函数,以及外部函数能访问的所有变量和函数。
但是,外部函数却不能够访问定义在内部函数中的变量和函数。这给内部函数的变量提供了一定的安全性。
此外,由于内部函数可以访问外部函数的作用域,因此当内部函数生存周期大于外部函数时,外部函数中定义的变量和函数的生存周期将比内部函数执行时间长。当内部函数以某一种方式被任何一个外部函数作用域访问时,一个闭包就产生了。
var pet = function (name) { // 外部函数定义了一个变量"name"
var getName = function () {
// 内部函数可以访问 外部函数定义的"name"
return name;
}
// 返回这个内部函数,从而将其暴露在外部函数作用域
return getName;
};
myPet = pet("Vivie");
myPet(); // 返回结果 "Vivie"
如果一个闭包的函数定义了一个和外部函数的某个变量名称相同的变量,那么这个闭包将无法引用外部函数的这个变量。
arguments
对象
函数的实际参数会被保存在一个类似数组的arguments对象中。在函数内,你可以按如下方式找出传入的参数:
arguments[i]
Copy to Clipboard
其中i
是参数的序数编号(译注:数组索引),以0开始。所以第一个传来的参数会是arguments[0]
。参数的数量由arguments.length
表示。
使用arguments对象,你可以处理比声明的更多的参数来调用函数。这在你事先不知道会需要将多少参数传递给函数时十分有用。你可以用arguments.length
来获得实际传递给函数的参数的数量,然后用arguments
对象来取得每个参数。
arguments
变量只是 ”类数组对象“,并不是一个数组。称其为类数组对象是说它有一个索引编号和length
属性。尽管如此,它并不拥有全部的Array对象的操作方法。
函数参数
从ECMAScript 6开始,有两个新的类型的参数:默认参数,剩余参数。
默认参数
在JavaScript中,函数参数的默认值是undefined
。然而,在某些情况下设置不同的默认值是有用的。这时默认参数可以提供帮助。
function multiply(a, b = 1) {
return a * b;
}
multiply(5); // 5
剩余参数
剩余参数语法允许将不确定数量的参数表示为数组。
function multiply(multiplier, ...theArgs) {
return theArgs.map(x => multiplier * x);
}
var arr = multiply(2, 1, 2, 3);
console.log(arr); // [2, 4, 6]
箭头函数
箭头函数表达式的语法比函数表达式更简洁,并且没有自己的this
,arguments
,super
或new.target
。
箭头函数表达式更适用于那些本来需要匿名函数的地方,并且它不能用作构造函数。
引入箭头函数有两个方面的作用:更简短的函数并且不绑定this
。
let sum = (a, b) => {
return a + b;
}
箭头函数使用注意点:
- 如果形参只有一个,则
()
可以省略 - 函数体如果只有一条语句,则
{}
可以省略(省略{}
必须去掉return
),函数的返回值为该条语句的执行结果 - 箭头函数
this
指向声明时所在作用域下this
的值 - 箭头函数不能作为构造函数实例化
- 箭头函数不可以使用
arguments
对象,该对象在函数体内不存在。可以用rest
参数代替。 - 不可以使用
yield
命令,因此箭头函数不能用作Generator
函数。
箭头函数不会更改
this
指向,所以非常适合设置与this
无关的回调,比如数组回调、定时器回调,不适合事件回调与对象方法。
构造函数
构造函数就是专门用来创建对象的函数,首字母大写是非常普遍而且很恰当的惯用法。
构造函数和普通函数的主要区别在于调用方式:
- 普通函数直接调用
- 构造函数需要使用
new
关键字来调用
构造函数的执行流程
- 构造函数执行时,会先创建一个新的对象
- 将
this
设置为新的对象 - 执行函数中的代码
- 将新建的对象作为返回值返回
回调函数 callback
被作为实参传入另一个函数,并在该外部函数内被调用,用以来完成某些任务的函数,称为回调函数。
简单来说就是由我们定义,但是不由我们调用的函数称为回调函数
this
this指的是函数运行时所在的“环境”。
在绝大多数情况下,函数的调用方式决定了 this
的值(运行时绑定)。this
不能在执行期间被赋值,并且在每次函数被调用时 this
的值也可能会不同。ES5 引入了 bind
方法来设置函数的 this
值,而不用考虑函数如何被调用的。ES2015 引入了箭头函数,箭头函数不提供自身的 this 绑定(this
的值将保持为闭合词法上下文的值)。
根据函数的调用方式不同,this
的值也不同
- 以函数的形式调用,
this
是window
- 以方法的形式调用,
this
是调用方法的对象 - 以构造函数的形式调用,
this
是新建的对象 - 以
call
和apply
的形式调用,this是它们的第一个参数 - 箭头函数中的
this
由它外层作用域决定 - 事件的回调函数中,
this
是绑定事件的对象
call
apply
bind
call
、apply
、bind
:是所有函数都具有的方法
注意:函数也是对象,函数具有方法
call(参数1, 参数2, 参数3...)
- 参数1:调用函数时,内部this的具有值,让this指向谁
- 剩余参数就是函数的参数
apply(参数1, [参数值1, 参数值2, 参数值3...])
- 参数1:调用函数时,内部this的具有值,让this指向谁
- 参数2:是一个数组,数组的每个值代表了函数的参数
bind(参数1, 参数2, 参数3...)()
- 参数和
call
一样,但是返回的是一个函数
递归
自己调用自己
::: danger 注意 必须有结束条件,没有结束条件就变成死循环 :::
function sun(n) {
if (n === 1) { // 结束条件
return 1
} else {
return n + sum(n - 1)
}
}
数组扁平化:把多维数组变成一维数组
function fn(arr) {
let result = []
for (let i = 0; i < arr.length; i++) {
if (typeof arr[i] === 'object') {
result = result.concat(fn(arr[i]))
} else {
result.push(arr[i])
}
}
return result
}
斐波那契数列:1 1 2 3 5 8 13 21 34 55 ...
function fn(n) {
if (n < 3) {
return 1
}
return fn(n - 1) + fn(n - 2)
}