JS函数

129 阅读4分钟

1.定义函数

1.1 具名函数
function 函数名(形式参数1,形式参数2){
   语句:
   return 返回值;
}
1.2 匿名函数
let a = function(x,y){
   return x+y;
}
或者给上面的函数一个名字:
let a = function fn(x,y){
    return x+y;
}

上面代码会将等号右边的函数的地址,赋值给变量a。注意:这种方式声明的函数,作用域仅限在当前语句中,在外面是访问不到fn()的:

1.3 箭头函数
let f1 = x=>x*x
let f2 = (x,y)=>x+y //圆括号不能省略
let f3 = (x,y)=>{console.log('hi');return x+y} //花括号不能省
let f4 = (x,y)=>({name:x,age:y}) //直接返回对象会出错,JS会将花括号括起来的部分当作代码块,所以需要加圆括号
1.4 用构造函数
let fun = new Function('x','y','return x+y')

2.函数的要素

2.1 调用时机

举个鲜明的例子: 可以看到,明明是先调用函数,但是由于打印日志的时机在a改变值之后,结果也不同。

2.2 作用域

\quad全局变量:在顶级作用域声明的变量是全局变量,window的属性是全局变量,其他的都是局部变量。
\quad 局部变量:局部变量只能在其作用域中生效。注意:函数可以嵌套,作用域也可以嵌套。举例:

function f1(){
   let a=1;
   function f2(){
      let a = 2;
      console.log(a);
   }
   console.log(a);
   a=3;
   f2();
}
f1()

结果为:1 2,a=3没有影响f2函数中a的值。
\quad 作用域规则: 如果多个作用域有相同的变量a,那么查找a的声明时,就向上去最近的作用域,简称【就近原则】。像上面的例子中,a的作用域与函数执行无关,那么这种作用域称为【静态作用域】

2.3 闭包

把上面的例子改一改:

function f1(){
   let a=1;
   function f2(){
      let a = 2;
      function f3(){
        console.log(a);
      }
      a=22;
      f3();
   }
   console.log(a);
   a=3;
   f2();
}

可以看到f3中用到了外部变量,所以可以引出闭包的定义:如果一个函数用到了外部的变量,那么这个函数加这个变量就叫做闭包

2.4 形式参数

形式参数的意思就是非实际参数。

function add(x,y){
  return x+y;//x,y就是形式参数,而不是实际的参数。
  //函数调用时传入的实际数值,才是实际参数
}

形参没有必要完全声明完,例如: 或者多声明形参,例如: 在Java中就不能这样调用。

2.5 返回值

每个函数都有返回值,就算没写return,返回的值也是undefined

2.6 调用栈

\quad 什么是调用栈:JS引擎在调用一个函数前,需要把函数所在的环境push到一个数组中。这个数组就叫做【调用栈】,这个栈的深度不是无限长的,当深度达到上限时,会【爆栈】。举例: 这个栈的深度上限,可用:

function computeMaxCallStackSize(){
 try {
       return 1+computeMaxCallStackSize();
      } catch(e){
      return 1;
     }
}

测试不同浏览器的栈深度上限。 Chorme为12578,Firefox为26773

2.7 函数提升

【函数提升】:不管把具名函数声明在哪里,他都会跑到第一行。举例: 使用赋值形式声明的函数,则不会提升:

2.8 arguments(除了箭头函数)

在函数中,可以用arguments获取函数的所有参数。举例: 可以看到,arguments是一个数组,但是是一个伪数组(没有push,pop)方法。

2.9 this(除了箭头函数)

在JS的函数中,this默认指向window: 我们可以通过call修改this的指向: 但是,JS会默认帮我们把1封装为对象,这时候就需要将函数声明代码修改为:

function fn(){
   'use strict'
   console.log(this)
}

这时候,我们就可以得到原始的对象: call方法中,还可指定方法的参数: 上面的call方法,还可以用fn.apply(1,[2,3,4])替代。
举一个更加清楚的例子,介绍使用call调用函数:

3.立即执行函数

当我们需要一个方法,需要让他立刻执行,但是又不想定义一个全局的函数对象。则可以通过操作符+匿名函数的形式声明并调用函数。举例: NaN是函数的返回值,不用理会。当然,还可以使用! ~ () + -都可以,推荐用!