Wrapper functions and function.

Consider the following code I have

/*...*/ var _fun = fun; fun = function() { /*...*/ _fun.apply(this, arguments); } 

I just lost the .length data on _fun because I tried to wrap it with some hooking logic.

Does not work

 var f = function(a,b) { }; console.log(f.length); // 2 f.length = 4; console.log(f.length); // 2 

ES5.1 annotated specification states that .length is defined as follows

 Object.defineProperty(fun, "length", { value: /*...*/, writable: false, configurable: false, enumerable: false } 

Given that the logic inside fun requires .length be exact, how can I intercept and overwrite this function without destroying .length data?

I have a feeling that I will need to use eval and the ingenious Function.prototype.toString to create a new line with the same number of arguments. I want to avoid this.

+6
source share
4 answers

I know that you would prefer some other way, but all I can think of is to put together something with the Function constructor. Smaller to say the least, but it seems to work:

 var replaceFn = (function(){ var args = 'abcdefghijklmnopqrstuvwxyz'.split(''); return function replaceFn(oldFn, newFn) { var argSig = args.slice(0, oldFn.length).join(','); return Function( 'argSig, newFn', 'return function(' + argSig + '){return newFn.apply(this, arguments)}' )(argSig, newFn); }; }()); // Usage: var _fun = fun; fun = replaceFn(fun, function() { /* ... */ _fun.apply(this, arguments); }); 
+3
source

The fake length is correctly and consistently formed - the last border in javascript, and this is largely the beginning and the end. In a language where you can fake almost everything, the length is still somewhat magical. ES6 will ship, and we can fake it now to a greater and lesser extent depending on which engine and version you have. For general network compatibility this is not possible. Proxies / noSuchMethod has been in Mozilla for some time. Proxies and WeakMaps got access to use in V8 in Chromium and node (to enable flags), which provide the tool necessary for the correct length.

More on "length": http://perfectionkills.com/how-ecmascript-5-still-does-not-allow-to-subclass-an-array/

Possible solution: http://wiki.ecmascript.org/doku.php?id=harmony:proxies + http://wiki.ecmascript.org/doku.php?id=harmony:weak_maps

+2
source

I use the following function for this purpose; its really fast for functions with a reasonable number of parameters, more flexible than the accepted answer, and works for functions with more than 26 parameters.

 function fakeFunctionLength(fn, length) { var fns = [ function () { return fn.apply(this, arguments); }, function (a) { return fn.apply(this, arguments); }, function (a, b) { return fn.apply(this, arguments); }, function (a, b, c) { return fn.apply(this, arguments); }, function (a, b, c, d) { return fn.apply(this, arguments); }, function (a, b, c, d, e) { return fn.apply(this, arguments); }, function (a, b, c, d, e, f) { return fn.apply(this, arguments); } ], argstring; if (length < fns.length) { return fns[length]; } argstring = ''; while (--length) { argstring += ',_' + length; } return new Function('fn', 'return function (_' + argstring + ') {' + 'return fn.apply(this, arguments);' + '};')(fn); } 
+2
source

You only need to go along the eval / Function route if you need to support functions with any number of parameters. If you can set a reasonable upper limit (my example is 5), you can do the following:

 var wrapFunction = function( func, code, where ){ var f; switch ( where ) { case 'after': f = function(t,a,r){ r = func.apply(t,a); code.apply(t,a); return r; } break; case 'around': f = function(t,a){ return code.call(t,func,a); } break; default: case 'before': f = function(t,a){ code.apply(t,a); return func.apply(t,a); } break; } switch ( func.length ) { case 0: return function(){return f(this, arguments);}; break; case 1: return function(a){return f(this, arguments);}; break; case 2: return function(a,b){return f(this, arguments);}; break; case 3: return function(a,b,c){return f(this, arguments);}; break; case 4: return function(a,b,c,d){return f(this, arguments);}; break; case 5: return function(a,b,c,d,e){return f(this, arguments);}; break; default: console.warn('Too many arguments to wrap successfully.'); break; } } 

You can also extend this way of wrapping code by creating various where switches. I implemented before and after because they are most useful for my own project - and around only because it reminds me of lisp. Using this setting, you can also pass the shell ( var f ) to external code, which allows you to develop a system-like plugin for the where keywords, which means that you can easily extend or override wrapFunction support.

Obviously, you can change the way you actually wrap the code as you like, the key really uses a similar technique for 999 and AdrianLang, just without worrying about line building and switching to new Function .

0
source

Source: https://habr.com/ru/post/896744/


All Articles