Loading

JavaScript中为什么使用立即执行函数来封装模块?

最近在学习JavaScript基础,在学习到面向对象编程时,学习到在JavaScript中实现模块化的方法,其中一个重要的点是如何封装私有变量。

实现封装私有变量的方法主要是:

  1. 使用构造函数
    function StringBuilder() {
      var buffer = [];
    
      this.add = function (str) {
         buffer.push(str);
      };
    
      this.toString = function () {
        return buffer.join('');
      };
    }
    // 上面这种做法有个缺点,就是每生成一个新的实例,其中的方法就会生成一个新的实例,造成内存浪费,因此做如下改进
    function StringBuilder() {
      this._buffer = [];
    }
    
    StringBuilder.prototype = {
      constructor: StringBuilder,
      add: function (str) {
        this._buffer.push(str);
      },
      toString: function () {
        return this._buffer.join('');
      }
    };
    // 此时所有实例会共享这两个方法,不会重复生成
    // 但是问题又来了,我们封装的私有变量变得不私有了,在外部可以直接修改_buffer,如
    var sb = new StringBuilder();
    sb._buffer = 'hello, sb';
  2. 使用立即执行函数
    var module = (function() {
        function StringBuilder() {
            this._buffer = [];
        }
    
        StringBuilder.prototype = {
            constructor: StringBuilder,
            add: function (str) {
                this._buffer.push(str);
            },
            toString: function () {
                return this._buffer.join('');
            }
        };
    
        function createStringBuilder() {
            var _sb = new StringBuilder();
            return {
                add: StringBuilder.prototype.add.bind(_sb),
                toString: StringBuilder.prototype.toString.bind(_sb)
            };
        }
    
        return {
            createStringBuilder: createStringBuilder
        };
    
    })();
    
    console.log(module);    // { createStringBuilder: [Function: createStringBuilder] }
    var sb = module.createStringBuilder();
    sb.add('hello');
    sb.add('爱编程的小菜鸟');
    console.log(sb.toString()); // hello爱编程的小菜鸟

在立即执行函数中,重点在于createStringBuilder这个函数,这个函数实现了内外对接的接口,对内保护了StringBuilder的私有成员,对外又能提供需要访问的接口,实现了真正的私有变量封装。下面我们用一个简单的例子对上面的三种方式进行对比:

// 构造函数,var1无法被外界修改,但是每生成一个实例,fn都会被拷贝
function Class1() {
    var var1 = 10;
    this.fn = function() {
        console.log(var1);
        var1 += 1;
    };
}
var c10 = new Class1();
var c11 = new Class1();
console.log(c10.var1); // undefined,变量不可被访问
console.log(c10 === c11);   // false
c10.fn();   // 10
c11.fn();   // 10
c10.fn();   // 11
c11.fn();   // 11

// 改进一下
function Class2() {
    this._var1 = 10;
}

Class2.prototype = {
    constructor: Class2,
    fn: function() {
        console.log(this._var1);
        this._var1 += 1;
    }
};
var c20 = new Class2();
var c21 = new Class2();
c20.fn();   // 10
c20.fn();   // 11
c21.fn();   // 10
c21.fn();   // 11
console.log(c20._var1); // 11,变量可被访问
console.log(c20.fn === c21.fn); // true

// 为了解决上述问题
var m = (function() {
    function Class3() {
        this.var1 = 10
    }
    Class3.prototype = {
        constructor: Class3,
        fn: function() {
            console.log(this.var1);
            this.var1 += 1;
        }
    };

    function createClass3() {
        var c3 = new Class3();
        return {
            fn: Class3.prototype.fn.bind(c3)
        };
    }

    return {
        createClass3: createClass3
    }
})();
var c30 = new m.createClass3();
var c31 = new m.createClass3();
c30.fn();   // 10
c30.fn();   // 11
c31.fn();   // 10
c31.fn();   // 11

 

posted @ 2018-12-18 19:32  土豆分米猪  阅读(335)  评论(0编辑  收藏  举报