最近在學習JavaScript基礎,在學習到面向對象編程時,學習到在JavaScript中實現模塊化的方法,其中一個重要的點是如何封裝私有變數。 實現封裝私有變數的方法主要是: 在立即執行函數中,重點在於createStringBuilder這個函數,這個函數實現了內外對接的介面,對內保護了Stri ...
最近在學習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