How to replace arguments.callee to be able to 'use strict' [duplicate] - javascript

Is it possible to see the callee/caller of a function when use strict is enabled?
'use strict';
function jamie (){
console.info(arguments.callee.caller.name);
//this will output the below error
//uncaught TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
};
function jiminyCricket (){
jamie();
}
jiminyCricket ();

For what it's worth, I agree with the comments above. For whatever problem you're trying to solve, there are usually better solutions.
However, just for illustrative purposes, here's one (very ugly) solution:
'use strict'
function jamie (){
var callerName;
try { throw new Error(); }
catch (e) {
var re = /(\w+)#|at (\w+) \(/g, st = e.stack, m;
re.exec(st), m = re.exec(st);
callerName = m[1] || m[2];
}
console.log(callerName);
};
function jiminyCricket (){
jamie();
}
jiminyCricket(); // jiminyCricket
I've only tested this in Chrome, Firefox, and IE11, so your mileage may vary.

Please note that this should not be used in production. This is an ugly solution, which can be helpful for debugging, but if you need something from the caller, pass it as argument or save it into a accessible variable.
The short version of #p.s.w.g answer(without throwing an error, just instantiating one):
let re = /([^(]+)#|at ([^(]+) \(/g;
let aRegexResult = re.exec(new Error().stack);
sCallerName = aRegexResult[1] || aRegexResult[2];
Full Snippet:
'use strict'
function jamie (){
var sCallerName;
{
let re = /([^(]+)#|at ([^(]+) \(/g;
let aRegexResult = re.exec(new Error().stack);
sCallerName = aRegexResult[1] || aRegexResult[2];
}
console.log(sCallerName);
};
function jiminyCricket(){
jamie();
};
jiminyCricket(); // jiminyCricket

It does not worked for me
Here is what I finally do, just in case it helps someone
function callerName() {
try {
throw new Error();
}
catch (e) {
try {
return e.stack.split('at ')[3].split(' ')[0];
} catch (e) {
return '';
}
}
}
function currentFunction(){
let whoCallMe = callerName();
console.log(whoCallMe);
}

You can get a stack trace using:
console.trace()
but this is likely not useful if you need to do something with the caller.
See https://developer.mozilla.org/en-US/docs/Web/API/Console/trace

functionName() {
return new Error().stack.match(/ at (\S+)/g)[1].get(/ at (.+)/);
}
// Get - extract regex
String.prototype.get = function(pattern, defaultValue = "") {
if(pattern.test(this)) {
var match = this.match(pattern);
return match[1] || match[0];
}
return defaultValue; // if nothing is found, the answer is known, so it's not null
}

Related

Is there a polyfill for es6 arrow function?

Is there a polyfill for es6 arrow function?
the following code throws syntax error exception in IE, is there a polyfill to make IE support arrow functions?
var myFunc = ()=>{
alert('es6');
}
myFunc();
Note: I don't want to use any transpiler.
Thanks in advance
A polyfill can add or fix missing built-in classes, functions, objects... but it cannot modify a compiler's accepted syntax.
There is no polyfill for arrow functions. It is a syntax error to write the code you have unless you use a transpiler.
Features that add new syntax can not be polyfilled.
I can only think of babel-standalone, which you can think of as a JIT compiler/transpiler (if that is OK with you).
I'm pretty green with JS so I have a feeling that this may not qualify as a polyfill... but it does seem to be a 'duct tape' stopgap though. I found a fiddle made by Luis Perez that gives this functionality. I'm still working to better understand arrow functions but it at least does work with one of the MDN arrow function examples. Here's the snippet that after playing with I managed to understand (better at least) lol. I hope it is useful to someone.
var str = [
'Hydrogen',
'Helium',
'Lithium',
'Beryllium'
];
var g_arrowCache = Object.create(null);
function arrow(expression) {
function cache(cache, key, getValueFunc) {
var value = cache[key];
if(value === undefined) {
value = getValueFunc(key);
cache[key] = value;
}
return value;
}
function arrowImpl(expression) {
// This function is a polyfill for proposed "arrow functions" in JavaScript.
// Example: str.map(_$("str => str.length"))
if (expression.search(/\bthis\b/) != -1) throw "'this' not supported";
var indexOfArrow = expression.indexOf("=>");
if(indexOfArrow == -1) throw "Expressio is missing the arrow operator =>";
var parametersString = expression.substring(0, indexOfArrow);
parametersString = parametersString.replace("(", "").replace(")", "");
var parameters = parametersString.split(",");
parameters.map(function(o) { return o.trim(); });
var functionBody = expression.substring(indexOfArrow + 2);
if(expression.indexOf("{") != -1) throw "Use of curly brackets for multiple statements not supported or recommended.";
if(expression.indexOf("}") != -1) throw "Use of curly brackets for multiple statements not supported or recommended.";
functionBody = "return " + functionBody.trim() + ";";
var args = parameters.slice(0);
args.push(functionBody);
var func = Function.constructor.apply(null, args);
return func;
}
return cache(g_arrowCache, expression, arrowImpl);
}
var _$ = arrow;
console.log(str.map(_$("str => str.length")));

How do you find out the caller function in JavaScript when use strict is enabled?

Is it possible to see the callee/caller of a function when use strict is enabled?
'use strict';
function jamie (){
console.info(arguments.callee.caller.name);
//this will output the below error
//uncaught TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
};
function jiminyCricket (){
jamie();
}
jiminyCricket ();
For what it's worth, I agree with the comments above. For whatever problem you're trying to solve, there are usually better solutions.
However, just for illustrative purposes, here's one (very ugly) solution:
'use strict'
function jamie (){
var callerName;
try { throw new Error(); }
catch (e) {
var re = /(\w+)#|at (\w+) \(/g, st = e.stack, m;
re.exec(st), m = re.exec(st);
callerName = m[1] || m[2];
}
console.log(callerName);
};
function jiminyCricket (){
jamie();
}
jiminyCricket(); // jiminyCricket
I've only tested this in Chrome, Firefox, and IE11, so your mileage may vary.
Please note that this should not be used in production. This is an ugly solution, which can be helpful for debugging, but if you need something from the caller, pass it as argument or save it into a accessible variable.
The short version of #p.s.w.g answer(without throwing an error, just instantiating one):
let re = /([^(]+)#|at ([^(]+) \(/g;
let aRegexResult = re.exec(new Error().stack);
sCallerName = aRegexResult[1] || aRegexResult[2];
Full Snippet:
'use strict'
function jamie (){
var sCallerName;
{
let re = /([^(]+)#|at ([^(]+) \(/g;
let aRegexResult = re.exec(new Error().stack);
sCallerName = aRegexResult[1] || aRegexResult[2];
}
console.log(sCallerName);
};
function jiminyCricket(){
jamie();
};
jiminyCricket(); // jiminyCricket
It does not worked for me
Here is what I finally do, just in case it helps someone
function callerName() {
try {
throw new Error();
}
catch (e) {
try {
return e.stack.split('at ')[3].split(' ')[0];
} catch (e) {
return '';
}
}
}
function currentFunction(){
let whoCallMe = callerName();
console.log(whoCallMe);
}
You can get a stack trace using:
console.trace()
but this is likely not useful if you need to do something with the caller.
See https://developer.mozilla.org/en-US/docs/Web/API/Console/trace
functionName() {
return new Error().stack.match(/ at (\S+)/g)[1].get(/ at (.+)/);
}
// Get - extract regex
String.prototype.get = function(pattern, defaultValue = "") {
if(pattern.test(this)) {
var match = this.match(pattern);
return match[1] || match[0];
}
return defaultValue; // if nothing is found, the answer is known, so it's not null
}

In Javascript using try / catch, besides the error message how can I catch function name and all arguments names/values passed?

I'd like to create a generic debugging routine using try / catch, within the catch segment I'd like a piece of code which will log the function name and all params passed (name => value).
Is this possible?
try{
// code
} catch(e) {
var function_name = ''; // how can I get this?
var function_params = ''; // how can I get this?
var errorText = 'UNEXPECTED ERROR: \n\n Error Details: '+e.toString();
errorText = errorText+' Called:'+function_name+'('+function_params+')';
}
In Chrome and Firefox you can use e.stack but no such luck in Internet Explorer.
The stack property is an array that you can loop over. It might differ a bit from browser to browser but it shouldn't be too hard to make a readable stacktrace.
You currently can't catch the arguments in all browsers.
In Chrome you might need to use:
Error.prepareStackTrace = function (error, stack) {
return stack;
}; // augments Chrome's Error.stack property with context data
Try using arguments.callee for the function name
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/arguments/callee
This isn't an exact answer because I don't give the function Name but this returns the entire function body...
Working sample: http://jsfiddle.net/sS6sY/
Object.prototype.getArgs = function(){
//returns the arguments passed to a function.
var els = [];
for(i=0; i< this.arguments.length; i++){
els.push(this.arguments[i]);
}
return els.join(',')
}
Object.prototype.getMethod = function(){
//returns the function as a string.
return this.constructor;
}
var Foo = function(){
this.arguments = arguments;
try{
throw {
name: 'Oops',
message: 'Didn\'t mean to make a Foo!'
}
}
catch(e){
console.log(this.getArgs());
console.log(this.getMethod());
}
}

Can I extend the console object (for rerouting the logging) in javascript?

Is it possible to extend the console object?
I tried something like:
Console.prototype.log = function(msg){
Console.prototype.log.call(msg);
alert(msg);
}
But this didn't work.
I want to add additional logging to the console object via a framework like log4javascript and still use the standard console object (in cases where log4javascript is not available) in my code.
Thanks in advance!
Try following:
(function() {
var exLog = console.log;
console.log = function(msg) {
exLog.apply(this, arguments);
alert(msg);
}
})()
You Can Also add log Time in This Way :
added Momentjs or use New Date() instead of moment.
var oldConsole = console.log;
console.log = function(){
var timestamp = "[" + moment().format("YYYY-MM-DD HH:mm:ss:SSS") + "] ";
Array.prototype.unshift.call(arguments, timestamp);
oldConsole.apply(this, arguments);
};
It's really the same solution some others have given, but I believe this is the most elegant and least hacky way to accomplish this. The spread syntax (...args) makes sure not a single argument is lost.
var _console={...console}
console.log = function(...args) {
var msg = {...args}[0];
//YOUR_CODE
_console.log(...args);
}
For ECMAScript 2015 and later
You can use the newer Proxy feature from the ECMAScript 2015 standard to "hijack" the global console.log.
Source-Code
'use strict';
class Mocker {
static mockConsoleLog() {
Mocker.oldGlobalConsole = window.console;
window.console = new Proxy(window.console, {
get(target, property) {
if (property === 'log') {
return function(...parameters) {
Mocker.consoleLogReturnValue = parameters.join(' ');
}
}
return target[property];
}
});
}
static unmockConsoleLog() {
window.console = Mocker.oldGlobalConsole;
}
}
Mocker.mockConsoleLog();
console.log('hello'); // nothing happens here
Mocker.unmockConsoleLog();
if (Mocker.consoleLogReturnValue === 'hello') {
console.log('Hello world!'); // Hello world!
alert(Mocker.consoleLogReturnValue);
// anything you want to do with the console log return value here...
}
Online Demo
Repl.it.
Node.js users...
... I do not forget you. You can take this source-code and replace window.console by gloabl.console to properly reference the console object (and of course, get rid of the alert call). In fact, I wrote this code initially and tested it on Node.js.
// console aliases and verbose logger - console doesnt prototype
var c = console;
c.l = c.log,
c.e = c.error,
c.v = c.verbose = function() {
if (!myclass || !myclass.verbose) // verbose switch
return;
var args = Array.prototype.slice.call(arguments); // toArray
args.unshift('Verbose:');
c.l.apply(this, args); // log
};
// you can then do
var myclass = new myClass();
myclass.prototype.verbose = false;
// generally these calls would be inside your class
c.v('1 This will NOT log as verbose == false');
c.l('2 This will log');
myclass.verbose = true;
c.v('3 This will log');
I noted that the above use of Array.prototype.unshift.call by nitesh is a better way to add the 'Verbose:' tag.
You can override the default behavior of the console.log function using the below approach, the below example demonstrates to log the line number using the overridden function.
let line = 0;
const log = console.log;
console.log = (...data) => log(`${++line} ===>`, ...data)
console.log(11, 1, 2)
console.log(11, 1, 'some')

Intercept calls to console.log in Chrome

I have a script that I can't change that makes a lot of console.log calls. I want to add another layer and respond if the calls contain certain strings. This works in Firefox, but throws an "Illegal invocation" error in Chrome on the 4th line:
var oldConsole = {};
oldConsole.log = console.log;
console.log = function (arg) {
oldConsole.log('MY CONSOLE!!');
oldConsole.log(arg);
}
Any ideas how to get around that? I also tried cloning the console...
You need to call console.log in the context of console for chrome:
(function () {
var log = console.log;
console.log = function () {
log.call(this, 'My Console!!!');
log.apply(this, Array.prototype.slice.call(arguments));
};
}());
Modern language features can significantly simplify this snippet:
{
const log = console.log.bind(console)
console.log = (...args) => {
log('My Console!!!')
log(...args)
}
}
I know it's an old post but it can be useful anyway as others solution are not compatible with older browsers.
You can redefine the behavior of each function of the console (and for all browsers) like this:
// define a new console
var console = (function(oldCons){
return {
log: function(text){
oldCons.log(text);
// Your code
},
info: function (text) {
oldCons.info(text);
// Your code
},
warn: function (text) {
oldCons.warn(text);
// Your code
},
error: function (text) {
oldCons.error(text);
// Your code
}
};
}(window.console));
//Then redefine the old console
window.console = console;
You can also use the same logic, but call it off the console object so the context is the same.
if(window.console){
console.yo = console.log;
console.log = function(str){
console.yo('MY CONSOLE!!');
console.yo(str);
}
}
With ES6 new spread operator you can write it like this
(function () {
var log = console.log;
console.log = function () {
log.call(this, 'My Console!!!', ...arguments);
};
}());
Can be simply:
console.log = (m) => terminal.innerHTML = JSON.stringify(m)
#terminal {background: black; color:chartreuse}
$ > <span id="terminal"></span>
<hr>
<button onclick="console.log('Hello world!!')">3V3L</button>
<button onclick="console.log(document)">3V3L</button>
<button onclick="console.log(Math.PI)">3V3L</button>
To fully intercept the console, we can override every methods :
const bindConsole=function(onMessage){
Object.keys(console)
.filter(type=>typeof(console[type])==='function')// *1
.forEach(type=>{
let _old=console[type];
console[type] = function (...args) {
_old.apply(console,args);
onMessage(type,args);// *2
};
});
};
For old browsers :
var bindOldConsole=function(onMessage){
for(var k in console){// *1
if(typeof(console[k])=='function')(function(type){
var _old=console[type];
console[type] = function () {
_old.apply(console,arguments);
onMessage(type,arguments);
};
})(k);
}
};
*1 Looks like console has only methods but better be sure.
*2 You may block cyclic calls to console from onMessage by replacing this line with :
if(!isCyclic())onMessage(type,args);
// es6. Not sure concerning old browsers :(
const isCyclic=function (){
let erst=(new Error()).stack.split('\n');
return erst.includes(erst[1],2);
};
Since I cannot comment (yet) on #ludovic-feltz answer, here is his answer corrected to allow string interpolation in the console :
// define a new console
var console = (function(oldCons){
return {
log: function(...text){
oldCons.log(...text);
// Your code
},
info: function (...text) {
oldCons.info(...text);
// Your code
},
warn: function (...text) {
oldCons.warn(...text);
// Your code
},
error: function (...text) {
oldCons.error(...text);
// Your code
}
};
}(window.console));
//Then redefine the old console
window.console = console;

Categories