JS(9)闭包、类数组对象、函数

96 阅读4分钟

闭包

闭包 是指一个函数(某函数内部被返回的函数)能够访问其他函数作用域中定义的变量的能力。

优点:

  • 实现数据封装、实现模块化
  • 保持状态
  • 解决全局变量污染

(补:全局变量污染是指在程序中过度使用全局变量,导致全局命名空间中的变量被不同部分的代码频繁修改和污染。会有不可预测性、可维护性差)

缺点:

  • 消耗内存
  • 影响性能

类数组对象

类数组对象,拥有一个 length 属性和若干索引属性的对象。其典型代表就是arguments对象(是函数的一个属性),当然一些 DOM 操作节点的方法也会返回类数组对象。

arguments:

  • length属性是实参的个数
  • 可以解决:函数的递归调用(降低递归调用与函数名称之间的耦合性)
    - 函数参数不定长问题

类数组对象 ==> 数组:

  • Array.prototype.splice.call(arguments)
  • Array.from(arguments)
  • 想调用数组的方法可以借助 .call()

函数

  • 函数本质是对象

函数定义

  • 函数定义的四种方法
- 函数声明
      - 只有函数声明有函数提升
- 函数表达式
- 箭头函数
      - 没有 arguments属性、new.target属性
      - 没有 prototype属性
      - 没有自己的 this 
          -【会继承自己作用域的上一层的this指向(如果该箭头函数 
            定义在全局作用域中,那此时它的this就是windows)】
          - 箭头函数的 this 在函数定义时就已经确定了,所以不会改变
            .call() .bind() 方法都不会改变它的 this 指向!!!
      - 不能进行 new 操作 【没有自己的 this】
- new King()调用
          - ES6 中新增了检测函数是否是通过 new 调用的 new.target 属性!!
          - 如果是普通调用,new.target 的值为 undefined
          - 如果是使用 new 关键字调用的,new.target 的值为被当作构造函数用的函数

函数提升

注意: 只有函数声明有函数提升

console.log(add(2, 3)); // 输出:5

function add(a, b) {
  return a + b;
}

函数重载

  • JS 中的函数没有函数重载 。因为没有函数签名(函数名、参数个数、参数类型)

函数重载指在一个类或对象中定义多个名称相同但参数个数或类型不同的函数。函数重载允许使用相同的函数名来实现不同的操作或功能,根据传递给函数的参数的不同来决定使用哪个函数。

但是可以借助函数的 arguments 属性来实现函数重载!!

function calculate() {
  if (arguments.length === 2 && typeof arguments[0] === 'number' && typeof arguments[1] === 'number') {
    return arguments[0] + arguments[1]; // 处理两个数字相加的情况
  } else if (arguments.length === 3 && typeof arguments[0] === 'number' && typeof arguments[1] === 'number' && typeof arguments[2] === 'string') {
    return arguments[0] + arguments[1] + arguments[2]; // 处理两个数字和一个字符串连接的情况
  } else {
    return 'Invalid arguments'; // 处理其他情况
  }
}

console.log(calculate(1, 2)); // 输出: 3
console.log(calculate(1, 2, ' Hello')); // 输出: "3 Hello"
console.log(calculate(1)); // 输出: "Invalid arguments"

函数属性

  • 每个函数都有三个属性,length 、caller 、prototype
  • length 的值为函数定义时,命名参数的个数【即形参的个数】
  • caller 的值为调用当前函数的函数(即该函数外部包括的函数),如果是在全局作用域下调用的,那么 caller 的值为 null

函数尾调用

尾调用是指在函数的最后一步调用另一个函数的现象,而又因为尾调用的特殊性质,在某些情况下,可以避免在调用链中增加额外的堆栈空间,从而提高程序的性能和效率。

function func(n) {
  if (n <= 1) {
    return 1;
  } else {
    return tailCall(n, 1);
  }
}

function tailCall(n, acc) {
  if (n <= 1) {
    return acc;
  } else {
    return tailCall(n - 1, acc * n);
  }
}

console.log(func(5)); // 输出:120

在这个例子中,函数 func 中调用了另一个函数 tailCall。由于 tailCall 是在 func 的最后一步调用,因此这是一个尾调用。这使得 JavaScript 引擎可以在调用 tailCall 之前将 func 的上下文从内存中移除,从而避免在调用链中增加额外的堆栈空间。

need满足的条件 :

  • 在严格模式下
  • 外部函数的返回值是对未个函数的调用
  • 尾调用函数执行后不需要执行额外的逻辑
  • 尾调用函数不是引用外部函数作用域中自由变量的闭包!!

函数参数传递(按值传递)

  • 原始类型的参数就不说了。就是按值传递,即函数内部对参数的改变不会影响到函数外部变量的值
  • 引用类型需要理解一下,引用类型是把地址的副本传递进去了,即如果在函数内部只是改变参数对象里面的属性,那么外部的对象也会跟着改变。但如果在函数内部把参数对象重新整体赋值,那么外部的对象不会受到影响!!

IIFE(立即调用的函数表达式)

IIFE IIFE 的作用是创建一个独立的函数作用域从而避免变量污染和命名冲突。 在 IIFE 内部声明的变量和函数只在函数内部可见,不会污染全局变量。这样可以有效地保护代码中的变量,增加代码的可维护性和可读性。