JavaScript里面的函数

我心飞翔 分类:javascript

什么是函数

函数是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
 

回复

我来回复
  • 暂无回复内容