什么是函数
函数是JavaScript里面很重要是一种数据类型,学会了函数可以帮我们处理很多复杂的逻辑。
声明函数的方法
1.用function声明函数
function a(n){
console.log(n) //这是函数声明
}
a(1) //执行函数输出结果是1
上面的例子声明了函数a,参数是n,最后一行的代码是执行函数 此时控制台上就会输出1
2.函数表达式
var a = function(n){
console.log(n)
}
a(1) //1
3.构造函数Function
var add = new Function(
'x',
'y',
'return x + y'
); //除了最后一个参数是函数体 其它都是函数的参数
// 等同于
function add(x, y) {
return x + y;
}
一般声明函数都会使用前两种方法 第三种方法比较复杂且可读性不高
如果同一个函数被多次声明,后面的声明就会覆盖前面的声明
函数的调用
在函数名的后面用圆括号运算符就会调用该函数 圆括号里的内容是实际参数
return -- 当js引擎遇到return语句会直接返回return语句后面的值 并且不会执行后面的代码
return语句不是必须的 如果函数里面没有return那么该函数将不会有返回值 或者说返回undefined
function add(x, y) {
return x + y; //如果后面还有代码将不会继续执行
}
add(1, 1)
函数自身可以调用自身 叫做递归
用递归实现斐波那契数列 --- 函数x内部调用自己 斐波那契数列的规律是第n位等于前两位的和
可以这样理解函数内部的语句 首先判断第0位和第1位的值 因为是固定的所以直接返回0和1
如果求的不是第0位和第1位那么执行第三条语句 调用自身 自己的前第二位和自己的前第一位相加
第三条语句会继续执行该函数然后继续递归 直到找到出口为止 前面两个条件判断就是出口
function x(n){
if(n === 0) return 0;
if(n === 1) return 1;
return x(n - 2) + x(n - 1);
}
x(4) // 3
函数名的提升
如果是命名函数 那么在预编译环节该函数会被提升到代码头部 就跟var定义变量一样 可以先执行再命名
f();
function f() {} //不会报错 因为函数被提升到了头部
但是函数表达式不能先执行
f();
var f = function (){};
// TypeError: undefined is not a function
//上面的代码等同于
var f //声明函数
f() //执行函数 但是此时函数还没有被赋值 所以报错
f = function(){} //赋值
函数的作用域
JavaScript中有三大作用域 全局作用域 函数作用域 块级作用域(ES5新增)
全局作用域是在所有地方都能访问到 变量在程序中一直都存在
函数作用域是变量只能在函数内部访问 外部无法访问函数内部变量
var x = 1
function a () {
console.log(x)
}
a() // 1 函数内部可以访问到外部变量
------------------------------------
function f(){
var v = 1;
}
v // ReferenceError: v is not defined 外部无法访问函数内部变量
------------------------------------
var a = 1
function x(){
var a = 2
}
a // 1
x() // 2 函数内部会覆盖同名全局变量
只有在函数内部声明的变量才是局部变量 仅在该函数下可以访问 在别的地方声明的都是全局变量 大括号{}里声明也是全局变量
在函数体内也会存在变量提升 跟在全局里一样
function a (){
x = 1
var x
}
a() // 1 函数体内适用var定义变量同样会被提升到头部
函数本身作用域
函数本身也是一个值 也有自己的作用域 它的作用域与变量一样,就是其声明时所在的作用域,与其运行时所在的作用域无关。
var a = 1;
此时函数x的作用域是在全局 所以函数内部的a也是会拿到全局的a
var x = function () {
console.log(a);
};
虽然函数x是在函数f里才执行 但是函数x绑定的是全局的作用域 所以运行结果是1
function f() {
var a = 2;
x();
}
f() // 1
函数的参数
函数的参数分别是形式参数和实际参数 正因为有了参数函数才会更灵活有意义 假如一个函数没有参数 那不就成了一个死数据了吗
形式参数是表示外部需要提供的数据 如果没有提供数据函数就不会有结果
function square(x) {
return x * x;
}
square(2) // 4
square(3) // 9
x就是形式参数 square()里的是实际参数 会把该参数替换成形参然后执行参数
闭包
想要理解闭包 必须先理解变量的作用域 首先函数内部的变量是可以访问到外部的全局变量的 但是反之不行
正常情况下 我们无法在全局获得函数内部的变量 但是函数内部的函数可以获取该变量 假设我们在函数里面在添加一个函数再把它return出来 这样不就可以在外部访问到该变量了吗 看例子
function f1() {
var n = 999; //f1 函数内部有一个变量n 外部无法访问
function f2() { //在f1里面再定义一个函数f2 f2是可以访问到f1里的n的
console.log(n);
}
return f2; //然后我们把f2返回出去
}
var result = f1(); //定义一个变量接收一下
result(); // 999 //这个时候我们在外部就能访问到函数内部的变量了
要理解一个函数出生时的环境很重要 就以上面的例子来说 f2的出生环境是在f1函数里面 所以他能访问f1的内部变量n 假设全局也有个变量n并且值和f1里的n不同 那么f2还是会取到f1里面n的值 因为它的出生环境是f1 如果f1没有变量n才会去全局里取n的值
闭包其实就是一座桥梁 让外部能访问到函数内部的变量 闭包的最大用处有两个 一个是访问函数内部变量 还有一个就是让这些变量始终保存在内存里面 因为一旦产生闭包 外层函数就无法释放内存 因为内层函数始终记得自己的出生环境 就会记住出生时的那些变量 闭包的缺点是影响性能 闭包会产生内存泄漏(内存无法得到释放) 闭包也能封装私有化变量 请看例子
function Person(name) {
var _age;
function setAge(n) {
_age = n;
}
function getAge() {
return _age;
}
return {
name: name,
getAge: getAge,
setAge: setAge
};
}
var p1 = Person('张三'); //将函数赋值给p1
p1.setAge(25); //外部无法访问该变量 只有p1可以访问
p1.getAge() // 25