声明后可以改变Javascript函数吗?

pr1*_*001 59 javascript oop function

让我说我有var a = function() { return 1; }.有可能改变a以便a()返回2吗?也许通过编辑a对象的属性,因为每个函数都是一个对象

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

jve*_*ema 41

你可以用javascript做各种有趣的事情,包括重新定义功能:

var a = function(){ return 1; }

alert(a()); //1

// keep a reference
var old = a;

// redefine
a = function(){
  // call the original function with any arguments specified, storing the result
  var originalResult = old.apply(old, arguments);
  // add one
  return originalResult + 1;
};
alert(a()); //2
Run Code Online (Sandbox Code Playgroud)

瞧.

编辑:更新以在更疯狂的场景中显示:

var 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
Run Code Online (Sandbox Code Playgroud)

你可以在这里看到,即使首先定义了"test",然后我们重新定义了substring(),但更改仍然适用.

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

  • 这不会重新定义该功能.它为引用旧的变量赋予一个全新的函数.其他地方对原始功能的引用不会更新. (25认同)
  • @hashchange:touche.也就是说,它只是一个"var"参考的事实在这里是无关紧要的.如果您想在应用程序中更改核心功能,则只需修改原型即可.这个概念仍然适用. (2认同)

hmu*_*ndt 40

我使用这样的东西来修改我无法访问其声明的现有函数:

// 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>
);
Run Code Online (Sandbox Code Playgroud)

您可以使用toString()来获取包含函数声明的字符串,而不是toSource().有些调用replace()来准备字符串以便与Function Constructor一起使用并修改函数的源代码.

  • toSource不适用于IE和Chrome.这更容易获得函数代码:`foo.toString().match(/ {([\ s\S]*)} /)[1]`.另外@ prm1001你应该接受这个作为正确的答案.它确实对我有所帮助 (6认同)
  • 我想更改生成函数的内容(通过Animate).这将是正确的答案.但截至2016年,toSource仍然没有(或已经没有)支持.此外,这使用正则表达式,不需要它,并且可以使用indexOf/lastIndexOf.[这使用toString()而没有正则表达式](http://stackoverflow.com/questions/3472370/how-can-dynmically-change-a-line-of-a-javascript-function/#answer-3472440).还要考虑[代理](http://stackoverflow.com/questions/1729501/javascript-overriding-alert/#answer-1729684) (2认同)

Sam*_*ler 22

var a = function() { return 1; }
alert(a()) // 1
a = function() { return 2; }
alert(a()) // 2
Run Code Online (Sandbox Code Playgroud)

从技术上讲,你丢失了一个函数定义并将其替换为另一个函数定义.


has*_*nge 19

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

我不想这么说,但就我能够弄清楚 - 而且我已经尝试过 - 它无法完成.确实,函数是一个对象,因此它具有可以在对象本身上进行调整和覆盖的方法和属性.不幸的是,功能体不是其中之一.它未分配给公共财产.

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

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


jps*_*ons 15

怎么样,无需重新定义功能:

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

  • 这是目前唯一正确的答案 - 唯一真正影响功能的答案,而不是创建新的答案。 (2认同)

rpl*_*iko 11

我坚持使用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
Run Code Online (Sandbox Code Playgroud)


Pet*_*ger 6

所有可行的解决方案都坚持“函数包装方法”。 其中最可靠的,似乎是rplantiko之一

这样的函数包装很容易被抽象掉。概念/模式本身可能被称为“方法修改”。它的实现绝对属于Function.prototype. 这将是很好的标准原型方法修饰符等支持1天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
Run Code Online (Sandbox Code Playgroud)
.as-console-wrapper { min-height: 100%!important; top: 0; }
Run Code Online (Sandbox Code Playgroud)

...并利用around,代码将转换为 ...

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
Run Code Online (Sandbox Code Playgroud)
.as-console-wrapper { min-height: 100%!important; top: 0; }
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)