在声明一个 Javascript 函数之后,你能修改它吗?

假设我有 var a = function() { return 1; }。有没有可能改变 a,使 a()返回 2?也许通过编辑 a对象的属性,因为 每个函数都是一个对象

更新: 哇,谢谢所有的回复。然而,我恐怕不是简单地重新分配一个变量,而是实际编辑一个现有的函数。我正在考虑如何在 Scala 中组合 部分函数来创建一个新的 PartialFunction。我有兴趣在 Javascript 中编写一些类似的东西,并且认为现有的函数也许可以更新,而不是创建一个全新的 Function对象。

66082 次浏览
let a = function() { return 1; }
console.log(a()) // 1


a = function() { return 2; }
console.log(a()) // 2

从技术上讲,您正在丢失一个函数定义,并将其替换为另一个函数定义。

当然,只要给它分配一个新的函数。

你能不能稍后再重新定义它? 当你想要改变的时候,试着把它重新定义为:

a = function() { return 2; }

如果你正在调试 javascript,想看看代码的改变是如何影响页面的,你可以使用这个 Firefox 扩展来查看/修改 javascript:

执行 JS Firefox 扩展: Https://addons.mozilla.org/en-us/firefox/addon/1729

你可以用 javascript 做很多有趣的事情,包括重新定义函数:

let a = function() { return 1; }
console.log(a()); // 1
    

// keep a reference
let old = a;
   

// redefine
a = function() {
// call the original function with any arguments specified, storing the result
const originalResult = old.apply(old, arguments);
// add one
return originalResult + 1;
};


console.log(a()); // 2

瞧。

编辑: 在一个更疯狂的场景中显示:

let test = new String("123");
console.log(test.toString()); // logs 123
console.log(test.substring(0)); // logs 123
String.prototype.substring = function(){ return "hahanope"; }
console.log(test.substring(0)); // logs hahanope

您可以在这里看到,即使“ test”是先定义的,然后我们重新定义 substring () ,更改仍然适用。

附注: 如果你这样做的话,你真的应该重新考虑一下你的架构... ... 5年后,当某个可怜的开发人员看到一个应该返回1,但似乎总是返回2的函数定义时,你会把他们搞糊涂的。

这个怎么样,不用重新定义函数:

var a = function() { return arguments.callee.value || 1; };
alert(a()); // => 1
a.value = 2;
alert(a()); // => 2

我使用类似的东西来修改一个现有的函数,它的声明对我来说是不可访问的:

// declare function foo
var foo = function (a) { alert(a); };


// modify function foo
foo = new Function (
"a",
foo.toSource()
.replace("alert(a)", "alert('function modified - ' + a)")
.replace(/^function[^{]+{/i,"")  // remove everything up to and including the first curly bracket
.replace(/}[^}]*$/i, "")  // remove last curly bracket and everything after<br>
);

您可以使用 ToString ()来获取包含函数声明的字符串,而不是 toSource ()。一些调用来替换() ,以准备与函数构造函数一起使用的字符串,并修改函数的源代码。

我坚持 jvenema 的解决方案,其中我不喜欢全局变量“ old”。将旧功能保留在新功能中似乎更好:

function a() { return 1; }


// redefine
a = (function(){
var _a = a;
return function() {
// You may reuse the original function ...
// Typical case: Conditionally use old/new behaviour
var originalResult = _a.apply(this, arguments);
// ... and modify the logic in any way
return originalResult + 1;
}
})();
a()  // --> gives 2

这是一个基于控制计时器 eworld.ui清楚的例子 Www.eworldui.net

在 TimePicker eworld.ui中,从外部无法访问 JavaScript,因此找不到任何与这些控件相关的 js。那么如何将 onchange 事件添加到计时器中呢?

有一个 js函数调用时,你 Select的时间之间的所有选项,控制提供给你。这个函数是: TimePicker_Up_SelectTime

首先,您必须复制此函数中的代码。

评估... 快速手表... TimePicker_Up_SelectTime.toString()

function TimePicker_Up_SelectTime(tbName, lblName, divName, selTime, enableHide, postbackFunc, customFunc) {
document.getElementById(tbName).value = selTime;
if(lblName != '')
document.getElementById(lblName).innerHTML = selTime;
document.getElementById(divName).style.visibility = 'hidden';
if(enableHide)
TimePicker_Up_ShowHideDDL('visible');
if(customFunc != "")
eval(customFunc + "('" + selTime + "', '" + tbName + "');");
eval(postbackFunc + "();");
}

现在

使用在重新分配相同的源代码之前保存的代码,但是添加任何您想要的代码。

TimePicker_Up_SelectTime = function (tbName, lblName, divName, selTime, enableHide, postbackFunc, customFunc) {
document.getElementById(tbName).value = selTime;
if (lblName != '')
document.getElementById(lblName).innerHTML = selTime;
document.getElementById(divName).style.visibility = 'hidden';
if (enableHide)
TimePicker_Up_ShowHideDDL('visible');
if (customFunc != "")
eval(customFunc + "('" + selTime + "', '" + tbName + "');");
eval(postbackFunc + "();");


>>>>>>>  My function  >>>>>   RaiseChange(tbName);
}

我已经在函数中添加了 My Function,所以现在我可以在选择时间时模拟 onchange 事件。

RaiseChange (...)可以是任何你想要的。

因此,您需要直接就地修改函数的代码,而不仅仅是将不同的函数重新分配给现有变量。

我不想这么说,但是就我所能理解的而且我已经尝试过了,这是不可能的。的确,函数是一个对象,因此它具有可以在对象本身上进行调整和覆盖的方法和属性。不幸的是,函数体不在其中。它不分配给公共财产。

关于 MDN 的文件列出了函数对象的属性和方法。没有一个让我们有机会从外部操纵功能体。

这是因为函数体 根据规格存储在函数对象的内部 [[Code]]属性中,不能直接访问。

所有可行的解决方案都遵循“函数包装方法”。 其中最可靠的似乎是 rplantiko。

这样的函数包装很容易被抽象出来。概念/模式本身可以称为“方法修改”。它的实现绝对属于 Function.prototype。有人支持就好了 一天由标准的原型方法修饰如 beforeafteraroundafterThrowingafterFinally

至于上述 Rplantiko的例子..。

function a () { return 1; }


// redefine
a = (function () {
var _a = a;
return function () {
// You may reuse the original function ...
// Typical case: Conditionally use old/new behaviour
var originalResult = _a.apply(this, arguments);
// ... and modify the logic in any way
return originalResult + 1;
};
})();


console.log('a() ...', a()); // --> gives 2
.as-console-wrapper { min-height: 100%!important; top: 0; }

... and making use of around, the code would transform to ...

function a () { return 1; }


console.log('original a ...', a);
console.log('a() ...', a()); // 1




a = a.around(function (proceed, handler, args) {
return (proceed() + 1);
});


console.log('\nmodified a ...', a);
console.log('a() ...', a()); // 2
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
(function(d){function f(a){return typeof a==e&&typeof a.call==e&&typeof a.apply==e}function g(a,b){b=null!=b&&b||null;var c=this;return f(a)&&f(c)&&function(){return a.call(b||null!=this&&this||null,c,a,arguments)}||c}var e=typeof d;Object.defineProperty(d.prototype,"around",{configurable:!0,writable:!0,value:g});Object.defineProperty(d,"around",{configurable:!0,writable:!0,value:function(a,b,c){return g.call(a,b,c)}})})(Function);
</script>

您可以像更改其他对象一样更改函数

var a1 = function(){return 1;}
var b1 = a1;
a1 = function(){
return b1() + 1;
};
console.log(a1()); // return 2


// OR:
function a2(){return 1;}
var b2 = a2;
a2 = function(){
return b2() + 1;
};
console.log(a2()); // return 2

const createFunction = function (defaultRealization) {
let realization = defaultRealization;


const youFunction = function (...args) {
return realization(...args);
};
youFunction.alterRealization = function (fn) {
realization = fn;
};


return youFunction;
}


const myFunction = createFunction(function () { return 1; });
console.log(myFunction()); // 1


myFunction.alterRealization(function () { return 2; });
console.log(myFunction()); // 2