转到正文

浪淘沙

静观己心,厚积薄发

存档

2015 年 10 月 的存档

I have the name of a function in JavaScript as a string. How do I convert that into a function pointer so I can call it later?

Depending on the circumstances, I may need to pass various arguments into the method too.

Some of the functions may take the form of namespace.namespace.function(args[...]).

 

========

Don’t use eval unless you absolutely, positively have no other choice.

As has been mentioned, using something like this would be the best way to do it:

window["functionName"](arguments);

That, however, will not work with a namespace’d function:

window["My.Namespace.functionName"](arguments); // fail

This is how you would do that:

window["My"]["Namespace"]["functionName"](arguments); // succeeds

In order to make that easier and provide some flexibility, here is a convenience function:

function executeFunctionByName(functionName, context /*, args */) {
  var args = [].slice.call(arguments).splice(2);
  var namespaces = functionName.split(".");
  var func = namespaces.pop();
  for(var i = 0; i < namespaces.length; i++) {
    context = context[namespaces[i]];
  }
  return context[func].apply(this, args);
}

You would call it like so:

executeFunctionByName("My.Namespace.functionName", window, arguments);

Note, you can pass in whatever context you want, so this would do the same as above:

executeFunctionByName("Namespace.functionName", My, arguments);

eval executes a string containing code, e.g.

eval("var x = 'Hello from eval!';");
console.log(x);

eval raises several issues:

  1. Security: your string can be injected with other commands by third-party scripts or user input.
  2. Debugging: it’s difficult to debug errors — you have no line numbers or obvious points of failure.
  3. Optimization: the JavaScript interpreter cannot necessarily pre-compile the code because it could change. While interpreters have become increasingly efficient, it’ll almost certainly run slower than native code.

Unfortunately, eval is very powerful and it’s easy for less experienced developers to overuse the command.

Despite the warnings, eval still works — even in Strict Mode — but you can normally avoid it. In the past it was primarily used for de-serializing JSON strings but we now have the safer JSON.parse method.

However, what if we have a function name in a string, e.g.

// function we want to run
var fnstring = "runMe";

function runMe() {
	// do stuff
}

How do we execute the runMe() function without using eval? I recently encountered this situation when using the HTML5 History API; the pushState method won’t permit you to store a direct reference to a function so you need to define its name as a string. You could also face similar challenges using Web Workers or any other API where objects are serialized.

The simplest and safest execution-without-eval solution is a range of conditions, e.g.

// function we want to run
var fnstring = "runMe";

switch (fnstring) {
	case "functionX": functionX(); break;
	case "functionY": functionY(); break;
	case "functionZ": functionZ(); break;
	case "runMe": runMe(); break;
}

It’s safe, but fairly inefficient and painful to write if you have dozens of possible function calls.

A better solution is to use the window object which references the current window and all items within it. We can check whether fnstring is available as an object within window and run it if it’s a function, e.g.

// function we want to run
var fnstring = "runMe";

// find object
var fn = window[fnstring];

// is object a function?
if (typeof fn === "function") fn();

You can perform other checks if necessary to ensure the function has an expected name.

What if the function we want to call has parameters — perhaps stored in an array? No problem; we simply use the apply method:

// function name and parameters to pass
var fnstring = "runMe";
var fnparams = [1, 2, 3];

// find object
var fn = window[fnstring];

// is object a function?
if (typeof fn === "function") fn.apply(null, fnparams);

So that’s another reason to stop using eval. As a bonus, this solution is safer, less error prone, easier to debug and will normally execute faster. I hope it helps.

 

One could also use:
setTimeout(“runMe”,0);

if(‘$’ in window && typeof window[‘$’] == ‘function’) window[‘$’](document)

js有的时候会出现,将方法名称作为一个参数传递到方法中,然后再调用。这个时候,就有可能会出现问题。

场景一

<html>
<head>
<title>js fucntion name as the paramter</title>
</head>
<body>

<input type="button" name="button" value="button" onclick="passParam(fname);">button content</button>
</body>
<script type="text/javascript" src="jquery-1.8.2.min.js"></script>
<script type="text/javascript">
function passParam(param) {
alert(typeof param);
param("sssssss");
}
function fname(s) {
alert("fname:(" + s + ")");
}
</script>
</html>

这种情况,没有任何问题,可以直接调用。typeof  param 结果为 function

场景二

<html>
<head>
<title>js fucntion name as the paramter</title>
</head>
<body>

<input type="button" name="button" value="button" onclick="passParam('fname');">button content</button>
</body>
<script type="text/javascript" src="jquery-1.8.2.min.js"></script>
<script type="text/javascript">
function passParam(param) {
alert(typeof param);
param("sssssss");
}
function fname(s) {
alert("fname:(" + s + ")");
}
</script>
</html>

和场景一唯一不同的就是参数使用引号包裹了下,就这么一点点差别,结果却是完全不同。typeof param 的结果为string .最终的运行结果为:TypeError: param is not a function。这样操作,js尝试运行的是param而不是param对应的值fname。

这个时候有同学就说,使用eval,eval将任何的string作为code来运行。

场景三

<html>
<head>
<title>js fucntion name as the paramter</title>
</head>
<body>

<input type="button" name="button" value="button" onclick="passParam('fname');">button content</button>
</body>
<script type="text/javascript" src="jquery-1.8.2.min.js"></script>
<script type="text/javascript">
function passParam(param) {
alert(typeof param);
eval("param(\"sssssss\")");
}
function fname(s) {
alert("fname:(" + s + ")");
}
</script>
</html>

结果依然和场景二一样。这个时候会有同学说:“不对,不对,你的eval使用方法错了”。然后这个同学给出了TA自己eval方法的实现方式

场景四

<html>
<head>
<title>js fucntion name as the paramter</title>
</head>
<body>

<input type="button" name="button" value="button" onclick="passParam('fname');">button content</button>
</body>
<script type="text/javascript" src="jquery-1.8.2.min.js"></script>
<script type="text/javascript">
function passParam(param) {
alert(typeof param);
eval(param + "(\"sssssss\")");
}
function fname(s) {
alert("fname:(" + s + ")");
}
</script>
</html>

运行,ok很好。正确执行了相应的方法,问题得到了解决。

大家都知道eval的功能异常的强大,强大到我们不敢、也不能随随便便使用它。那么我们能不能不适用eval方法的实现方式哪?大家开动脑筋,想了一会,这个时候有另外一个同学兴奋的说有了,可以使用setTimeout间接调用

场景五

<html>
<head>
<title>js fucntion name as the paramter</title>
</head>
<body>

<input type="button" name="button" value="button" onclick="passParam('fname');">button content</button>
</body>
<script type="text/javascript" src="jquery-1.8.2.min.js"></script>
<script type="text/javascript">
function passParam(param) {
alert(typeof param);
setTimeout(param + "(\"ssssss\")",0);
}
function fname(s) {
alert("fname:(" + s + ")");
}
</script>
</html>

运行结果很好。那么是否还有其他方法可以实现这个哪?大家又陷入了深思。。

“window,可以使用当前window对象。我们定义的所有元素都会包含在window对象里面。”  这个时候一个默默无闻的同学终于开口了。

场景六

<html>
<head>
<title>js fucntion name as the paramter</title>
</head>
<body>

<input type="button" name="button" value="button" onclick="passParam('fname');">button content</button>
</body>
<script type="text/javascript" src="jquery-1.8.2.min.js"></script>
<script type="text/javascript">
function passParam(param) {
alert(typeof param);
// find object
var fn = window[param];
// is object a function?
if (typeof fn === "function") fn("sssss");
}
function fname(s) {
alert("fname:(" + s + ")");
}
</script>
</html>

运行下,非常棒。

这个方法还可以优化下。

场景七

<html>
<head>
<title>js fucntion name as the paramter</title>
</head>
<body>

<input type="button" name="button" value="button" onclick="passParam('fname');">button content</button>
</body>
<script type="text/javascript" src="jquery-1.8.2.min.js"></script>
<script type="text/javascript">
function passParam(param) {
alert(typeof param);

<div class="post-message " dir="auto" data-role="message">

if(param in window && typeof window[param] == 'function') window[param]("sssss");
}
function fname(s) {
alert("fname:(" + s + ")");
}
</script>
</html>

不错不错。

还可以再优化,可以抽象出来

/**
 * 根据字符串类型的方法名 调用对应的js function
 * executeFunctionByName("My.Namespace.functionName", window, arguments);
 * executeFunctionByName("Namespace.functionName", My, arguments);
 *
 * @param functionName
 * @param context
 * @returns {*}
 */

var smvcJSUtil = {
    executeFunctionByName: function (functionName, context /*, args */){ //对调 functionName function
        var args = [].slice.call(arguments).splice(2);
        var namespaces = functionName.split(".");
        var func = namespaces.pop();
        for (var i = 0; i < namespaces.length; i++) {
            context = context[namespaces[i]];
        }
        return context[func].apply(this, args);
    },
    functionExistsByName: function(functionName) { //判断制定字符串是否为function
        return functionName in window && typeof window[functionName] == 'function'
    }
};

方法越来越好了。。我们原来的问题不但解决了,而且还因此学到了很多知识。

参考:

http://www.sitepoint.com/call-javascript-function-string-without-using-eval/

http://stackoverflow.com/questions/359788/how-to-execute-a-javascript-function-when-i-have-its-name-as-a-string

 

上次发布代码的时候 再某个model类里面发现了一段比较奇葩的代码,代码如下:

$req_arr['PosFlag'] = 0;//上生产要改成2

顿时就被这种写法弄得雷死了。这种各种环境不同的情况不应该使用配置项来处理吗?不错,程序猿是可以懒,大家也推荐程序猿懒,

但这种做法不是懒,是没有脑子。

说起来这个还想起来上次发现的一个问题。再模板文件里面写死链接地址(链接地址也是有平台差异的)。我也是无力吐槽了。

 

一句话,请谨记:所有平台差异都不应该hardcoding在代码里面,都必须写到配置里面。

还有一句,别特么动不动就写1,2,3这样的魔术数字,你特么注释都不加,鬼知道这些带别什么意思。