如何确定一个JavaScript函数是原生的(不使用测试“[native code]”)?

19
我想知道有没有办法区分JavaScript脚本函数(如function(){})和JavaScript原生函数(如Math.cos)。我已经知道了func.toString().indexOf('[native code]') != -1的技巧,但我想知道是否还有其他方法来检测它。
背景:我需要创建一个No-op转发ES6代理,它可以处理对象上的本地函数,但它会失败并显示TypeError: Illegal invocation(请参见Illegal invocation error using ES6 Proxy and node.js)。
为了解决这个问题,在我的代理的get处理程序中,我使用.bind()绑定了所有的函数,但如果我能有效地检测本地函数,我只需要绑定这些本地函数。
更多细节:https://github.com/FranckFreiburger/module-invalidate/blob/master/index.js#L106 注意:
(function() {}).toString() -> "function () {}"
(function() {}).prototype  -> {}

(require('os').cpus).toString() -> "function getCPUs() { [native code] }"
(require('os').cpus).prototype  -> getCPUs {}

(Math.cos).toString() -> "function cos() { [native code] }"
(Math.cos).prototype  -> undefined

(Promise.resolve().then).toString() -> "function then() { [native code] }"
(Promise.resolve().then).prototype  -> undefined

编辑:
目前最好的解决方案是测试!('prototype' in fun),但它不能与require('os').cpus一起使用...


1
你有检查过这个链接吗?https://davidwalsh.name/detect-native-function - Tareq
2
为什么你想要另一种方式? - jonrsharpe
@jonrsharpe,我需要创建一个No-op转发ES6代理,可以处理对象上的本地函数,但是这会失败(请参见https://dev59.com/n5_ha4cB1Zd3GeqP0owU)。 - Franck Freiburger
1
在问题中包含上下文会很有帮助,这样人们就不会建议其他具有类似限制的方法了。 - jonrsharpe
@jonrsharpe 我更新了问题。 - Franck Freiburger
显示剩余4条评论
2个回答

16

你可以尝试使用一个带有toString函数值的Function构造函数。如果它没有抛出错误,那么你会得到一个自定义函数,否则你就会得到一个原生函数。

function isNativeFn(fn) {
    try {
        void new Function(fn.toString());    
    } catch (e) {
        return true;
    }
    return false;
}

function customFn() { var foo; }

console.log(isNativeFn(Math.cos));          // true
console.log(isNativeFn(customFn));          // false
console.log(isNativeFn(customFn.bind({}))); // true, because bind 


这是 try ... catch 块的本质,但如果没有 indexOf 或正则表达式,我实际上看不到其他的方法。 - Nina Scholz
请检查以下代码:console.log(isNativeFn( customFn.bind( {/*whatever*/} )); - Thomas
"**bind()**方法创建一个新函数,并且显然是本地的。" - Nina Scholz
1
是的,但是当通过ES6代理访问时,它不会抛出“TypeError:非法调用”。 - Franck Freiburger
1
你的isNativeFn可以被绕过。 我伪造了一个函数:`var a=(function(){}).bind(null); isNativeFn(a); //true` - Sugar
显示剩余2条评论

8

我的总结是:不要使用它,它不起作用。你不能确定一个函数是否是原生的,因为Function#bind()也会创建“原生”函数。

function isSupposedlyNative(fn){
    return (/\{\s*\[native code\]\s*\}/).test(fn);
}

function foo(){ }
var whatever = {};

console.log("Math.cos():", isSupposedlyNative( Math.cos ));
console.log("foo():", isSupposedlyNative( foo ));
console.log("foo.bind():", isSupposedlyNative( foo.bind(whatever) ));

自从John-David Dalton发布的版本,Tareq在评论中提到的那个版本基本上做了相同的事情,所以这段代码也无法工作。我已经检查过了。
Nina的方法基于类似的原理,因为函数体中的[native code]部分再次在尝试将其解析为新函数时抛出错误。
唯一安全的确定您正在处理的函数是否为本机函数的方法是保持对本机函数的引用并将您的函数与该引用进行比较,但我想这对于您的用例来说可能不是一个选项。

我同意。此外,现在我们有代理API,使得检测函数是否被猴子补丁更加困难。 唯一“安全”的方法是保留“干净”的本地函数的引用,并稍后将您可能的猴子补丁函数与其进行比较(这可能不切实际)。 我在这里添加了更多上下文:https://dev59.com/TGw15IYBdhLWcg3wcrhC#73204287 - Matteo Mazzarolo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接