2017-05-11 26 views
0

我試圖抽象出一個讓我更容易編寫代碼的JQuery常見行爲:將對象封裝到類似set的方法被調用時,它會被執行並返回對象本身(非常類似於JQuery)。封裝一個對象,當一個`set`類的方法被調用時,它被執行並且對象本身被返回(很像JQuery)

看:

let encapsulate = function (guy) { 
    return function() { 
     guy.call(this, [].slice.apply(arguments)) 
     Object.setPrototypeOf(this, guy.prototype) 
     Object.getOwnPropertyNames(guy.prototype) 
      .filter(propName => propName.match(/set/g)) 
      .forEach(propName => { 
       this[propName] =() => { 
        guy.prototype[propName].apply(this, [].slice.apply(arguments)) 
        return this 
       } 
      }) 
    } 
} 

測試的情況是:

// works 
let date = new Date(); 
console.log(date.setDate(10)); // logs 1494447383428 
console.log(date.getDate()); // logs 10 
console.log(Date.prototype.setDate.apply(date, [20])); // logs 1494447383428 
console.log(date.getDate()); // logs 20 

// does not work 
let ED = encapsulate(Date); 
let date1 = new ED(); 
console.log(date1.setDate(10)); // should log date1 object but throws an error 

會拋出一個錯誤Method Date.prototype.setDate called on incompatible receiver [object Object]

你能幫我嗎? d:

+1

它意味着它說的是什麼。 「日期」並不是真正的可分類的,當然也不像你做的那樣。嘗試使用組合繼承(就像jQuery一樣)! – Bergi

+0

當然......但是這個錯誤,爲什麼它出現在我調用'guy.prototype [propName] .apply(this,[] .slice.apply(arguments))'而不是當我調用Date.prototype.setDate時。應用(日期,[20])',例如?我很困惑。 – susanoobit

+0

因爲'date'是具有適當內部插槽的實際日期實例,而'this'不是 - 從'Date.prototype'繼承的對象是不夠的。 – Bergi

回答

0

(更新)從BERGI新建議:

let encapsulate = function (guy) { 
    return function() { 
     this.inner = new (Function.prototype.bind.apply(guy, arguments)) 

     let prototype = 
      Object.getOwnPropertyNames(guy.prototype) 
       .reduce((proto, propName) => { 
        let method = propName.match(/^set/g) ? 
         (function() { 
          guy.prototype[propName].apply(this.inner, [].slice.apply(arguments)) 
          return this 
         }).bind(this) : 
         (function() { 
          return guy.prototype[propName].apply(this.inner, [].slice.apply(arguments)) 
         }).bind(this) 

        return Object.defineProperty(proto, propName, { 
         value: method, 
         enumerable: false, 
         writable: false, 
         configurable: false 
        }) 
       }, {}) 

     Object.defineProperty(prototype, 'applyFunction', { 
      value: fn => { fn(this); return this }, 
      enumerable: false, 
      writable: false, 
      configurable: false 
     }) 

     Object.setPrototypeOf(this, prototype) 
    } 
} 

(已取消)繼什麼BERGI說,我已經做到了這一點,用組成:

let encapsulate = function (guy) { 
    return function() { 
     let argumentsWrapper = 
      [].slice.apply(arguments) 
       .reduce((aw, argument, idx) => { 
        aw['a' + idx] = argument; 
        return aw; 
       }, {}) 

     this.inner = eval('new guy(' + 
      Object.keys(argumentsWrapper) 
       .reduce((string, argumentName, idx, arr) => 
        string + 'argumentsWrapper.' 
        + argumentName 
        + (idx != arr.length - 1 ? ',' : ''), 
       '') 
      + ')') 

     let setProperties = Object.getOwnPropertyNames(guy.prototype) 
      .filter(propName => propName.match(/^set/g)) 

     setProperties.forEach(function (propName) { 
       this[propName] = (function() { 
        guy.prototype[propName].apply(this.inner, [].slice.apply(arguments)) 
        return this 
       }).bind(this) 
      }, this) 

     Object.getOwnPropertyNames(guy.prototype) 
      .filter(propName => !propName.match(/^set/g)) 
      .forEach(function (propName) { 
       this[propName] = (function() { 
        return guy.prototype[propName].apply(this.inner, [].slice.apply(arguments)) 
       }).bind(this) 
      }, this) 

     this.applyFunction = fn => { 
      fn(this) 
      return this 
     } 
    } 
} 

這不是很美麗:/

+0

如何避免'eval',看看[這裏](http://stackoverflow.com/q/1606797/1048572)。另外,不應該將所有這些屬性放在返回函數的'.prototype'上嗎? – Bergi

+0

謝謝@Bergi!現在好嗎? – susanoobit

+0

不,不是在每次調用時重新創建這些'prototype'對象,而是使用'Object.setPrototypeOf(this)',您應該將它們分配給'encapsulate'返回的函數的'.prototype'。 – Bergi

相關問題