javascript eval context not contains parameters in chrome - javascript

I encountered a strange error today.
When I invoked the below function in chrome, I got:
var t = function(i){console.log(i);console.log(eval("i"));};
t("123");
//the result in chrome
123
undefined
But the above code invoked in firefox, it came out just as I thought: the second log statement was the same as the first.
In my opinion,the eval statement will use the context of the anonymous function as its runtime context, which contains the parameters.
I didn't find any material involve eval context and parameter.
Can anyone tell me why ?
Actually,I used tempo.js to render html and came out a similar question as I listed above.
the source code is here:
_replaceVariables: function (renderer, _tempo, i, str) {
return str.replace(this.varRegex, function (match, variable, args) {
try {
...
if (variable === '.') {
val = eval('i');
} else if (utils.typeOf(i) === 'array') {
val = eval('i' + variable);
} else {
val = eval('i.' + variable);
}
.....
} catch (err) {
console.log(err);
}
return '';
});
},
When run in chrome,the eval statement got error like this:
TypeError: Cannot convert null to object
I can't figure out why this happened, so I tried the code at the beginning.

The Chrome console implementation of the console functions involves some asynchronous behavior that causes weird issues like what you've discovered.
That said, in your particular case my Chrome logs "123" twice. I find it generally to be a really good idea to augment debugging output with some unique identifying text:
var t = function(i){console.log("param is " + i);console.log("eval result is " + eval("i"));};
The Chrome console output collapses repeated lines and prefixes them with a little circled counter:
(source: gutfullofbeer.net)
That little "2" before "123" means it was logged twice.

Related

pass argument to function, parameter doesn't become variable

Trying to learn basic javascript, but ran into some issues.
Is it true that the following script stores the string to the parameter, turning it to a variable:
function funcOne(paraOne) {
document.write(paraOne);
}
funcOne("A Message");
Am I understanding it correctly, that this is equivalent to var paraOne = "A Message"; ?
Because when I try using this variable elsewhere, an error message just shows in console saying:
ReferenceError: Can't find variable: paraOne
Wondering what I am missing here.
The scope of a parameter is limited to the function that defines it. So, for example, this would be perfectly legal:
function funcOne(paraOne) {
document.write(paraOne);
console.log(paraOne);
}
funcOne("A Message");
While this would cause a RefernceError:
function funcOne(paraOne) {
document.write(paraOne);
}
funcOne("A Message");
console.log(paraOne); // Error here!

How can I hide a function call from transpiled code using source maps?

Let's say I have a language that looks like
print "Hello World"
which transpiles to
var $__Helpers = {
print: function(s) {
if (typeof s != 'string')
throw new TypeError('String expected');
console.log(s);
}
};
$__Helpers.print("Hello World");
If a user of this language does
print 5
a TypeError will be thrown by $__Helpers.print saying "String expected". I want the developer tools to show the print 5 line as the originating call for this error. I know how to get my source map to show a call stack that looks like
transpiled_script.js:2
original_script.os:1
where transpiled_script.js:2 is the script and line number for the call to the $__Helpers.print function and original_script.os:1 is the script and line number for the call to print 5. I want to have the dev tools just ignore the top call to transpiled_script.js (which is only an implementation detail of my transpiler) and only show the call from the original script (which is the part that they should debug in their own script).
I clearly can't simply map transpiled_script.js:2 to original_script.os:1 because there could be multiple calls to print inside original_script.os, so it's not a 1 to 1 relationship.
Is there any way to do this?
(I am using escodegen to generate my source and my source map (escodegen uses the Node mozilla/source-map module), so having a way to tell escodegen or mozilla/source-map to do this would be ideal, but I can override escodegen's output if that's not possible.)
You may split the trace and print the required lines of it
var $__Helpers = {
print: function(s) {
if (typeof s != 'string'){
var err = new TypeError('String expected');
var trace = err.stack.split('\n')
console.error(trace[0]); // TypeError: string expected
console.error(trace[2]); // the line who called the function, probably
//original_script.os:1, or whatever line number the call was from
//quit the script
}
console.log(s);
} };
EDIT: a better solution, is to replace the trace of the error, than throw it, the code now look like this:
var $__Helpers = {
print: function(s) {
if (typeof s != 'string'){
var err = new TypeError('String expected: Got'+s);
err.stack = err.stack.replace(/\n.*transpiled_script\.js.*?\n/g,"\n");
throw err;
}
console.log(s);
} };
this will work for errors in nested called too.

Website with JS doesn't work in IE9 until the Developer Tools is activated

I'm developing a complex website that heavily leverages jQuery and a number of scripts. On load of the site, none of my scripting is working (though I can confirm that other scripts are functioning fine). I wouldn't be posting such a lame question here on SE except for one thing:
The instant I hit F12 to turn on developer tools so I can debug my issue, everything instantly works perfectly!
Worse, if I shut down the browser, start it up, turn on Dev Tools first and visit the site, everything works as expected.
So I can't even debug the darned problem because Dev Tools fixes it! What could Dev Tools be doing that makes things work? Does it change the UA (I do some jQuery.browser detection)? Does it do something to doctype?
EDIT
All my console logging is wrapped in the following wrapper utility function:
function log(msg){
if (console){
console.log(msg);
}
}
Any thoughts or suggestions I could try would be welcome. I'll post here if I find a solution.
I appreciate I'm pretty late to the party here, but I've got a solution for IE9 that's a little different.
(function() {
var temp_log = [];
function log() {
if (console && console.log) {
for (var i = 0; i < temp_log.length; i++) {
console.log.call(window, temp_log[i]);
}
console.log.call(window, arguments);
} else {
temp_log.push(arguments);
}
}
})();
Basically instead of console.log you use log. If console.log exists then it works as normal, otherwise it stores log entries in an array and outputs them on the next log where the console is available.
It would be nice if it pushed the data as soon as the console is available, but this is less expensive than setting up a custom setInterval listener.
Updated function (1 October 2012)
I've updated this script for my own use and thought I'd share it. It has a few worthy improvements:
use console.log() like normal, i.e. no longer need to use non-standard log()
supports multiple arguments, e.g. console.log('foo', 'bar')
you can also use console.error, console.warn and console.info (though outputs them as console.log)
script checks for native console every 1000ms and outputs the buffer when found
I think with these improvements, this has become a pretty solid shim for IE9. Check out the GitHub repo here.
if (!window.console) (function() {
var __console, Console;
Console = function() {
var check = setInterval(function() {
var f;
if (window.console && console.log && !console.__buffer) {
clearInterval(check);
f = (Function.prototype.bind) ? Function.prototype.bind.call(console.log, console) : console.log;
for (var i = 0; i < __console.__buffer.length; i++) f.apply(console, __console.__buffer[i]);
}
}, 1000);
function log() {
this.__buffer.push(arguments);
}
this.log = log;
this.error = log;
this.warn = log;
this.info = log;
this.__buffer = [];
};
__console = window.console = new Console();
})();
You have console calls, in IE these will fail if the dev tools are not open. A simple fix is to wrap any console calls in a function like:
function log(msg) {
if(console)
console.log(msg);
}
I have hacked it the following way
<script type="text/javascript">
(function () {
if (typeof console == "undefined") {
console = {
log : function () {}
}
}
})();
</script>
And this is the first script element in the .
Most of the other solutions should work great, but here's a short one liner if you don't care about catching log messages if the console is not available.
// Stub hack to prevent errors in IE
console = window.console || { log: function() {} };
This lets you still use the native console.log function directly still instead of wrapping it with anything or having a conditional each time.
I find it much more convenient to simply use console && console.log('foo', 'bar', 'baz') rather than use a wrapper function.
The code you provided:
function logError(msg){
if (console) {
console.log(msg);
} else {
throw new Error(msg);
}
}
Will produce an error for IE when dev tools are closed because console will be undefined.
The console.log wrapper that I used was not sufficient to detect the console in IE9. Here's the wrapper that works from a related question on SE:
function logError(msg){
try {
console.log(msg);
} catch (error) {
throw new Error(msg);
}
}
function log(msg){
try {
console.log(msg);
} catch (error) { }
}
A proper test for the availability of the console object would be:
if (typeof console === "undefined" || typeof console.log === "undefined")
If you have multiple parallel script files, maybe the files are being loaded/executed in a different order with developer tools on/off.
I have run into this issue many times. Basically with variables we do this to check if they are valid
var somevar;
if (somevar)
//do code
this works because somevar will resolve to undefined. But if your checking a window property for example. window.console.
if (console) <---- this throws an exception
You cannot do the same check. The browser treats it differently. Basically only doing this
if (window.console) <---- will NOT throw an exception if undefined
//some code
this will work the same as the first example. So you need to change your code to
function log(msg){
if (window.console){
console.log(msg);
}
}

Catching "NullPointerExceptions" in JavaScript

I'm writing quite a bit of code in Prototype.js which returns null if a DOM-id wasn't found.
$("someId").show();
If someId doesn't exist, a method will be called on null, which halts the entire program, in effect disabling all JS effects after the error. I could just check for null before executing such a statement, but this is getting tiring.
I would like to catch an exception but I'm not sure which one its is. MDC lists the following ECMA Script error types, but on first glance none of them seem to be what I want:
* Error
* EvalError
* RangeError
* ReferenceError
* SyntaxError
* TypeError
* URIError
* DOMException
* EventException
* RangeException
Also, do browsers have a unified way of dealing with a method call on null?
I don't believe there's unity to be found. Chrome throws a TypeError, but IE throws an Error, so you would probably have to catch everything and make severe assumptions. Better to check for null first.
var element = $('someId');
if (element) {
element.show();
// whatever else...
}
If element.show() is the only thing you need it for, then it can obviously be written a lot shorter, but in most cases that would be appropriate.
The correct way to handle this is to check for null before doing something with an object. There are several shorthand ways to do this, the shortest is (as Alex K) wrote
$("someId") && $("someId").show();
but this seems to me to be harder to read.
To answer your question directly you can do
try { $('someId').show(); } catch (e) {}
but this seems amateurish. You should program explicitly because later on someone else won't know why you wrote that odd code. The first example is slightly opaque but at least contains the null test first, and doesn't hide errors in the show() method.
Incidentally, if you were using JQuery instead of Prototype, this code would work without error even if there is no object with id 'someId':
$('#someId').show()
That's because the $() function in JQuery returns a collection which may be empty but is never null.
If your going to chain .show() on $("someId") then check its result first.
if ($("someId"))
$("someId").show();
or
$("someId") && $("someId").show();
or
if (someVar = $("someId"))
someVar.show();
If for some reason you really need to identify them you could wrap $() and throw a custom exception:
function NullReferenceException(id) {this.id = id}
function $my(id) {
var el = $(id);
if (!el)
throw new NullReferenceException(id);
return el
}
try {
$my("iDontExistId").show();
} catch (e) {
if (e instanceof NullReferenceException)
alert(e.id + " doesn't exist");
}
Just ignore which exception it is...
try
{
null.hey()
}
catch(e)
{
//handle it here
}

Javascript function in an object/hash

I can call a function directly (I'll use alert as an example) like so
alert("Hello World!"); // pops up an alert window
However, when I put a function in an object, calling it no longer works:
d = {func: alert};
d.func("Hello World!"); // doesn't do anything
d["func"]("Hello World!"); // also doesn't do anything
I figured maybe I needed to explicitly pass in a blank this argument, so I tried
d.func(null, "Hello World!") // still nothing
but to no avail. Interestingly, this does work
d.func.apply(null, ["Hello World!"]); // success!
but that's so gratuitously verbose it makes my teeth hurt (to quote JWZ). Is there a more concise, less ugly way?
Functions in JavaScript are passed by value. The alert() function is natively implemented, meaning it has no JavaScript value. Depending on your browser, the meaninfulness (forgive me for that) of that native wrapper varies. Your code actually works in Google Chrome, but won't work in Firefox, and off the top of my head I'm going to say it won't work in Internet Explorer, either, which is generally not friendly about modifying native objects and functions. You could use the following instead:
d = {
func: function (message) {
alert(message);
}
};
If you try this:
function test(x) {
alert(x);
}
var x = {func: test}
x.func('Hi!');
It works as you expect. When I try doing this to alert directly Firebug gives me the following error message:
[Exception... "Cannot modify properties of a WrappedNative"
nsresult: "0x80570034 (NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN)"
location: "JS frame :: http://stackoverflow.com/questions/859466/javascript-function-in-an-object-hash :: anonymous :: line 72" data: no]
So I am guessing it's a security thing or something to do with it being a native function.
I've always done it like this:
var d = Object;
d.hello = function(msg) {
alert(msg)
};
d.hello('hello');
Of course, you can also use PrototypeJS to get all object oriented:
var Message = Class.create( {
display: function(msg) {
alert(msg);
}
});
var msg = new Message();
msg.display('hello');

Categories

Resources