最近在学习JavaScript基础,在学习到面向对象编程时,学习到在JavaScript中实现模块化的方法,其中一个重要的点是如何封装私有变量。
实现封装私有变量的方法主要是:
- 使用构造函数
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';
- 使用立即执行函数
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