我想要它的主要原因是我想扩展我的初始化功能。
像这样的东西:
// main.js
window.onload = init();
function init(){
doSomething();
}
// extend.js
function extends init(){
doSomethingHereToo();
}
所以我想扩展一个函数,就像我在 PHP 中扩展一个类一样。
我也想从其他文件中扩展它,例如,我main.js在extended.js.
我想要它的主要原因是我想扩展我的初始化功能。
像这样的东西:
// main.js
window.onload = init();
function init(){
doSomething();
}
// extend.js
function extends init(){
doSomethingHereToo();
}
所以我想扩展一个函数,就像我在 PHP 中扩展一个类一样。
我也想从其他文件中扩展它,例如,我main.js在extended.js.
通过更广泛地了解您实际尝试做什么以及您在做什么的背景下,我相信我们可以为您提供比问题的字面答案更好的答案。
但这是一个字面的答案:
如果您将这些函数分配给某处的某个属性,则可以包装原始函数并将替换的函数放在该属性上:
// Original code in main.js
var theProperty = init;
function init(){
doSomething();
}
// Extending it by replacing and wrapping, in extended.js
theProperty = (function(old) {
function extendsInit() {
old();
doSomething();
}
return extendsInit;
})(theProperty);
如果您的函数还没有在对象上,您可能希望将它们放在那里以方便上述操作。例如:
// In main.js
var MyLibrary = {
init: function init() {
}
};
// In extended.js
(function() {
var oldInit = MyLibrary.init;
MyLibrary.init = extendedInit;
function extendedInit() {
oldInit.call(MyLibrary); // Use #call in case `init` uses `this`
doSomething();
}
})();
但是有更好的方法来做到这一点。例如,提供一种注册init函数的方法。
// In main.js
var MyLibrary = (function() {
var initFunctions = [];
return {
init: function init() {
var fns = initFunctions;
initFunctions = undefined;
for (var index = 0; index < fns.length; ++index) {
try { fns[index](); } catch (e) { }
}
},
addInitFunction: function addInitFunction(fn) {
if (initFunctions) {
// Init hasn't run yet, remember it
initFunctions.push(fn);
} else {
// `init` has already run, call it almost immediately
// but *asynchronously* (so the caller never sees the
// call synchronously)
setTimeout(fn, 0);
}
}
};
})();
在 2020 年(或实际上是 2016 年之后的任何时间),可以写得更紧凑一点:
// In main.js
const MyLibrary = (() => {
let initFunctions = [];
return {
init() {
const fns = initFunctions;
initFunctions = undefined;
for (const fn of fns) {
try { fn(); } catch (e) { }
}
},
addInitFunction(fn) {
if (initFunctions) {
// Init hasn't run yet, remember it
initFunctions.push(fn);
} else {
// `init` has already run, call it almost immediately
// but *asynchronously* (so the caller never sees the
// call synchronously)
setTimeout(fn, 0);
// Or: `Promise.resolve().then(() => fn());`
// (Not `.then(fn)` just to avoid passing it an argument)
}
}
};
})();
有几种方法可以解决这个问题,这取决于您的目的是什么,如果您只想在相同的上下文中执行该函数,您可以使用.apply():
function init(){
doSomething();
}
function myFunc(){
init.apply(this, arguments);
doSomethingHereToo();
}
如果你想用更新的替换它init,它看起来像这样:
function init(){
doSomething();
}
//anytime later
var old_init = init;
init = function() {
old_init.apply(this, arguments);
doSomethingHereToo();
};
其他方法很棒,但它们不保留任何附加到 init 的原型函数。为了解决这个问题,您可以执行以下操作(灵感来自 Nick Craver 的帖子)。
(function () {
var old_prototype = init.prototype;
var old_init = init;
init = function () {
old_init.apply(this, arguments);
// Do something extra
};
init.prototype = old_prototype;
}) ();
另一种选择可能是:
var initial = function() {
console.log( 'initial function!' );
}
var iWantToExecuteThisOneToo = function () {
console.log( 'the other function that i wanted to execute!' );
}
function extendFunction( oldOne, newOne ) {
return (function() {
oldOne();
newOne();
})();
}
var extendedFunction = extendFunction( initial, iWantToExecuteThisOneToo );
函数扩展的思想来自函数范式,从 ES6 开始就支持它:
function init(){
doSomething();
}
// extend.js
init = (f => u => { f(u)
doSomethingHereToo();
})(init);
init();
根据@TJCrowder 对堆栈转储的担忧,今天的浏览器处理情况要好得多。如果您将此代码保存到test.html 中并运行它,您将得到
test.html:3 Uncaught ReferenceError: doSomething is not defined
at init (test.html:3)
at test.html:8
at test.html:12
第 12 行:init 调用,第 8 行:init 分机,第 3 行:未定义的doSomething()调用。
注意:非常尊重老将 TJ Crowder,他多年前在我还是个新手时亲切地回答了我的问题。多年后,我仍然记得尊重的态度,我努力学习好榜样。