我最近有必要动态地在javascript中重写javascript函数。我做的很容易,有多么有趣,让我很震惊。
在这里,我有一些HTML:
<div id="excelExport1234"
onclick="if(somestuff) location.href='http://server/excelExport.aspx?id=56789&something=else'; else alert('not important');"
>Click here to export to excel</div>
我无法更改输出的HTML,但我需要在该链接中添加一个额外的参数。我开始考虑它,并意识到我可以这样做:
excelExport = $('excelExport1234');
if (needParam)
eval('excelExport.onclick = ' + excelExport.onclick.toString().replace("excelReport.aspx?id", "excelReport.aspx?extraParam=true&id") + ';');
else
eval('excelExport.onclick = ' + excelExport.onclick.toString().replace("extraParam=true&", "") + ';');
它就像一个冠军! excelExport.onclick返回一个函数对象,我将其转换为字符串,然后执行一些字符串操作。由于它现在采用“function(){...}”的形式,我只需返回并将其分配给dom对象的onclick事件。不得不使用eval有点难看,但是AFAIK没有一个javascript函数构造函数可以接受一串代码并很好地将它转换成一个对象。
无论如何,我的观点并不是我非常聪明(我不是),我的观点是这很酷。我知道javascript不是唯一可以做到这一点的语言。我听说过lisp为了这个目的已经有多年的宏了。除了真正的grok宏之外,你需要真正了解lisp,我不会理解它,我只是'得到它'。
所以我的问题是:您可以(轻松地)动态重写功能的其他语言,你能给我一个简单的例子吗?我想看看你能做到的其他地方,以及如何它完成了!
(另外,我也不知道该如何标记,因此我随机猜测)
答案 0 :(得分:9)
LISP是这方面的终极语言。 LISP函数是实际的LISP列表,这意味着您可以像操作任何其他数据结构一样操作LISP源代码。
以下是一个非常简单的例子:
(define hi
(lambda () (display "Hello World\n")))
;; Displays Hello World
(hi)
(set! hi
(lambda () (display "Hola World\n")))
;; Displays Hola World
(hi)
然而,这可以在函数是第一类对象的任何语言中使用。 LISP的这种语法的最有趣的展示之一是在它的宏系统中。我真的不觉得我可以做正义的主题,所以如果你有兴趣,请阅读这些链接:
http://en.wikipedia.org/wiki/Macro_(computer_science)#Lisp_macros
答案 1 :(得分:3)
我想这取决于你究竟定义为“轻松动态重写”。例如,在.Net中,你有Func类型和lambdas,它允许你将函数定义为变量或临时匿名函数,例如。
int[] numbers = {1, 2, 3, 4, 5};
Func<int[], int> somefunc;
if (someCondition)
{
somefunc = (is => is.Sum());
} else {
somefunc = (is => is.Count());
}
Console.WriteLine(somefunc(numbers).ToString());
以上是一个非常人为的例子,要么计算整数数组中的项目,要么使用动态创建的函数进行求和,这些函数受某些任意条件的影响。
注意 - 请不要指出这些事情可以很容易地完成而没有lambda(他们显然可以)我只是想写一个非常简单的例子来演示C#中的概念 < / p>
答案 2 :(得分:2)
Self-modifying code也称为退化代码。这通常被认为是一件坏事,它曾经是高级语言的目标,以防止它被轻易写入。
这来自维基百科条目:
有些人认为自我修改代码是一种不好的做法,会使代码难以阅读和维护。然而,有些方法可以认为自修改是可以接受的,例如当子例程指针被动态改变时 - 即使效果几乎与直接修改相同。
答案 3 :(得分:2)
我认为大多数动态语言都是如此。这是Python中的一个例子
def f(x): print x def new_function(x): print "hello", x f("world") f = new_function f("world")
输出
world hello world
我认为应该谨慎使用这种技术
答案 4 :(得分:2)
Scheme允许你这样做。
(define (salute-english name) (display "Hello ") (display name))
(define (salute-french nom) (display "Salut ") (display nom))
现在,您可以通过将salute
变量分配给正确的函数salute-english
或salute-french
来重新定义函数,如下所示:
(define salute salute-english)
(define (redefined-the-salute-function language)
(if (eq? language 'french)
(set! salute salute-french)
(set! salute salute-english)))
更多通用的函数式编程语言允许您这样做或者函数是第一类值。函数可以被操纵,传递,有时分配给变量等等。该列表包括: Lisp ,方案, Dylan , OCaml 和 SML 。一些具有一流功能的语言包括 Python , Ruby , Smalltalk ,我认为 Perl 。
请注意,当您拥有可以交互式键入程序的交互式语言时,必须重新定义函数/方法:REPL必须能够执行此操作,以防您碰巧重新键入已定义的功能。
答案 5 :(得分:1)
我曾经在TCL一直这样做,这是一件轻而易举的事情。我可以通过网络调查某些界面,然后动态创建一个定制的界面来访问和控制事物。例如,您可以从通用SNMP库创建自定义SNMP接口。
我没有使用它,但是C#有一些内置的支持来生成它自己的字节码,这相当令人印象深刻。
我在C中也做过这样的事情,但是它不可移植,几乎不值得麻烦。这种技术有时用于“自我优化”代码,以生成适当的C函数,以优化处理给定的数据集。
答案 6 :(得分:1)
Perl很容易。
*some_func = sub($) {
my $arg = shift;
print $arg, "\n";
};
some_func('foo');
Re Sam Saffron的要求:
*hello_world = sub() {
print "oops";
};
hello_world();
*hello_world = sub() {
print "hello world";
};
hello_world();
答案 7 :(得分:1)
你可以用C ++做,但这不容易,安全或推荐。
答案 8 :(得分:0)
在PLSQL中:
create or replace procedure test
as
begin
execute immediate '
create or replace procedure test2
as
begin
null;
end;
';
end;
/
答案 9 :(得分:0)
这是Python中的其他内容(除了luc的答案),我不推荐,但只是为了展示它 - 有exec,它可以执行一个字符串,你可以构建成任何代码......
此处显示的I / O来自Python 2.5.2解释器会话。只是一些构造字符串以从子字符串执行的简单示例(&gt;&gt;&gt;是解释器提示符)...
>>> def_string = 'def my_func'
>>> param_string_1 = '():'
>>> param_string_2 = '(x):'
>>> do_string_1 = ' print "Do whatever."'
>>> do_string_2 = ' print "Do something with", x'
>>> do_string_3 = ' print "Do whatever else."'
>>> do_string_4 = ' print "Do something else with", x'
>>> def_1 = '\n'.join([def_string+param_string_1, do_string_1, do_string_3])
>>> print def_1
def my_func():
print "Do whatever."
print "Do whatever else."
>>> exec def_1
>>> my_func()
Do whatever.
Do whatever else.
>>> def_2 = '\n'.join([def_string+param_string_2, do_string_2, do_string_4])
>>> print def_2
def my_func(x):
print "Do something with", x
print "Do something else with", x
>>> exec def_2
>>> my_func('Tom Ritter')
Do something with Tom Ritter
Do something else with Tom Ritter
>>>
答案 10 :(得分:0)
Ruby中的小事:
def hello_world; puts "oops"; end
hello_world
# oops
def hello_world; puts "hello world"; end
hello_world
# hello world
当然这个例子很无聊:
require "benchmark"
# why oh _why
class Object
def metaclass; class << self; self; end; end
def meta_eval &blk; metaclass.instance_eval &blk; end
end
class Turtle
end
def make_it_move(klass)
klass.send(:define_method, :move) { |distance|
puts "moving #{distance} meters"
sleep(0.1 * distance)
}
end
make_it_move(Turtle)
turtle = Turtle.new
turtle.move(1)
# moving 1 meters
def profile(instance, method)
instance.meta_eval do
m = instance_method(method)
define_method method do |*a|
puts "Benchmarking #{instance.class} #{method}"
puts Benchmark.measure {
m.bind(instance).call(*a)
}
end
end
end
profile(turtle, :move)
turtle.move(10)
# Benchmarking Turtle move
# moving 10 meters
# 0.000000 0.000000 0.000000 ( 1.000994)
Turtle.new.move(3)
# moving 3 meters
上面的代码:
答案 11 :(得分:0)
许多语言都支持更改函数的功能,并且它并不像您想象的那么复杂。在函数式语言中,函数是值,函数名称是与任何变量一样绑定到它们的符号。如果该语言允许您将符号重新分配给其他功能,则这很简单。
我认为更有趣的功能是能够获取函数的源代码(上面是toString
)并从字符串创建新函数(在本例中为eval
)。