delegates模組是由TJ大神寫的,該模組的作用是將內部物件上的變數或函式委託到外部物件上。
然後我們就可以使用外部物件就能獲取內部物件上的變數或函式。delegates委託方式有如下:
getter: 外部物件可以通過該方法訪問內部物件的值。
setter:外部物件可以通過該方法設定內部物件的值。
access: 該方法包含getter和setter功能。
method: 該方法可以使外部物件直接呼叫內部物件的函式。
專案檔案如下結構:
|----- 專案 | |-- delegates.js # 委託代理的js | |-- index.js # 入口檔案的js
1. getter (外部物件可以通過該方法訪問內部物件的值。)
使用方法demo如下(index.js):
const delegates = require('./delegates'); const obj = { xx: { name: 'kongzhi', age: 30, test: function() { console.log('xxxxxxx'); } } }; // 通過delegates將內部物件 xx 委託到外部物件obj上 var d = new delegates(obj, 'xx'); d.getter('name').getter('age').getter('test'); console.log(obj.name); // kongzhi console.log(obj.age); // 30 obj.test(); // xxxxxxx
2. setter (外部物件可以通過該方法設定內部物件的值。)
使用方法的demo如下(程式碼在index.js內):
const delegates = require('./delegates'); const obj = { xx: { name: 'kongzhi', age: 30, test: function() { console.log('xxxxxxx'); } } }; // 通過delegates將內部物件 xx 委託到外部物件obj上 var d = new delegates(obj, 'xx'); d.setter('name').setter('age').setter('test'); // 使用setter後,就可以在obj物件上直接修改變數或函式的值了 obj.name = '123456'; obj.age = '31'; obj.test = function() { console.log('yyyy'); } /* 在外部物件obj修改完成後,我們再使用 外部物件[內部物件][變數] 這種方式獲取值, 就可以看到值更新了 */ console.log(obj.xx.name); // 123456 console.log(obj.xx.age); // 31 obj.xx.test(); // yyyy
3. access (該方法包含getter和setter功能。)
使用方法的demo如下
const delegates = require('./delegates'); const obj = { xx: { name: 'kongzhi', age: 30, test: function() { console.log('xxxxxxx'); } } }; // 通過delegates將內部物件 xx 委託到外部物件obj上 var d = new delegates(obj, 'xx'); d.access('name').access('age').access('test'); // access 該方法既有setter功能,又有getter功能 // 1. 直接使用外部物件 obj, 來訪問內部物件中的屬性 console.log(obj.name); // kongzhi console.log(obj.age); // 30 obj.test(); // xxxxxxx // 2. 使用常規的方法獲取物件的內部的屬性 console.log(obj.xx.name); // kongzhi console.log(obj.xx.age); // 30 obj.xx.test(); // xxxxxxx // 3. 修改內部物件的屬性 obj.name = '2222'; console.log(obj.name); // 2222 console.log(obj.xx.name); // 2222
4. method (該方法可以使外部物件直接呼叫內部物件的函式。)
使用方法的demo如下:
const delegates = require('./delegates'); const obj = { xx: { name: 'kongzhi', age: 30, test: function() { console.log('xxxxxxx'); } } }; // 通過delegates將內部物件 xx 委託到外部物件obj上 var d = new delegates(obj, 'xx'); d.method('test'); obj.test(); // xxxxxxx
5. fluent
該方法的作用是,如果該方法傳了引數的話,那麼它的含義是修改該變數的值,如果沒有傳入引數的話,那麼
它的作用是獲取該引數的值。
注意:只針對變數有用,如果是函式的話,不建議使用;
如下程式碼demo所示:
const delegates = require('./delegates'); const obj = { xx: { name: 'kongzhi', age: 30, test: function() { console.log('xxxxxxx'); } } }; // 通過delegates將內部物件 xx 委託到外部物件obj上 var d = new delegates(obj, 'xx'); d.fluent('name').fluent('age'); // 無引數 獲取該物件的值 console.log(obj.name()); // kongzhi console.log(obj.age()); // 30 // 有引數,就是修改對應的值 obj.name('11111') obj.age(31) console.log(obj.xx.name); // 11111 console.log(obj.xx.age); // 31
二:delegates模組原始碼如下:
/** * Expose `Delegator`. */ module.exports = Delegator; /** * Initialize a delegator. * * @param {Object} proto * @param {String} target * @api public */ /* Delegator 函式接收二個引數,proto指是一個是外部物件,target指外部物件中的一個屬性,也就是內部物件。 首先判斷this是否是Delegator的實列,如果不是實列的話,就直接使用 new 實列化一下。 因此 const xx = Delegator(obj, 'xx') 或 const xx = new Delegator(obj, 'xx') 都是可以的。 this.proto = proto; 外部物件儲存該實列this.proto 中。 this.target = target; 和proto一樣。 this.methods = []; this.getters = []; this.setters = []; this.fluents = []; 如上四個陣列作用是 記錄委託了哪些屬性和函式。 */ function Delegator(proto, target) { if (!(this instanceof Delegator)) return new Delegator(proto, target); this.proto = proto; this.target = target; this.methods = []; this.getters = []; this.setters = []; this.fluents = []; } /** * Delegate method `name`. * * @param {String} name * @return {Delegator} self * @api public */ /* method的作用是:該方法可以使外部物件直接呼叫內部物件的函式。如下demo: const obj = { xx: { name: 'kongzhi', age: 30, test: function() { console.log('xxxxxxx'); } } }; // 通過delegates將內部物件 xx 委託到外部物件obj上 var d = new delegates(obj, 'xx'); d.method('test'); obj.test(); // xxxxxxx 1. 首先我們呼叫 d.method('test'); 就把該test方法存入 this.methods陣列中。 2. 該方法返回了一個函式 obj['test'] = function() { return obj['xx']['test'].apply(obj['xx'], arguments); } 3. 最後返回 this, 返回該例項化物件,目的是可以鏈式呼叫。 4. 因此就返回了第二步函式。因此當我們使用 obj.test() 的時候,就會自動呼叫該函式。然後 使用 apply方法自動執行 obj['xx']['test'].apply(obj['xx'], arguments); */ Delegator.prototype.method = function(name){ var proto = this.proto; var target = this.target; this.methods.push(name); proto[name] = function(){ return this[target][name].apply(this[target], arguments); }; return this; }; /** * Delegator accessor `name`. * * @param {String} name * @return {Delegator} self * @api public */ /* 該方法的作用是包含 getter的作用,同時也包含setter的作用,如demo如下: const obj = { xx: { name: 'kongzhi', age: 30, test: function() { console.log('xxxxxxx'); } } }; // 通過delegates將內部物件 xx 委託到外部物件obj上 var d = new delegates(obj, 'xx'); d.access('name').access('age').access('test'); // access 該方法既有setter功能,又有getter功能 // 1. 直接使用外部物件 obj, 來訪問內部物件中的屬性 console.log(obj.name); // kongzhi console.log(obj.age); // 30 obj.test(); // xxxxxxx // 2. 使用常規的方法獲取物件的內部的屬性 console.log(obj.xx.name); // kongzhi console.log(obj.xx.age); // 30 obj.xx.test(); // xxxxxxx // 3. 修改內部物件的屬性 obj.name = '2222'; console.log(obj.name); // 2222 console.log(obj.xx.name); // 2222 */ Delegator.prototype.access = function(name){ return this.getter(name).setter(name); }; /** * Delegator getter `name`. * * @param {String} name * @return {Delegator} self * @api public */ /* getter,該方法的作用是:外部物件可以通過該方法訪問內部物件的值。比如如下demo const obj = { xx: { name: 'kongzhi', age: 30, test: function() { console.log('xxxxxxx'); } } }; // 通過delegates將內部物件 xx 委託到外部物件obj上 var d = new delegates(obj, 'xx'); d.getter('name').getter('age').getter('test'); console.log(obj.name); // kongzhi console.log(obj.age); // 30 obj.test(); // xxxxxxx 1. 該方法接收一個引數 name, 該引數是一個字串型別。 2. 把該引數name值儲存到 this.getters陣列中。然後我們使用 __defineGetter__ 監聽物件屬性值的變化。 想要理解 __defineGetter__ 作用,請看我這篇文章 (https://www.cnblogs.com/tugenhua0707/p/10324983.html#_labe1) 如果獲取該物件值的話,就會自動呼叫 __defineGetter__ ,就能監聽到,因此就返回 this[target][name]; 即使: obj['xx']['name']; */ Delegator.prototype.getter = function(name){ var proto = this.proto; var target = this.target; this.getters.push(name); proto.__defineGetter__(name, function(){ return this[target][name]; }); return this; }; /** * Delegator setter `name`. * * @param {String} name * @return {Delegator} self * @api public */ /* 該方法的作用是:外部物件可以通過該方法設定內部物件的值。使用demo如下: const obj = { xx: { name: 'kongzhi', age: 30, test: function() { console.log('xxxxxxx'); } } }; // 通過delegates將內部物件 xx 委託到外部物件obj上 var d = new delegates(obj, 'xx'); d.setter('name').setter('age').setter('test'); // 使用setter後,就可以在obj物件上直接修改變數或函式的值了 obj.name = '123456'; obj.age = '31'; obj.test = function() { console.log('yyyy'); } // 在外部物件obj修改完成後,我們再使用 外部物件[內部物件][變數] 這種方式獲取值, 就可以看到值更新了 console.log(obj.xx.name); // 123456 console.log(obj.xx.age); // 31 obj.xx.test(); // yyyy 1. 同樣的道理,使用 __defineSetter__方法來監聽物件值發生改變,如果物件值發生改變的話,就返回 this[target][name] = val; 把值賦值進去。最後返回this物件。 */ Delegator.prototype.setter = function(name){ var proto = this.proto; var target = this.target; this.setters.push(name); proto.__defineSetter__(name, function(val){ return this[target][name] = val; }); return this; }; /** * Delegator fluent accessor * * @param {String} name * @return {Delegator} self * @api public */ /* 該方法的作用是,如果該方法傳了引數的話,那麼它的含義是修改該變數的值,如果沒有傳入引數的話,那麼 它的作用是獲取該引數的值。 使用demo如下: const obj = { xx: { name: 'kongzhi', age: 30, test: function() { console.log('xxxxxxx'); } } }; // 通過delegates將內部物件 xx 委託到外部物件obj上 var d = new delegates(obj, 'xx'); d.fluent('name').fluent('age'); // 無引數 獲取該物件的值 console.log(obj.name()); // kongzhi console.log(obj.age()); // 30 // 有引數,就是修改對應的值 obj.name('11111') obj.age(31) console.log(obj.xx.name); // 11111 console.log(obj.xx.age); // 31 1. 當我像如上demo一樣,使用 d.fluent('name').fluent('age');後,會依次儲存到 this.flunts陣列中。 2. 然後返回一個函式,如下程式碼: obj['name'] = function(val) { if ('undefined' != typeof val) { this[target][name] = val; return this; } else { return this[target][name]; } } 如果值沒有傳遞電話,就直接返回 this[target][name]; 即:obj['xx']['name']; 如果傳遞了值的話,就把值賦值到物件裡面去,如程式碼:this[target][name] = val; 即:obj['xx']['name'] = val; */ Delegator.prototype.fluent = function (name) { var proto = this.proto; var target = this.target; this.fluents.push(name); proto[name] = function(val){ if ('undefined' != typeof val) { this[target][name] = val; return this; } else { return this[target][name]; } }; return this; };