你可以在声明后更改 Javascript 函数吗?

IT技术 javascript function oop wrapper modifier
2021-02-23 00:52:08

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

更新:哇,谢谢大家的回复。但是,恐怕我不想简单地重新分配变量,而是实际编辑现有函数。我正在思考如何在 Scala 中结合部分函数来创建一个新的PartialFunction. 我有兴趣在 Javascript 中编写类似的东西,并认为现有的函数可能会被更新,而不是创建一个全新的Function对象。

6个回答

你可以用 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(),但更改仍然适用。

旁注:如果你这样做,你真的应该重新考虑你的架构......当他/他正在查看一个应该返回 1 的函数定义时,你会在 5 年后混淆一些可怜的开发人员的废话,但似乎总是返回 2....

@hashchange:触摸。也就是说,它只是一个“var”引用这一事实在这里无关紧要。例如,如果您想更改应用程序中的核心功能,只需修改原型即可。这个概念仍然适用。
2021-04-23 00:52:08
这不会重新定义函数。它将一个全新的函数分配给引用旧函数的变量。不会更新其他地方对原始函数的引用。
2021-05-06 00:52:08
谢谢您的帮助!我环顾四周,希望找到一种方法来保存任何当前的 window.onload 函数,然后附加我自己的函数(分配 window.onload 会覆盖任何以前的函数)。
2021-05-14 00:52:08

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

// 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() 一些对 replace() 的调用准备用于函数构造函数的字符串并修改函数的源代码。

有趣的是,这就是垫片的本质。当浏览器没有实现某个功能或功能很差时,您可以使用自己的自定义功能设置内置对象,因此您的脚本始终具有统一的环境。我希望在整个函数上有比字符串替换更细粒度的控制。但至少函数引用在替换和重新解析后似乎仍然有效,所以这很方便。
2021-04-23 00:52:08
感谢 hmundt 和 Bubu Daba。正确,显然必须使用字符串。
2021-04-30 00:52:08
有趣的。但是,不经过字符串就没有办法做到这一点吗?无赖。
2021-05-10 00:52:08
我想更改生成的函数的内容(通过 Animate)。这将是正确的答案。但截至 2016 年,仍然不(或已经不)支持 toSource。此外,这使用正则表达式,在不需要它的地方,可以使用 indexOf/lastIndexOf。这使用 toString() 而没有 regex还可以考虑代理它
2021-05-10 00:52:08
toSource 不适用于 IE 和 Chrome。这更容易获得函数代码:foo.toString().match(/{([\s\S]*)}/)[1]. 另外@prm1001 你应该接受这个作为正确答案。它也帮助了我
2021-05-15 00:52:08

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

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

MDN 上文档列出了函数对象的属性和方法。它们都没有给我们机会从外部操纵函数体。

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

我很努力,也没有办法实现。甚至 Proxy 也不会真正让您挂钩原始函数,因为要拦截您必须调用新的代理。
2021-04-30 00:52:08
这是唯一正确的答案。该规范从未允许对声明的 Function 进行可写访问[[Code]],只能通过toString().
2021-05-01 00:52:08
let a = function() { return 1; }
console.log(a()) // 1

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

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

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

var a = function() { return arguments.callee.value || 1; };
alert(a()); // => 1
a.value = 2;
alert(a()); // => 2
不幸的是,arguments.callee 已被弃用
2021-04-26 00:52:08
这是目前唯一正确的答案 - 唯一真正影响功能的答案,而不是创建新的答案。
2021-04-30 00:52:08
同意,每次重新定义它时,都会覆盖旧参考,而不会更改其他现有参考,这确实是 sux。
2021-05-12 00:52:08
如果你给函数命名,你可以在函数体上使用它的名字作为参考... const a = function myFunction() { return myFunction.value || 1 }
2021-05-15 00:52:08