2011-09-24 72 views
47
// Don't break the function prototype. 
// pd - https://github.com/Raynos/pd 
var proto = Object.create(Function.prototype, pd({ 
    "prop": 42 
})); 

var f = function() { return "is a function"; }; 
f.__proto__ = proto; 

console.log(f.hasOwnProperty("prop")); // false 
console.log(f.prop); // 42 
console.log(f()); // "is a function" 

.__proto__是非標準的,不推薦使用。如何繼承JavaScript功能?

我該如何繼承原型創建對象,但讓該對象成爲函數。

Object.create返回對象不是函數。

new Constructor返回一個對象不是函數。

動機: - 一個跨瀏覽器finherit

var finherit = function (parent, child) { 
    var f = function() { 
     parent.apply(this, arguments); 
     child.apply(this, arguments); 
    }; 
    f.__proto__ = parent; 
    Object.keys(child).forEach(function _copy(key) { 
     f[key] = child[key]; 
    }); 
    return f; 
}; 

我不相信這是可能的,所以我們也許應該提出一個Function.create到ES-討論郵件列表

/* 
    Creates a new function whose prototype is proto. 
    The function body is the same as the function fbody. 
    The hash of propertydescriptors props is passed to defineproperties just like 
    Object.create does. 
*/ 
Function.create = (function() { 
    var functionBody = function _getFunctionBody(f) { 
    return f.toString().replace(/.+\{/, "").replace(/\}$/, ""); 
    }; 
    var letters = "abcdefghijklmnopqrstuvwxyz".split(""); 

    return function _create(proto, fbody, props) { 
    var parameters = letters.slice(0, fbody.length); 
    parameters.push(functionBody(fbody)); 
    var f = Function.apply(this, parameters); 
    f.__proto__ = proto; 
    Object.defineProperties(f, props); 
    return f; 
    }; 
})(); 

Related es-discuss mail

正如在es-discuss線程中提到的,存在一個ES:稻草人<|原型操作員將允許這一點。

讓我們來看看它會是什麼樣使用<|

var f1 = function() { 
    console.log("do things"); 
}; 

f1.method = function() { return 42; }; 

var f2 = f1 <| function() { 
    super(); 
    console.log("do more things"); 
} 
console.log(f1.isPrototypeOf(f2)); // true 
console.log(f2()); // do things do more things 
console.log(f2.hasOwnProperty("method")); // false 
console.log(f2.method()); // 42 
+0

'console.log(f.apply);'將返回'undefined' ...因此,這種方式在任何方面都不是最佳的......仍在考慮替代方案。 –

+0

@FelixKling我忘了我應該從'Function.prototype'繼承謝謝 – Raynos

+0

有一個[這個問題](http://stackoverflow.com/questions/6564481/javascript-polymorphic-callable-objects)我遇到一段時間回到哪裏這個提問者引用了[這篇文章](http://ajaxian.com/archives/javascript-tips-for-rookies-and-gurus),它顯示了創建*「多態可調用對象」*的模式。不知道這是否是你之後,但可能值得一看。 – user113716

回答

9

我希望我理解這個權利。

我相信你想要一個函數既是一個預定義的原型(是的,一個類,只是不是一個經典的類)的實例,以及直接可調用?對?如果是這樣,那麼這是非常有意義的,並且非常強大和靈活(特別是在JavaScript等高度異步的環境中)。 可悲的是,沒有辦法做到這一點雅緻 JavaScript中沒有操縱__proto__。您可以通過分解一個匿名函數並將所有引用複製到所有方法(這似乎是您正在引導的方向)來充當代理類來執行此操作。這個缺點是...

  1. 它在運行時方面非常昂貴。
  2. (functorObj instanceof MyClass)永遠不會是true
  3. 屬性將不能直接訪問(如果它們都是通過引用分配的,這將是一個不同的故事,但基元是按值賦值的)。這可以通過訪問器通過defineProperty解決,或者在必要時簡單地命名訪問器方法(看起來就是你要找的東西,只需通過獲取器/設置器將所有屬性添加到函子defineProperty而不只是函數,如果你不需要跨引擎支持/向後兼容)。
  4. 您可能會遇到最終原生原型(如Object.prototype或Array.prototype [如果您繼承])可能無法按預期方式運行的邊緣情況。
  5. 調用functorObj(someArg)總是使this上下文對象,無論是否被稱爲functorObj.call(someOtherObj, someArg)(這是不是這樣的方法調用,雖然)
  6. 因爲仿函數對象是在請求時創建的,這將是在時間上鎖定並操縱初始原型不會影響分配的函數對象,就像普通對象會受到影響(修改MyClass.prototype不會影響任何函子對象,反之亦然)。

如果你輕輕地使用它,但沒有一個應該是個大問題。

在你的類的原型定義類似...

// This is you're emulated "overloaded" call() operator. 
MyClass.prototype.execute = function() { 
    alert('I have been called like a function but have (semi-)proper access to this!'); 
}; 

MyClass.prototype.asFunctor = function(/* templateFunction */) { 
    if ((typeof arguments[0] !== 'function') && (typeof this.execute !== 'function')) 
     throw new TypeError('You really should define the calling operator for a functor shouldn\'t you?'); 
    // This is both the resulting functor proxy object as well as the proxy call function 
    var res = function() { 
     var ret; 
     if (res.templateFunction !== null) 
     // the this context here could be res.asObject, or res, or whatever your goal is here 
     ret = res.templateFunction.call(this, arguments); 
     if (typeof res.asObject.execute === 'function') 
     ret = res.asObject.execute.apply(res.asObject, arguments); 
     return ret; 
    }; 
    res.asObject = this; 
    res.templateFunction = (typeof arguments[0] === 'function') ? arguments[0] : null; 
    for (var k in this) { 
     if (typeof this[k] === 'function') { 
     res[k] = (function(reference) { 
      var m = function() { 
       return m.proxyReference.apply((this === res) ? res.asObject : this, arguments); 
      }; 
      m.proxyReference = reference; 
      return m; 
     })(this.asObject[k]); 
     } 
    } 
    return res; 
}; 

得到的使用看起來像......

var aobj = new MyClass(); 
var afunctor = aobj.asFunctor(); 
aobj.someMethodOfMine(); // << works 
afunctor.someMethodOfMine(); // << works exactly like the previous call (including the this context). 
afunctor('hello'); // << works by calling aobj.execute('hello'); 

(aobj instanceof MyClass) // << true 
(afunctor instanceof MyClass) // << false 
(afunctor.asObject === aobj) // << true 

// to bind with a previous function... 
var afunctor = (new MyClass()).asFunctor(function() { alert('I am the original call'); }); 
afunctor() // << first calls the original, then execute(); 
// To simply wrap a previous function, don't define execute() in the prototype. 

你甚至可以綁定鏈無數其它物品/功能/直到奶牛回家。只需重構代理呼叫。

希望有所幫助。哦,當然,你可以改變工廠流程,這樣一個沒有new操作符的構造函數就會實例化一個新對象並返回該函子對象。然而你更喜歡(你也可以用其他方式來做)。

最後,要讓函數變得更加優雅一些,可以使代理函數成爲Function.prototype的方法,並將對象傳遞給它,如果你想要做一些類似於(你將不得不換用templateFunctionthisthis課程)的說法...

var functor = (function() { /* something */ }).asFunctor(aobj); 
+3

很酷。我從來沒有聽說過函子。我肯定會命名我的下一個狗函子。 – Ben

+0

這真是一個僞函數(是的,有趣的名字)代理的一切。這是一個馬虎的方式來實現它。有些語言本身就支持這一點。如果EMCAScript5允許使用'__proto__'操作(或等效的替代標準),**或**操作符重載,那麼JavaScript也是如此。可悲的是,事實並非如此。儘管如此,它的力量和靈活性......嘆了口氣。 –

+0

@ mr.stobbe你可以刺激使用ES6代理來做到這一點嗎? (在鉻和FF工程)。或者你可以作弊並假裝存在('protoOf = function(proto,thing){thing .__ proto__ = proto;}') – Raynos