函数

一、定义

  1. 声明式 functon fn(){}
  2. 表达式var fn = function(){}(函数表达式一般指 匿名函数)

二、参数(形参,实参)

  1. arguments 类数组对象 表示函数的实参集合

arguments.callee---指向当前执行的函数 arguments.length---指向传递给当前函数的参数数量 arguments.arguments--- 返回一个新的Array迭代器对象,该对象包含参数中每个索引的值

示例:

  function test(a,b,c){
    // 获取实参 和实参相互映射, length与实参个数相对应
    
    // 不在严格模式下:
    // arguments[0]改变,会使相对应的形参也发生改变,修改形参,arguments[0]也会相应被修改
   
    // 而在严格模式则不会被修改
    "use strict"
    arguments[0] = 12
    console.log(a) 

  }
  test(1,2,3);
  1. 函数.length -- 表示函数期望的参数数量
  function nu(a,b,c,d){
      console.log(nu.length) // 4
      // 表示期望传入参数的个数,和实际传入参数的个数无关
  }
  nu(1,2)

三、预编译(执行期间上下文)

  • 预编译前了解
    1. 暗示全局变量 imply global : 即任何变量如果变量未声明赋值,此变量就为全局对象window所有
    2. 全局上的任何变量都归全局对象window所有

    window 就是全局的域 window == GO对象

  • 预编译 步骤
    1. function预编译
      1. 创建 AO对象Active Object
      2. 形参,声明变量为 AO对象 属性,值为 undefinded
      3. 实参的值与形参的值统一
      4. 函数声明,存入 AO 对象中,函数名为属性名,函数体为值
    2. 全局预编译
      1. 生成 GO 对象
      2. 变量声明 为 GO 对象属性, 值为 undefinded
      3. 函数声明 存入 GO 对象中

示例:

// 一、预解析
// GO{
// 先找声明变量,未找到,执行时b赋值123
// b:123
// },
function a(a) {
  console.log(a) // 1
  console.log(b); // not defind
  var a = b = 123;
  console.log(a) // 123
  console.log(b) // 123
}
a(1)
// AO{
// a:undefined->1
// }

四、作用域、作用域链

  1. [[scope]]:每个javascript函数都是一个对象,每个对象都有属性,有的属性能访问,有的属性不可以,这些属性仅供javascript引擎存取,[[scope]]就是其中一个。

    [[scope]] 就是我们所说的作用域,其中存储了执行期上下文的集合

    作用域链[[scope]] 中存储的执行期上下文对象的集合,呈链式链接,我们把这种链式链接叫做作用域链。

    示例:

    function a() {
      function b() {
        var b = 1;
        aa = 123;
        global = '局部'
        function c() { } // 执行b 才会定义 c; 不执行函数,永远不读取函数内部
      }
      var aa = 444;
      b();
      console.log(aa); // 123
      console.log(global); // 局部
    }
    var global = '全局';
    a();
    //1. a 定义 a.[[scope]] ---->0: GO{a:function} // 所谓定义就是执行前定义
    // 2.a 执行 a.[[scope]] --- > 0: AO{b:function} a 的 AO对象 
    //  ----> 1: GO{} 
    // 3. b 定义 b.[[scope]] -------> 0: AO{ b:function }
    // -------> 1:GO{}
    // 4. b 执行 b.[[scope]] ---------> 0: AO{c:function} b 的AO对象
    //                       ----------->1: AO{b:function} a 的AO 对象
    //                      -----------> 2: GO{} a 的 GO 对象
    
    
  2. 立即执行函数 一般设置初始值或者执行一次;只有表达式才能执行

    	(function(){}());
    	(function(){})();
    	+ function(){}();
    	- function(){}();
    	... || function(){}(); ... && function(){}()
    

    示例:实现索引

    // 第一种情况:
    function a() {
      var arr = [];
      for (var i = 0; i < 5; i++) {// 一直在替换AO对象内的i
        arr[i] = function () {
          console.log(i)
        }
      };
      return arr;
    }
    var test = a();
    for (var j = 0; j < 5; j++) {
      test[j]();
    }
    // 输出:5,5,5,5,5
    
    // 第二种情况:
    function a() {
      var arr = [];
      for (var i = 0; i < 5; i++) {
        (function (j) {
          arr[j] = function () { // 定义fun时站立于立即执行函数的AO对象内
            console.log(j); // 读取的是AO对象内的j
          }
        }(i))
      };
      return arr;
    }
    var test = a();
    for (var j = 0; j < 5; j++) {
      test[j]();
    }
    // 输出 0,1,2,3,4
    

五.闭包

  1. 什么是闭包

简单来说,闭包就是一个内部函数能够访问和操作其外部函数变量的函数 当内部函数保存到外部时,将会生成闭包

示例:

function a() {
  var aa = []
  function b() {
    var bb = 'bbbbb参数'
    aa = function () {
      console.log(bb)
    }
  };
  b();
  return aa;
};
var test = a();
test();
// a 定义 a.[[scope]] ----->GO{}
// a 执行 a.[[scope]]   ---->0: a AO{aa: []],b()}
//                     ----->1: GO{ }
// b 定义 b.[[scope]]   ---->0: a AO{aa: undefined,b()}
//                     ----->1: GO{ }
// b 执行 b.[[scope]]   ---->0:bAO{bb:...}
//                      ---->1: a AO{aa: [function()],b()}
//                     ----->2: GO{ }
  1. 闭包的作用 闭包有两个主要的功能:保存保护
  • 保存功能使得闭包能够记住并访问其词法作用域,即使该函数在其词法作用域之外执行。
  • 保护功能则允许闭包对变量进行私有封装,防止变量被外部随意修改,同时也防止了命名空间的污染。
  • 用途:
    1. 实现共有变量
    2. 可以做缓存
    3. 可以实现封装,属性私有化,私有化变量
    4. 模块化开发,防止污染全局变量

示例一:简单的计数器, 实现共有变量

function createCounter() {  
    var count = 0; // 私有变量  
  
    return {  
        increment: function() { // 公开方法,用于增加计数器  
            count++;  
        },  
        getCount: function() { // 公开方法,用于获取计数器的值  
            return count;  
        }  
    };  
}  
  
// 使用createCounter函数创建一个计数器实例  
const counter = createCounter();  
  
// 调用公开方法来增加计数器的值  
counter.increment();  
counter.increment();  
  
// 获取计数器的值  
console.log(counter.getCount()); // 输出:2

示例二:实现缓存

function A() {
  var num = 1;
  function B() {
    num++;
    console.log(num);
  }
  function C() {
    num--;
    console.log(num);
  }
  return [B, C]
}
var arr = A();
arr[0](); // 2
arr[1](); // 1
  1. 闭包的缺点 闭包可能导致内存泄漏的问题,因为闭包可以保留其外部环境的引用,如果这部分环境很大或者生命周期很长,就可能导致不必要的内存占用。

注意:

if 条件句中的 function 会被编译成 函数表达式, 声明会被提升到当前作用域的最顶部, 但是赋值会被留在原地

示例:

function fn() {
  console.log('哈哈哈');
}
(function () {
  if (false) {
    function fn() {
      console.log('啦啦啦');
    }
  }
  fn(); //  fn is not a function
  console.log(fn) // undefined
  // if 中 function 被预编译成表达式,赋值留在原地,所以立即执行函数AO中{fn:undefined}; 执行赋值,不执行不赋值
}());