omitting or amending/extending built-in javascript methods for compatibility - javascript

I was reading about console.log() somewhere on Stackoverflow earlier today, and I remember reading that it causes IE to bug out. The user had said that all instances of it should be removed from the code before deployment or a javascript library could be used to omit it in IE.
My question is regarding this second option, how would one write such a library? Would it just be something that's run onload that affects how the js is parsed from then forward? Like something that says if browser == IE, omit all instances of console.log()... or would it be an extension to the console.log() method that makes it self-aware of what browser it's being called in? I'm somewhat familiar with extending jQuery functions but have never seen this done with raw js. I'm interested in this because I feel like it would be a good way to achieve browser cross-compatiblity just in general, this isn't about console.log() specifically.

One approach is to just make sure that console.log() is always defined so you can always use it without worry. You can add this code to make sure that it is always defined.
This code will make it so that in IE (or in any other browser that doesn't have it), console.log() will be defined and safely do nothing. When it does already exist, this code will do nothing leaving the previous definition of console.log() in place and untouched.
if (!window.console) {
window.console = {};
}
if (!console.log) {
console.log = function() {};
}

There are a few different ways to go about testing for global properties that may or may not exist. You should try to be as defensive as possible, e.g.
var global = this;
if (!('console' in global)) {
global.console = {};
}
if (!('log' in global.console)) {
global.console.log = /* whatever */;
}
The in tests could perhaps use typeof instead. The idea is to not use an identifier that may not exist. Perhaps a little saner is:
// If console exists, declaring it will not hurt
// If it didn't exist, it does now
var console;
if (typeof console == 'undefined') {
console = {};
}
if (typeof console.log == 'undefined') {
console.log = /* whatever */;
}

Related

How to make JavaScript code more bullet-proof?

I come from a compiled-language background (C/C++/Objective-C) and am currently writing a full-fledged application in JavaScript. (TypeScript actually, but my question is the same for both.)
The problem I'm running into is that if a single error occurs, the entire flow of execution halts. For instance, if I have:
myFunction()
{
doSomethingA();
doSomethingB();
doSomethingC();
}
then if doSomethingA() has something like this:
var myValue = window.myData.myValue;
but "myData" doesn't exist on "window" at the time, then all code STOPS EXECUTING... doSomethingB() and doSomethingC() do not execute, and a console error is logged. That might be fine for simple web pages, but I'm creating an application, and it needs to not 'stop working' inexplicably.
Granted, I can use try/catch to be 'aware' of this error, but that STILL doesn't solve my problem: I would like to write the code in a way such that doSomethingB() and doSomethingC() continue to execute, even if a problem arises.
However, this is a huge over-simplification of my code. Imagine there are MANY of these functions. It would be impractical to surround each with its own separate try/catch block. Even if I did, I need the rest of a given function to continue to execute even if something in the first part fails.
Of course, I can 'protect' my variables by using:
if ( typeof window.myData != "undefined")
or
if (window.myData.hasOwnProperty("myValue")
but that becomes very messy if you have several levels to check, such as when accessing:
var myValue = window.myData.something.anotherLevel.somethingElse.value;
Now I have to check if myData, something, anotherLevel, and somethingElse are all valid before accessing this value 'safely'. This results in very ugly code.
Is there a better way to make my code more 'bullet-proof'? If a property is unexpectedly missing, I need code to continue executing after the problem statement. Is that possible without 'protecting' every single statement that accesses data that has even a tiny chance of being undefined?
That's just how JavaScript is. It's not a strongly-typed language. You have the right approach checking for falsy/undefined. You could look into a utility library, something like Lodash's isUndefined() method to help ease the pain a bit. They have a lot of helper methods like that such as checking for object types, arrays, object literals, etc.
https://lodash.com/docs#isUndefined
Normally You have to have some set of "nested if hells" like:
if(typeof window.myData !== 'undefined' && typeof window.myData.something !== 'undefined' && typeof window.myData.something.anotherlevel !== 'undefined' ...) {
}
To ease developers pain there is utilities for example lodash
it's very helpful and shortens Your code.
it has _.get(object, path) function that takes element by path and if not found it just returns undefined.
<script src="https://raw.githubusercontent.com/lodash/lodash/4.13.1/dist/lodash.js"></script>
<script>
window.myData = {
something: {
anotherlevel: {
value: 'YOU GOT ME! (:'
},
zerolevel: {
value: 0
}
}
};
function getDataByPath(path) {
var result = _.get(window, path);
alert(result);
}
</script>
<button onclick="getDataByPath('myData.something.anotherlevel.value')">
anotherlevel.value
</button>
<br/><br/>
<button onclick="getDataByPath('myData.something.zerolevel.value')">
zerolevel.value
</button>
<br/><br/>
<button onclick="getDataByPath('myData.something.unexistentlevel.value')">
unexistentlevel.value
</button>
Your best bet would be to check, if the variables you use are undefined or not with
typeof window.yourVariable === "undefined"
It's however not very safe to rely on variables in the window object, use scopes and closures.
I'm accepting Prefix's answer, since I don't like accepting my own answers. And, it's useful to hear about the Lodash helper, and know that the "nested if hells" I have are a normal (sad to hear it!) part of JavaScript.
However, the 'solution' I've gone with that has ended up making me feel very happy, is to actually reject a premise in my original question: that try/catch is not a good solution.
Turns out try/catch, combined with attempting to make sure variables are undefined, is a good way to catch those cases that I've missed.
I've actually ended up architecting it as follows. Let's say I have lots of functions I want to happen as a result of doStuff(), and I've put them into window.callbacks. I can then do (untested pseudocode, but it gives you the gist of it):
doStuff() {
for (var myFunc in window.callbacks) {
if (window.callbacks.hasOwnProperty(myFunc) ) {
try {
window.callbacks[myFunc].call();
}
catch(err) {
console.log("%cCaught error in " + myFunc + ": " + (err.stack || err), 'background: #222; color: #bada55');
}
}
}
}
This logs a unique console log message, complete with stack trace, showing what triggered the error, and yet CODE EXECUTION CONTINUES. Subsequent callbacks will still be called. The only code that doesn't execute is the remainder of the callback that caused the error.
Given my compiled-language background, this gives me warm fuzzy feelings... it means that at worst, a tiny function will fail to execute, not an entire code path following an error.

How can I detect use of invalid properties in javascript?

I'm learning Javascript and I wrote the following code:
if (mystring.len > 0) {
// do stuff
}
I accidentally used .len instead of .length. To my surprise, no error was raised. mystring.len returned undefined and this made the comparison fail but the code kept right on running. I would prefer an actual error to be raised so I can fix the code. Adding "use strict" didn't help, nor did jslint.
I know there are ways to actively check whether or not a property exists, but that's not what I'm looking for. I want Javascript to tell me when I've made a typo in a property name.
Is there a way to cause Javascript to give an error in this case?
Nope - that is how JavaScript works and it's incredibly useful. Who is to say that checking len is something that needs fixing? Consider:
if(mystring.len === undefined) {
mystring.len = "Test";
}
The best you can do is to simply check that the thing is defined before using it:
if(mystring.len !== undefined) {
}
I appreciate the strangeness, and how it doesn't feel robust (having originally come from a C# background) but there isn't a lot you can do unfortunately. The fact that JavaScript is case sensitive makes this even more frustrating. You will learn to live with it though!
If you really really wanted to run some static analysis then you could considering creating a transpiler (e.g. Babel) extension to run this sort of analysis - but it would get really difficult if you ever expected something to be undefined which I find is common place.
edit
Here's a real example that I'd use where undefined is useful. I'm working with a library that needs to move stuff from one location to another. It can't do that unless the original location has been specified, so I might write something like the following, initializing values if I don't have them for some reason:
function update(node) {
if(node.x === undefined) { node.x = 0; }
node.y = node.y || 0; // This is the shorthand way I'd actually write it
// Do some other stuff
};
"use strict" (in my experience) is used so that variables that aren't explicitly declared/instantiated that are then referenced will throw errors (else, JS would just make a new var on the fly). So that wouldn't help here.
This sounds like an error that would typically be picked up by a compiler in other languages, but since JS is interpreted, you won't have that kind of explicit error checking unless you're in a beefy IDE. Are you using a text editor or something to write JS?
Thats not the way JavaScript considers your above code. Every variable in JS is an object. So, when you do mystring.len, its actually trying to access the len property of mystring obj and when it doesn't find that property, it will return undefined - which is how it should be. Thats why you will not be able to find any error using JSLint.
Just to give you an example -
var myObj = {name: 'Hello', id: 1};
console.log(myObj.name); // Returns 'Hello'
console.log(myObj.text); // 'undefined'
In order to prevent such code from giving you any errors, you can easily use the hasOwnProperty() method like follows-
if(myObj.hasOwnProperty('text')) doSomething();
Since myObj doesn't have any property text, the doSomething() function will never be called.
This is the behaviour of JavaScript as mentioned by many/all answers. However there is an option to prevent new properties you might want to try:
Object.seal https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal
The simple answer is JavaScript is not ment to be typesafe...You shouldn't check it, but if you still want to check you can do it by:
if ('len' in mystring){
}
You should look into Typescript if you ask this question...

Securing JavaScript eval function

We want to give our users the ability to execute self created JavaScript code within our application. For this we need to use eval to evaluate the code. To reduce all security concerns to a minimum (if not zero), our idea is to prevent the usage of any window or document function within the code. So no XMLHttpRequest or anything similar.
This is the code:
function secure_eval(s) {
var ret;
(function(){
var copyXMLHttpRequest = XMLHttpRequest; // save orginal function in copy
XMLHttpRequest = undefined; // make orignal function unavailable
(function() {
var copyXMLHttpRequest; // prevent access to copy
try {
ret = eval(s)
} catch(e) {
console.log("syntax error or illegal function used");
}
}())
XMLHttpRequest = copyXMLHttpRequest; // restore original function
}())
return ret;
}
This works as follows:
secure_eval('new XMLHttpRequest()'); // ==> "illegal function used"
Now I have several questions:
Is this pattern the right way to secure eval?
What functions of window and document are the ones which are considered harmful?
To ship around question 2. I tried to mask all (native) functions of window But I am not able to enumerate them:
This does not list XMLHttpRequest for instance:
for( var x in window) {
if( window[x] instanceof Function) {
console.log(x);
}
}
Is there a way to get a list of all native functions of window and document?
EDIT:
One of my ideas is to perform the eval within a Worker and prevent access to XMLHttpRequest and document.createElement (see my solution above). This would have (to my mind) the following consequences:
no access to the original document
no access to the original window
no chance to communicate with external resources (no ajax, no scripts)
Do you see any drawback or leaks here?
EDIT2:
In the meantime I have found this question which answer solves many of my problems plus a couple of things I did not even think about (i.e. browser dead lock with "while(true){}".
Your code does not actually prevent the use of XMLHttpRequest. I can instantiate an XMLHttpRequest object with these methods:
secure_eval("secure_eval = eval"); // Yep, this completely overwrites secure_eval.
secure_eval("XMLHttpRequest()");
Or:
secure_eval("new (window.open().XMLHttpRequest)()")
Or:
secure_eval("new (document.getElementById('frame').contentWindow.XMLHttpRequest)()")
This 3rd method relies on the presence of an iframe in the HTML of the page, which someone could add by manipulating the DOM in their browser. I do such manipulations every now and then with Greasemonkey to remove annoyances or fix broken GUIs.
This took me about 5 minutes to figure out, and I am not by any means a security guru. And these are only the holes I was able to find quickly, there are probably others, that I don't know about. The lesson here is that it is really really really hard to secure code through eval.
Using A Worker
Ok, so using a Worker to run the code is going to take care of the 2nd and 3rd cases above because there's no window accessible in a Worker. And... hmm.. the 1st case can be handled by shadowing secure_eval inside its scope. End of story? If only...
If I put secure_eval inside a web worker and run the following code, I can reacquire XMLHttpRequest:
secure_eval("var old_log = console.log; console.log = function () { foo = XMLHttpRequest; old_log.apply(this, arguments); };");
console.log("blah");
console.log(secure_eval("foo"));
The principle is to override a function that is used outside secure_eval to capture XMLHttpRequest by assigning it to a variable that will be deliberately leaked to the global space of the worker, wait until that function is used by the worker outside secure_eval, and then grab the saved value. The first console.log above simulates the use of the tampered function outside secure_eval and the 2nd console.log shows that the value was captured. I've used console.log because why not? But really any function in the global space could be modified like this.
Actually, why wait until the worker may use some function we tampered with? Here's another, better, quicker way to do access XMLHttpRequest:
secure_eval("setTimeout(function () { console.log(XMLHttpRequest);}, 0);");
Even in a worker (with a pristine console.log), this will output the actual value of XMLHttpRequest to the console. I'll also note that the value of this inside the function passed to setTimeout is the global scope object (i.e. window when not in a worker, or self in a worker), unaffected by any variable shadowing.
What About the Other Question Mentioned in This Question?
What about the solution here? Much much better but there is still a hole when run in Chrome 38:
makeWorkerExecuteSomeCode('event.target.XMLHttpRequest',
function (answer) { console.log( answer ); });
This will show:
function XMLHttpRequest() { [native code] }
Again, I'm no security guru or cracker bent on causing trouble. There are probably still more ways I'm not thinking about.
I'll try and answer your questions in order here.
Is this pattern the right way to secure eval?
This part is slightly subjective. I don't see any major security drawbacks to this. I tried several ways to access XMLHttpRequest, but i couldn't:
secure_eval('XMLHttpRequest')
secure_eval('window.XMLHttpRequest')
secure_eval('eval("XMLHttpRequest")()')
secure_eval('window.__proto__.XMLHttpRequest') // nope, it's not inherited
However, it will be a lot if you want to blacklist more things.
What functions of window and document are the ones which are considered harmful?
That depends on what you consider "harmful". Is it bad if the DOM is accessible at all? Or what about WebKit desktop notifications, or speech synthesis?
You'll have to decide this based on your specific use case.
To ship around question 2. I tried to mask all (native) functions of window, but I am not able to enumerate them:
That's because most of the methods are non-enumerable. To enumerate, you can use Object.getOwnPropertyNames(window):
var globals = Object.getOwnPropertyNames(window);
for (var i = 0; i < globals.length; i++) {
if( window[globals[i]] instanceof Function) {
console.log(globals[i]);
}
}
One of my ideas is to perform the eval within a Worker and prevent access to XMLHttpRequest and document.createElement (see my solution above).
This sounds like a good idea.
I stumbled across a really, really nice blog article about the notorious Eval here. The article does discuss in detail. You won't be able to alleviate all security concerns, but you can prevent Cross-Script Attacks by building tokens for the input. This would in theory prevent malicious code that could be harmful from being introduced.
Your only other hurdle will be Man-In-The-Middle Attacks. I'm not sure if that would be possible, as you can't trust input and output.
The Mozilla Developer Network does explicitly state:
eval() is a dangerous function, which executes the code it's passed
with the privileges of the caller. If you run eval() with a string
that could be affected by a malicious party, you may end up running
malicious code on the user's machine with the permissions of your
webpage / extension. More importantly, third party code can see the
scope in which eval() was invoked, which can lead to possible attacks
in ways to which the similar Function is not susceptible.
eval() is also generally slower than the alternatives, since it has to
invoke the JS interpreter, while many other constructs are optimized
by modern JS engines.
There are safer (and faster!) alternatives to eval() for common
use-cases.
I'm slightly against Eval and truly try to use it when warranted.
I have stated it yet in my question, but to make it more clear I will post it as an answer also:
I think the accepted answer on this question is the correct and only way to completely isolate and constrain eval().
It is also secure against these hacks:
(new ('hello'.constructor.constructor)('alert("hello from global");'))()
(function(){return this;})().alert("hello again from global!");
while(true){} // if no worker --> R.I.P. browser tab
Array(5000000000).join("adasdadadasd") // memory --> boom!
There was a question long ago much like this. So I dusted off some old code and fixed it up.
It essentially works by taking advantage of the with keyword and providing it with a frozen empty object. The prototype of the empty object is filled with null properties, the keys of which match the names global variables like self, window and their enumerable property keys; The prototype object is also frozen. eval is then called within the with statement (Almost the same way that scripts run with an implicit with(window){} block if I understand correctly). When you try to access window or its properties you get redirected (via the with block) to null versions (with same key) found in empty object (or rather the empty object's prototype):
function buildQuarantinedEval(){
var empty=(function(){
var exceptionKeys = [
"eval", "Object", //need exceptions for these else error. (ie, 'Exception: redefining eval is deprecated')
"Number", "String", "Boolean", "RegExp", "JSON", "Date", "Array", "Math",
"this",
"strEval"
];
var forbiddenKeys=["window","self"];
var forbidden=Object.create(null);
[window,this,self].forEach(function(obj){
Object.getOwnPropertyNames(obj).forEach(function(key){
forbidden[key]=null;
});
//just making sure we get everything
Object.keys(obj).forEach(function(key){
forbidden[key]=null;
});
for(var key in obj){
forbidden[key]=null;
}
});
forbiddenKeys.forEach(function(key){
forbidden[key]=null;
});
exceptionKeys.forEach(function(key){
delete forbidden[key];
});
Object.freeze(forbidden);
var empty=Object.create(forbidden);
Object.freeze(empty);
return empty;
})();
return function(strEval){
return (function(empty,strEval){
try{
with(empty){
return eval(strEval);
}
}
catch(err){
return err.message;
}
}).call(empty,empty,strEval);
};
}
Setup by building a function/closure that evaluates some expression:
var qeval=buildQuarantinedEval();
qeval("'some expression'"); //evaluate
Tests:
var testBattery=[
"'abc'","8*8","console","window","location","XMLHttpRequest",
"console","eval('1+1+1')","eval('7/9+1')","Date.now()","document",
"/^http:/","JSON.stringify({a:0,b:1,c:2})","HTMLElement","typeof(window)",
"Object.keys(window)","Object.getOwnPropertyNames(window)",
"var result; try{result=window.location.href;}catch(err){result=err.message;}; result;",
"parseInt('z')","Math.random()",
"[1,2,3,4,8].reduce(function(p,c){return p+c;},0);"
];
var qeval=buildQuarantinedEval();
testBattery.map(function(code){
const pad=" ";
var result= qeval(code);
if(typeof(result)=="undefined")result= "undefined";
if(result===null)result= "null";
return (code+pad).slice(0,16)+": \t"+result;
}).join("\n");
Results:
/*
'abc' : abc
8*8 : 64
console : null
window : null
location : null
XMLHttpRequest : null
console : null
eval('1+1+1') : 3
eval('7/9+1') : 1.7777777777777777
Date.now() : 1415335338588
document : null
/^http:/ : /^http:/
JSON.stringify({: {"a":0,"b":1,"c":2}
HTMLElement : null
typeof(window) : object
Object.keys(wind: window is not an object
Object.getOwnPro: can't convert null to object
var result; try{: window is null
parseInt('z') : parseInt is not a function
Math.random() : 0.8405481658901747
[1,2,3,4,8].redu: 18
*/
Notes: This technique can fail when some properties of window are defined late (after initializing/creating our quarantined eval function). In the past, I've noticed some property keys are not enumerated until after you access the property, after which Object.keys or Object.getOwnPropertyNames will finally be able grab their keys. On the other hand this technique can also be quite aggressive in blocking objects/functions you do not want blocked (an example would be like parseInt); In these cases, you'll need to manually add global objects/functions that you do want into the exceptionKeys array.
*edit* Additional considerations: How well this all performs depends entirely on how well the mask matches that of the property keys of the window object. Any time you add an element to the document and give it a new ID, you just inserted a new property into the global window object, potentially allowing our 'attacker' to grab it and break out of the quarantine/firewall we've setup (i.e. access element.querySelector then eventually window obj from there). So the mask (i.e., the variable forbidden) either needs to be updated constantly perhap with watch method or rebuilt each time; The former conflicts with the necessity of the mask to have a frozen interface, and the latter is kinda expensive having to enumerate all the keys of window for each evaluation.
Like I said earlier, this is mostly old code I was working on, then abandoned, that was quickly fixed up on short order. So it's not by any means thoroughly tested. I'll leave that to you.
and a jsfiddle
I have small idea about secure eval for small or limited things if you know well what u going to use eval in you can create white list and black list and excute only the strings that has the valid but it good for small covered app for example calculator has few options (x, y) and (+,*,-,/) if i added this characters in white list and add check for script length and study what excepted length of the script run it can be secure and no one can pass that
const x = 5;
const y = 10;
function secureEval(hack_string){
// 0 risk eval calculator
const whiteList = ['',' ', 'x', 'y','+','*','/','-'];
for (let i=0; i<hack_string.length; i++){
if (!whiteList.includes(hack_string[i])){
return 'Sorry u can not hack my systems';
}
}
return 'good code system identify result is : ' + eval(hack_string);
}
// bad code
document.getElementById("secure_demo").innerHTML = secureEval('x * y; alert("hacked")');
document.getElementById("demo").innerHTML = secureEval('x * y');
<!DOCTYPE html>
<html>
<body>
<h1>Secure Eval</h1>
<p id="secure_demo"></p>
<p id="demo"></p>
</body>
</html>

Get Javascript function name

Is there a way to obtain function's name from outside of it?
Lets say there is a js script on web page that we cannot modificate, just read. The script contains object, which contains objects and functions. Lets say that we want to find function named "HelloWorld".
With firebug, we loop through these objects and methods with a script, which looks something like this
// Parameter is target object.
function getFunctionNames(obj) {
// For each objects / functions
for (var id in obj) {
// Focus only on functions
if (typeof(obj[id]) == "function") {
// Get name of the function.
// console.log("Function: " + obj[id].toString());
// Code above returns a block of code without the name. Example output:
// Function: function(name) { alert("Hello World! Hello " + name + "!"); }
//
// Expected output would be
// Function: HelloWorld
}
}
}
obj[id].toString() returns a block of code instead of a name.
obj[id].name returns an empty string. Anonymous function(?).
I cannot use arguments.callee.name because I cannot modify the target code.
I could just browse objects and functions in firebug or just read source code, but I'm looking a way to do it with Javascript.
Edit
For real world example, head to Youtube and try to get the name of function "setMsg()" from "yt" object via Javascript.
Edit2
Accepting Simon's answer for being kinda closest what I was looking for. It appears that I was seeking variable name, rather than function name. While answer didn't help me on original problem, it surely answered to original question. Paul Draper's comments helped me to right direction.
Thanks!
Use obj.name
Note that arguments.callee returns a function. name is property on every function (though it's empty for anonymous functions), so that's why arguments.callee.name works.
This works for webkit (Chrome and Safari), Firefox, and possibly others. It does not work for IE: function.name not supported in IE.
As mentioned, the function doesn't have any intrinsic name other than the "" it gets from being an anonymous function. Some browsers (Firefox, probably Chrome, maybe others) do however perform some limited form of static analysis to figure out names of declared functions, to help with error stack traces. You can get to it in an relatively cross-browser way by getting setMsg to throw an exception and then parse exc.stack:
// cheat with .% in Firebug; there might be other ways of doing this, I dunno:
yt.setMsg.%m.za.__defineSetter__('a', function() { throw new Error(); });
try { yt.setMsg('a', 'a'); }
catch(e) { alert(e.stack.split('\n')[2].split('#')[0]); }
... On the other hand, this is a pretty terrible hack and dependent on the actual function involved (and if you know the function, you probably know its name already). It does work a bit more reliably when done from inside the function.
If you restrict yourself to just Firefox and are doing this for debug purposes, there are better ways of getting to it. Set devtools.chrome.enabled to true in about:config, open a Scratchpad (Shift+F4), set it to environment: browser, and run the following:
Components.utils.import("resource://gre/modules/jsdebugger.jsm");
window.addDebuggerToGlobal(window);
dbg = new Debugger();
dw = dbg.addDebuggee(content);
f = content.wrappedJSObject.yt.setMsg;
name = dw.makeDebuggeeValue(f).displayName;
dbg.removeDebuggee(content);
alert(name);
In both cases, you will note that it alerts "m.ya" instead of "setMsg", and indeed this is because the function was originally declared as m.ya = function() { ...; }. There is no reason why "setMsg" would be a preferable name, from the point of the browser.

How dangerous is it in JavaScript, really, to assume undefined is not overwritten?

Every time anyone mentions testing against undefined, it's pointed out that undefined is not a keyword so it could be set to "hello", so you should use typeof x == "undefined" instead. This seems ridiculous to me. Nobody would ever do that, and if they did it would be reason enough to never use any code they wrote... right?
I found one example of someone who accidentally set undefined to null, and this was given as a reason to avoid assuming that undefined isn't overwritten. But if they'd done that, the bug would have gone undetected, and I fail to see how that's better.
In C++ everyone is well aware that it's legal to say #define true false, but nobody ever advises you avoid true and use 0 == 0 instead. You just assume that nobody would ever be a big enough jerk to do that, and if they do, never trust their code again.
Has this ever actually bitten somebody where someone else assigned to undefined (on purpose) and it broke your code, or is this more of a hypothetical threat? I'm willing to take my chances to make my code marginally more readable. Is this a really bad idea?
To reiterate, I am not asking for how to protect against reassigned undefined. I've seen those tricks written 100 times already. I'm asking how dangerous it is to not use those tricks.
No, I never have. This is mostly because I develop on modern browsers, which are mostly ECMAScript 5 compliant. The ES5 standard dictates that undefined is now readonly. If you use strict mode (you should), an error will be thrown if you accidentally try to modify it.
undefined = 5;
alert(undefined); // still undefined
'use strict';
undefined = 5; // throws TypeError
What you should not do is create your own scoped, mutable undefined:
(function (undefined) {
// don't do this, because now `undefined` can be changed
undefined = 5;
})();
Constant is fine. Still unnecessary, but fine.
(function () {
const undefined = void 0;
})();
No proper code will do such a thing. But you can never know what some wannabe-smart developer or a plugin/library/script you are using did. On the other side, it's extremely unlikely and modern browsers will not allow overwriting undefined at all, so if you are using such a browser for development you'll quickly notice if any code tries to overwrite it.
And even though you did not ask for it - many people will probably find this question when looking for the more common "how to protect against redefined undefined" issue, so I'll answer that anyway:
There's a very good way to get a truly undefined undefined no matter how old the browser is:
(function(undefined) {
// your code where undefined is undefined
})();
This works because an argument that is not specified is always undefined. You can also do it with a function that accepts some real arguments, e.g. like this when you are using jQuery. It's usually a good idea to ensure a sane environment in this way:
(function($, window, undefined) {
// your code where undefined is undefined
})(jQuery, this);
Then you can be sure that inside that anonymous function the following things are true:
$ === jQuery
window === [the global object]
undefined === [undefined].
However, note that sometimes typeof x === 'undefined' is actually necessary: If the variable x has never been set to a value (contrary to being set to undefined), reading x in a different way such as if(x === undefined) will throw an error. This does not apply to object properties though, so if you know that y is always an object, if(y.x === undefined) is perfectly safe.
There's a simple solution to that: compare against void 0 which is always undefined.
Note that you should avoid == as it may coerce the values. Use === (and !==) instead.
That said, the undefined variable may be set by error if someone writes = instead of == when comparing something against undefined.
Only you know what code you use, and therefore how dangerous it is. This question can't be answered in the way you've clarified you want it answered.
1) Create a team policy, disallow redefining undefined, reserving it for its more popular usage. Scan your existing code for undefined left assignment.
2) If you don't control all the scenarios, if your code is used outside situations you or your policies control, then obviously your answer is different. Scan the code that does use your scripts. Heck, scan web for statistics of undefined left assignment if you wish, but I doubt that's been done for you, because it's easier to just pursue answer #1 or #3 here instead.
3) And if that answer isn't good enough, it's probably because, again, you require a different answer. Maybe you are writing a popular library that will be used inside corporate firewalls, and you don't have access to the calling code. Then use one of the other fine answers here. Note the popular jQuery library practices sound encapsulation, and begins:
(function( window, undefined ) {
Only you can answer your question in the specific way you seek. What more is there to say?
edit: p.s. if you really want my opinion, I'll tell you it's not dangerous at all. Anything that would be so likely to cause defects (such as assigning to undefined, which is obviously a well-documented risky behaviour) is itself a defect. It's the defect that is the risk. But that's just in my scenarios, where I can afford to hold that perspective. As I'd recommend you do, I answered the question for my use-cases.
It's safe to test against undefined. As you already mention. If you get to some code that overrides it (which is highly improvable), just don't use it anymore.
Maybe if you are creating a library for public use, you can use some of the techniques to avoid the user change it. But even in this case, it's their problem, not your library.
You can use undefined in your code when coding for browsers supporting ECMAScript 5.1 as it is immutable according to the language specification.
Also see this compatibility table or this caniuse ECMAScript 5 to see that all modern browsers (IE 9+) have implemented immutable undefined.
It's not dangerous at all. It can only be overwritten when running on an ES3 engine and that's not likely to be used any more.
First of all, if your code breaks it's probably not because some other developer out there "is trying to be a jerk" as you put it.
It's true that undefined is not a keyword. But it is a global level primitive. It was intended to be used like this (see "undefined" at developer.mozilla.org):
var x;
if (x === undefined) {
// these statements execute
}
else {
// these statements do not execute
}
The common alternative to that (also from MDN) and in my opinion the better way is:
// x has not been declared before
if (typeof x === 'undefined') { // evaluates to true without errors
// these statements execute
}
if(x === undefined){ // throws a ReferenceError
}
Which has a couple of advantages, the obvious one (from the comments) is that it does not trigger an exception when x is not declared. It's also worth noting that MDN also points out that it is important to use === over == in the first case because:
var x=null;
if (x === undefined) {
// this is probably what you meant to do
// these lines will not execute in this case
}
else if (x == undefined) {
// these statements will execute even though x *is* defined (as null)
}
else {
// these statements do not execute
}
This is another often overlooked reason why it is probably better to just use the second alternative in all cases.
Conclusion: It's not wrong to code it the first way, and certainly not dangerous. The argument you've seen that you use as an example against it (that it can be overwritten) is not the strongest argument for coding the alternative with typeof. But using typeof is stronger for one reason specifically: it doesn't throw an exception when your var is not declared. It could also be argued that using == instead of === is a common mistake in which case it's not doing what you expected it to. So why not use typeof?

Categories