JS 函数 学习笔记

函数是一段可以反复调用的代码块。函数还能接受输入的参数,不同的参数会返回不同的值

声明函数的 5 种方式

  • 具名函数 (function 命令)

     function f(x, y){
         return x + y;
     }
     f.name // 'f'
    
  • 函数表达式

    var f
    f = function(x, y){
    	return x + y;
    }
    f.name // 'f'
    
  • 具名函数赋值

    var f
    f = function f2(x, y){
        return x + y;
    }
    f.name // 'f2'
    console.log(f2) // undefined
    
  • window.Function

    var f = new Function('x', 'y', 'return x + y;')
    f.name // 'anonymous'
    
  • 箭头函数

    var f = (x, y) => {
        return x + y;
    }
    var sum = (x, y) => x + y
    var n2 = n => n*n
    

函数的特性

  • 如果同一个函数被多次声明,后面的声明就会覆盖前面的声明

  • 函数内部的return语句表示返回,JavaScript 引擎遇到return语句,就直接返回return后面的哪个表达式的值,即时后面还有语句也不会得到执行。return语句不是必须的,如果没有,函数默认返回undefined

  • 在 JavaScript 中函数可以看成一个种值,与其他值(数字,字符串,布尔值等)地位相同,称为第一等公民,函数可以当作惭怍传递给另外一个函数。

  • 函数名的提升

    JavaScript 引擎将函数名视同变量名,所以采用function命令声明函数时,整个函数会像变量声明一样,被提升到代码头部,所以,下面的代码不会出错。

    f();
    
    function f() {}
    // 表面上,代码好像在声明之前就调用了函数f。
    // 但是实际上,由于“变量提升”,函数 f 被提升到了代码头部,也就是在调用之前已经声明了
    

    如果使用函数表达式的方式,声明前就调用,下面的代码就会出错

    f();
    var f = function (){}
    // TypeError: f is not a function
    
    // 等价于
    var f;
    f();
    f = function(){}
    

函数作用域

作用域(scope)指的是变量存在的范围,在 ES5 的规范中只有全局作用域和函数作用域,ES6 新增了块级作用域。

全局作用域:变量在整个程序中一直存在,所有地方都可以读取

函数作用域:变量只在函数内部存在,外部无法访问函数内部的变量,除了使用闭包。

  • 函数内部可以读取全局变量

    var v = 1;
    
    function f() {
      console.log(v);
    }
    
    f()
    // 1
    
  • 在函数内部定义的变量,外部无法读取,称为“局部变量”

    function f(){
      var v = 1;
    }
    
    v // ReferenceError: v is not defined
    
  • 函数内部定义的变量,会在该作用域内覆盖同名全局变量,就近原则

    var v = 1;
    
    function f(){
      var v = 2;
      console.log(v);
    }
    
    f() // 2
    v // 1
    
  • 与全局作用域一样,函数作用域内部也会产生“变量提升”现象。var命令声明的变量,不管在什么位置,变量声明都会被提升到函数体的头部

    function foo(x) {
      if (x > 100) {
        var tmp = x - 100;
      }
    }
    
    // 等同于
    function foo(x) {
      var tmp;
      if (x > 100) {
        tmp = x - 100;
      };
    }
    
  • 函数本身也是一个值,也有自己的作用域。它的作用域与变量一样,就是其声明时所在的作用域,与其运行时所在的作用域无关。参考本文中的 题目二

闭包

function f1() {
  var n = 999;
  function f2() {
    console.log(n);
  }
  return f2;
}

var result = f1();
result(); // 999

函数 f2 和变量 n 的组合就形成了一个闭包。

「函数」和「函数内部能访问到的变量」(也叫环境)的总和,就是一个闭包

闭包的作用:

闭包常常用来「间接访问一个变量」。换句话说,「隐藏一个变量」,让函数执行形成的私有作用域,保护里面的变量不受外界干扰的机制。

var api = (function() {
  var lives = 50
  var 奖励一条命 = function() {
    lives += 1
    console.log('当前剩余' + lives)
  }
  var 死一条命 = function() {
    lives -= 1
    console.log('当前剩余' + lives)
  }
  return {
    a: 奖励一条命,
    b: 死一条命
  }
 
}())
api.a() // 当前剩余51
api.b() // 当前剩余50
api.b() // 当前剩余49

参考阅读 JS 中的闭包是什么

立即执行函数

(function(){ 
/* code */ 
}());

// 或者

(function(){ 
/* code */ 
})();

通常情况下,只对匿名函数使用这种“立即执行的函数表达式”。

它的目的有两个:

一是不必为函数命名,避免了污染全局变量;

二是 IIFE 内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。

练习题

题目一

var a = 1
function f1(){
    alert(a) // 是多少
    var a = 2
}
f1.call()

// 遇到代码题,请先提升声明!
// 遇到代码题,请先提升声明!
// 遇到代码题,请先提升声明!

var a = 1
function f1(){
    var a
    alert(a)
    a = 2
}
f1.call() 
// 答案: alert 一个 undefined

题目二

var a = 1
function f1(){
    var a = 2
    f2.call()
}
function f2(){
    console.log(a) // 是多少
}
f1.call()
// 函数本身也是一个值,也有自己的作用域。
// 它的作用域与变量一样,就是其声明时所在的作用域,与其运行时所在的作用域无关。

// 答案:输出 1

题目三

var liTags = document.querySelectorAll('li')
for(var i = 0; i<liTags.length; i++){
    liTags[i].onclick = function(){
        console.log(i) // 点击第3个 li 时,打印 2 还是打印 6?
    }
}

// 答案 6

// 如何解决?
// 1. 使用 let 声明变量 i
// 2. 使用立即执行函数

var liTags = document.querySelectorAll('li')
for (var i = 0; i < liTags.length; i++) {
  (function(item) {
    liTags[item].onclick = function() {
      console.log(item)
    }
  })(i);

}
posted @ 2019-05-24 15:44  AALMIX  阅读(181)  评论(0编辑  收藏  举报