闭包、IIFE立即执行函数

目录

词法(lexical)作用域

A.函数作为参数:IIFE匿名闭包

B.函数返回函数,且子函数 调用 父级作用域的变量

优点

私有变量/方法:封装在函数内部,避免全局污染,保护变量不被外部访问和修改

缺点

内存占用:无法被垃圾回收。滥用,会内存泄漏

性能损耗:作用域链的查找,性能损耗

使用场景

函数柯里化(返回函数):参数复用,延迟执行

防抖节流

私有变量

发布-订阅

链式调用

内存泄漏:内存浪费->慢->崩溃

不再使用/为空的引用未被移除:闭包/DOM移除,子节点引用没移除

IIFE:()()(立即调用函数表达式)/自执行匿名函数

闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment词法环境)的引用的组合

词法(lexical)作用域

根据源代码中声明变量的位置来确定该变量在何处可用

function init() {
  var name = "Mozilla"; // name 是一个被 init 创建的局部变量
  function displayName() {
    // displayName() 是内部函数,一个闭包
    alert(name); // 使用了父函数中声明的变量
  }
  displayName();
}
init();

嵌套函数可访问声明于它们外部作用域的变量。

闭包是由函数以及声明该函数的词法环境组合而成的。该环境包含了这个闭包创建时作用域内的任何局部变量。在本例子中,myFunc 是执行 makeFunc 时创建的 displayName 函数实例的引用。displayName 的实例维持了一个对它的词法环境(变量 name 存在于其中)的引用。

function makeFunc() {
  var name = "Mozilla";
  function displayName() {
    alert(name);
  }
  return displayName;
}

var myFunc = makeFunc();
myFunc();

在一些编程语言中,一个函数中的局部变量仅存在于此函数的执行期间。一旦 makeFunc() 执行完毕,你可能会认为 name 变量将不能再被访问。然而,因为代码仍按预期运行,所以在 JavaScript 中情况显然与此不同。

A.函数作为参数:IIFE匿名闭包

for (var i = 0; i < 5; i++) {
  (function(j) {
    setTimeout(function timer() {
      console.log(j);
    }, 1000);
  })(i);
}
//或者
for(var i = 0;i < 5;i++)
{
    setTimeout((function(i){
        return () => console.log(i);
    })(i),1000)
}

B.函数返回函数,且子函数 调用 父级作用域的变量

因为js作用域生命周期在于内部脚本是否全部执行完毕才会销毁,并且不会带到父级作用域

因为被下级作用域内  引用,而没有被释放。就导致上级作用域内的变量,等到下级作用域执行完后 或者 当闭包(子函数)不再被引用时才会被释放

  function createCounter() {
   let counter = 0
   const myFunction = function() {
     counter = counter + 1
     return counter
   }
   return myFunction
 }
 const increment = createCounter()
 const c1 = increment()
 const c2 = increment()
 const c3 = increment()
 console.log('example increment', c1, c2, c3)//1 2 3

优点

私有变量/方法:封装在函数内部,避免全局污染,保护变量不被外部访问和修改

缺点

内存占用:无法被垃圾回收。滥用,会内存泄漏

性能损耗:作用域链的查找,性能损耗

使用场景

函数柯里化(返回函数):参数复用,延迟执行

防抖节流

私有变量

JavaScript 没有私有变量的原生支持

var Counter = (function () {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function () {
      changeBy(1);
    },
    decrement: function () {
      changeBy(-1);
    },
    value: function () {
      return privateCounter;
    },
  };
})();

console.log(Counter.value()); /* logs 0 */
Counter.increment();
Counter.increment();
console.log(Counter.value()); /* logs 2 */
Counter.decrement();
console.log(Counter.value()); /* logs 1 */
发布-订阅
function createPubSub() {
  // 存储事件及其对应的订阅者
  const subscribers = {};

  // 订阅事件
  function subscribe(event, callback) {
    // 如果事件不存在,则创建一个新的空数组
    if (!subscribers[event]) {
      subscribers[event] = [];
    }
    // 将回调函数添加到订阅者数组中
    subscribers[event].push(callback);
  }

  // 发布事件
  function publish(event, data) {
    // 如果事件不存在,则直接返回
    if (!subscribers[event]) {
      return;
    }
    // 遍历订阅者数组,调用每个订阅者的回调函数
    subscribers[event].forEach((callback) => {
      callback(data);
    });
  }

  // 返回订阅和发布函数
  return {
    subscribe,
    publish,
  };
}

// 使用示例
const pubSub = createPubSub();

// 订阅事件
pubSub.subscribe("event1", (data) => {
  console.log("订阅者1收到事件1的数据:", data);
});

pubSub.subscribe("event2", (data) => {
  console.log("订阅者2收到事件2的数据:", data);
});

// 发布事件
pubSub.publish("event1", "Hello");
// 输出: 订阅者1收到事件1的数据: Hello

pubSub.publish("event2", "World");
// 输出: 订阅者2收到事件2的数据: World

链式调用

function calculator() {
  let result = 0;

  function add(num) {
    result += num;
    return this;
  }

  function subtract(num) {
    result -= num;
    return this;
  }

  function multiply(num) {
    result *= num;
    return this;
  }

  function divide(num) {
    result /= num;
    return this;
  }

  function getResult() {
    return result;
  }

  function clear() {
    result = 0;
    return this;
  }

  return {
    add,
    subtract,
    multiply,
    divide,
    getResult,
    clear,
  };
}
const calc = calculator();
const result = calc.add(5).subtract(2).divide(3).multiply(6).getResult();
console.log(result); // 输出:6

内存泄漏:内存浪费->慢->崩溃

不再使用/为空的引用未被移除:闭包/DOM移除,子节点引用没移除

IIFE:()()(立即调用函数表达式)/自执行匿名函数

  1. 第一部分是一个具有词法作用域的匿名函数,并且用圆括号运算符 () 运算符闭合起来。这样不但阻止了外界访问 IIFE 中的变量,而且不会污染全局作用域。
  2. 第二部分创建了一个立即执行函数表达式 (),通过它,JavaScript 引擎将立即执行该函数。
(function () {
  // …
})();

(() => {
  // …
})();

(async () => {
  // …
})();

猜你喜欢

转载自blog.csdn.net/qq_28838891/article/details/133136715