zoukankan      html  css  js  c++  java
  • 老外写的js闭包

    原文:https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Closures

    老外写的很详细,有兴趣的可以看原文。(不得不佩服老外的逻辑思维能力)

    下面是老外说的一个稍微有趣的例子: 

    //在这个例子中,定义了一个函数makeAdder(x),它接收一个参数x和返回一个新函数。
    //返回新函数接受单个参数y,并返回x和y的总和。
    
    //在本质上,是一个函数makeAdder工厂
    
    //add5和add10都闭包。他们共享相同的函数体定义,但存储在不同的环境。在add5环境,x是5。至于add10而言,x是10。
    function makeAdder(x) {
        return function (y) {
            return x + y;
        };
    }
    var add5 = makeAdder(5);
    var add10 = makeAdder(10);
    add5.toString(); //function (y) {  return x + y; };  makeAdder内部方法
    alert(add5(2)); // 7
    alert(add10(2)); // 12

     接着老外又给了一个稍微实用的的例子: 

    function makeSizer(size) {
        return function () {
            document.body.style.fontSize = size + 'px';
        };
    }
    
    var size12 = makeSizer(12);
    size12.toString(); //function () { document.body.style.fontSize = size + 'px'; }
    var size14 = makeSizer(14);
    var size16 = makeSizer(16);
    
    
    document.getElementById('size-12').onclick = size12;
    document.getElementById('size-14').onclick = size14;
    document.getElementById('size-16').onclick = size16;


    <
    a href="#" id="size-12">12</a> <a href="#" id="size-14">14</a> <a href="#" id="size-16">16</a>

    老外开始慢慢引入重点了,还是看代码:

    //闭包的模块模式 
    //创建一个单一的环境,共享三功能:增量计数器。减量计数器。计数器值。
    //这三个公共函数闭包,共享相同的环境。
    //由于JavaScript的词法作用域,他们各自都能访问内部私有changeBy函数 privateCounter变量。
    var Counter = (function () {
        var privateCounter = 0;    //私有变量
        function changeBy(val) {   //私有函数
            privateCounter += val;
        }
        return {
            increment: function () {   //返回方法
                changeBy(1);           //调用内部私有函数
            },
            decrement: function () {    
                changeBy(-1);
            },
            value: function () {       
                return privateCounter;
            }
        }
    })();
    
     
    alert(Counter.value()); /* Alerts 0 */
    Counter.increment();
    Counter.increment();
    alert(Counter.value()); /* Alerts 2 */
    Counter.decrement();
    alert(Counter.value()); /* Alerts 1 */

    怎么样,老外讲的例子还容易懂吧。

    上面单一环境中,三个公共函数还是用的同一个变量。接着看:

    var makeCounter = function () {
        var privateCounter = 0;
        function changeBy(val) {
            privateCounter += val;
        }
        return {
            increment: function () {
                changeBy(1);
            },
            decrement: function () {
                changeBy(-1);
            },
            value: function () {
                return privateCounter;
            }
        }
    };
    
    var Counter1 = makeCounter();
    var Counter2 = makeCounter();
    alert(Counter1.value()); /* Alerts 0 */
    Counter1.increment();
    Counter1.increment();
    alert(Counter1.value()); /* Alerts 2 */
    Counter1.decrement();
    alert(Counter1.value()); /* Alerts 1 */
    alert(Counter2.value()); /* Alerts 0 */

    相信你已经看到   Counter1 Counter2 闭包变量包含一个不同的实例privateCounter。(即:两个实例的变量独立变化,互不影响)

    老外又举了一个例子:创建闭包循环,一个常见的错误,看代码:
    <p id="help">Helpful notes will appear here</p>
    <p>E-mail: <input type="text" id="email" name="email"></p>
    <p>Name: <input type="text" id="name" name="name"></p>
    <p>Age: <input type="text" id="age" name="age"></p>
    function showHelp(help) {
      document.getElementById('help').innerHTML = help;
    }
     
    function setupHelp() {
      var helpText = [
          {'id': 'email', 'help': 'Your e-mail address'},
          {'id': 'name', 'help': 'Your full name'},
          {'id': 'age', 'help': 'Your age (you must be over 16)'}
        ];
     
      for (var i = 0; i < helpText.length; i++) {
        var item = helpText[i];
        document.getElementById(item.id).onfocus = function() {
          showHelp(item.help);
        }
      }
    }
     
    setupHelp(); 
     

    上面是错误的写法(所有的事件都提示年龄),正确的如下:

    function showHelp(help) {
      document.getElementById('help').innerHTML = help;
    }
     
    function makeHelpCallback(help) {
      return function() {
        showHelp(help);
      };
    }
     
    function setupHelp() {
      var helpText = [
          {'id': 'email', 'help': 'Your e-mail address'},
          {'id': 'name', 'help': 'Your full name'},
          {'id': 'age', 'help': 'Your age (you must be over 16)'}
        ];
     
      for (var i = 0; i < helpText.length; i++) {
        var item = helpText[i];
        document.getElementById(item.id).onfocus = makeHelpCallback(item.help);
      }
    }
     
    setupHelp(); 
    这是预期的。而不是回调都共享一个单一的环境,makeHelpCallback函数为每个调用它的对象创建一个新的环境, 。

    写到这老外还没结束。

    老外引出了:构造函数和原型 。
     
    老外说:当闭包不是必要的时候,创建一个新对象/类、方法通常应该是关联到对象的原型,而不是定义成对象的构造函数。
              原因是,每当在调用构造函数的方法将得到重新分配(即为每一个对象创建)。
    老外说的很明白,接下来看js 原型的写法(prototype)。
    当然老外还是有例子的(看代码):
    //构造函数写法
    function MyObject(name, message) {
      this.name = name.toString();      //变量
      this.message = message.toString();
      this.getName = function() {       //方法
        return this.name;
      };
     
      this.getMessage = function() {
        return this.message;
      };
    }

    原型写法:

    //原型的写法1
    function MyObject(name, message) {
      this.name = name.toString();        //变量
      this.message = message.toString();  
    }
    MyObject.prototype = {
      getName: function() {      //方法
        return this.name;
      },
      getMessage: function() {
        return this.message;
      }
    };
     
    //原型的写法2
    function MyObject(name, message) {
      this.name = name.toString();
      this.message = message.toString();
    }
    MyObject.prototype.getName = function() {
      return this.name;
    };
    MyObject.prototype.getMessage = function() {
      return this.message;
    };

    调用方法:

    var a = new MyObject('zhangsan', '我是张三');
    a.getMessage();
    a.getName();

    怎么样看到这里你是不是明白了:js构造函数,js 的原型(prototype),以及js闭包了。

             你是不是应该明白了js创建类,方法,属性了。 是不是有了面向对象的感觉。 

    怎么样,老外还可以吧。。。

    说说我的理由:老外写的文章,例子多,浅显易懂,循序渐进,由浅及深,逻辑缜密。

                       通篇没有什么定义(不像国人写的东西,一开始就解释名词)。

    好吧,最后欢迎您的拍砖!

     
     

              

                  

  • 相关阅读:
    Python Scrapy 爬虫入门
    Python Requests 高级用法
    Python Requests库快速入门
    linux 高并发事件触发处理 — epoll
    初识Python爬妹子图片
    android 使用gradle实现资源自动拷贝
    Android Studio Cmake使用 (附带demo)
    JNI 学习笔记
    Cmake 常见命令说明
    mybatis逆向工程生成代码
  • 原文地址:https://www.cnblogs.com/wanghk/p/3029011.html
Copyright © 2011-2022 走看看