js高级-闭包

第六章:闭包

什么是闭包

闭包就是能够读取其他函数内部变量的函数,由于在 Javascript 语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成 定义在一个函数内部的函数
所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

闭包的好处:延展了函数的作用域

闭包的弊端:

一般情况下:函数执行完毕,立即释放函数开辟的作用域(用完就释放,节省内存)

但是如果使用闭包:因为内部函数还要访问外部函数作用域中的变量,外部函数执行完毕,不会立即释放开辟的作用域,造成程序性能降低,消耗内存。

闭包的用途:

  • 可以在函数外部读取函数内部成员
  • 让函数内成员始终存活在内存中

闭包:在一个作用域中可以访问另一个作用域的变量

// 未发生闭包
function fn() {  // 当fn函数执行时,会开启一个作用域。执行完毕后,作用域和作用域中的变量都会被销毁
    var n = 10;
    return n;
}

fn();

发生闭包,闭包特点:延展了函数的作用域范围

function fn() {
    var n = 10;
    return function () {
    	return n;
    }
}
var f = fn();  // fn函数执行完毕后,因为f函数还要访问fn作用域中的变量n
console.log(f());

闭包演示

闭包演示:案例 1

闭包的概念:在一个作用域中可以访问另一个作用域的变量或者函数

function getRandom() {
    var random = parseInt(Math.random() * 10) + 1;
    return function () {
    	return random; 
    }
}
var fn = getRandom();
console.log(fn());
console.log(fn());

闭包演示:案例 2

注意:形参n也是局部变量

function getFun(n) {
    return function (m) { // 一个函数只有一个返回值 且 就近返回数值
        return m;
    }
}

// 求 100 + m
var fn100 = getFun(100);
// 求 1000 + m
var fn1000 = getFun(1000);

console.log(fn100(1));
console.log(fn1000(1));

闭包演示:经典面试题

面试题一:使用闭包改造代码
  console.log('start');

    for (var i = 0; i < 3; i++) {
      setTimeout(function () {
          console.log(i);
      }, 0);
    }

    console.log('end');

// 代码执行顺序:start -> end -> 结果为3(出现3次)

// **************  需要:使用闭包改造上面代码,要求依次输出:0 1 2  ************
console.log('start');

    for (var i = 0; i < 3; i++) {
      (function (i) {
        setTimeout(function () {
          console.log(i);
        }, 0);
      })(i);
    }

    console.log('end');

// 代码执行顺序:start -> end -> 依次:0 1 2
面试题二:点击不同的按钮,改变页面字体大小
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>

<body>
  我是字体的演示

  <button id="btn1">按钮1</button>
  <button id="btn2">按钮2</button>
  <button id="btn3">按钮3</button>

  <script>
      
    // ..........传统方法1:................

    var btn1 = document.getElementById('btn1');
    var btn2 = document.getElementById('btn2');
    var btn3 = document.getElementById('btn3');

    btn1.onclick = function() {
        document.body.style.fontSize = '12px';
    }
    btn2.onclick = function() {
        document.body.style.fontSize = '14px';
    }
    btn3.onclick = function() {
        document.body.style.fontSize = '16px';
    };

    // ..........方法2:闭包的应用.............

    // 创建一个函数,设置body的字体大小
    var btn1 = document.getElementById('btn1');
    var btn2 = document.getElementById('btn2');
    var btn3 = document.getElementById('btn3');
      
	// 创建一个函数makeFun,设置body的字体大小
    function makeFun(size) {
      return function () {  // 函数不需要传参数? 如果本身没有传入参数可以去上级去找
        document.body.style.fontSize = size + 'px';
      }
    }
      
	// 真正执行点击事件的函数是:函数makeFun返回的函数
    btn1.onclick = makeFun(12);
    btn2.onclick = makeFun(14);
    btn3.onclick = makeFun(16);
      
  </script>
</body>

</html>
面试题二:点击不同的按钮,改变页面字体大小(闭包写法)
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>

<body>
  我是字体的演示
  <div id="box">
    <button size="12">按钮1</button>
    <button size="14">按钮2</button>
    <button size="16">按钮3</button>
  </div>
  <script>
    // ...................闭包的应用................

    var box = document.getElementById('box');
    var buttons = box.children;

    // 创建一个函数makeFun,设置body的字体大小
    function makeFun(size) {
      return function () { // 不需要传参数? 如果本身没有传入参数可以去上级去找(js基础作用域链)
        document.body.style.fontSize = size + 'px';
        console.log(size);
      }
    }

    for (var i = 0; i < buttons.length; i++) {
      var btn = buttons[i];

      // 获取标签的自定义属性getAttribute
      var size = btn.getAttribute('size');
      btn.onclick = makeFun(size); //点击的时候才会调用return返回的匿名函数

    }
  </script>
</body>

</html>

需求:点击li的时候输出当前li对应的索引(闭包实现)

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>

<body>
  <ul id="heroes">
    <li>安琪拉</li>
    <li>李白</li>
    <li>诸葛亮</li>
    <li>狄仁杰</li>
  </ul>

  <script>
    // .......推荐方式1:给li注册点击事件...............
    //  需求:点击li的时候输出当前li对应的索引

    var heroes = document.getElementById('heroes');
    var list = heroes.children;
    for (var i = 0; i < list.length; i++) {
      var li = list[i];
      li.index = i; // 让对象的自定义属性:每一次循环,记录每次的i
      li.onclick = function () {
        // 2 点击li的时候输出当前li对应的索引
        console.log(this.index);
      }
    }

    // .......方式2:给li注册点击事件....................
    //   需求:点击li的时候输出当前li对应的索引

    var heroes = document.getElementById('heroes');
    var list = heroes.children;
    for (var i = 0; i < list.length; i++) {
      var li = list[i];

      // 每一个i,对应一个作用域
      // 内部的function还要访问外部的变量i;延展了作用域
      (function (i) {
        li.onclick = function () {
          // 2 点击li的时候输出当前li对应的索引
          console.log(i);
        }
      })(i);
    }
  </script>
</body>

</html>

闭包的思考题

思考题 1:没有发生闭包

// 思考1:返回值是函数,不一定会存在闭包
// 闭包:一个作用域可以访问另一个作用域的变量(不能是全局作用域)或者函数
var name = "The Window";
var object = {
  name: "My Object",
  getNameFunc: function () {
    return function () {
      return this.name; // 函数中的this指向调用者fn(),所以this--->window
    };
  }
};

var fn = object.getNameFunc()
fn();

console.log(object.getNameFunc()()); // 结果是:The Window

思考题 2:发生了闭包,闭包发生在getNameFunc函数

var name = "The Window";  
var object = {    
  name: "My Object",
  getNameFunc: function () {
    var that = this; // this指向object,将this赋值给that,此时that也指向object
    return function () {
      return that.name; // that也指向object
    };
  }
};
console.log(object.getNameFunc()()); // My Object
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落花流雨

你的鼓励将是我创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值