This topic aims to incite you to share as many javascript ES5 coding mistakes as you know, in order to help all of us learn from those shared mistakes, or simply make fun of them :D
Let's start by sharing mine:
Not knowing Javascript ES5 variables are references
Not knowing Javascript ES5 uses short circuit evaluation
Not knowing Javascript ES5 only has global and function scopes(corresponding to global and local variables)
Not knowing return only works for the innermost function
Adding properties to Object.prototype without making them not enumerable
I'll continue to share more and more later. Let's share Javascript ES5 coding mistakes you know as well :)
Let's start by sharing mine:
Not knowing Javascript ES5 variables are references
Example - Treating this:
As the same as this:
However, this:
Is the same as this:
Application - Thinking that this method's referentially transparent:
When a truly side effect free version should be something this :
JavaScript:
// array1 and array2 refers to 2 different empty arrays
array1 = [];
array2 = [];
JavaScript:
array1 = array2 = []; // Both array1 and array2 refer to the same empty array
JavaScript:
// Both array2 and array3 refer to what array1 currently refers to
array2 = array1;
array3 = array1;
JavaScript:
array2 = array3 = array1; // Both array2 and array3 refer to what array1 currently refers to
JavaScript:
// This function actually isn't side effect free
sideEffectFreeFunction = function(array, val)
var tempArray = array; // The passed argument array will be modified due to sort
tempArray.sort(function(a, b) { return a.someProp - b.someProp; });
for (var index = 0, length = tempArray.length; index < length; index++) {
if (tempArray[index].someProp > val) { return tempArray[index]; }
}
return null;
} // sideEffectFreeFunction
JavaScript:
// This function's really side effect free
sideEffectFreeFunction = function(array, val)
// tempArray is a new array instead of referring to what array currently refers to
var tempArray = array.slice(0);
//
tempArray.sort(function(a, b) { return a.someProp - b.someProp; });
for (var index = 0, length = tempArray.length; index < length; index++) {
if (tempArray[index].someProp > val) { return tempArray[index]; }
}
return null;
} // sideEffectFreeFunction
Not knowing Javascript ES5 uses short circuit evaluation
Example - Treating this:
As the same as this:
Application - Writing buggy codes like this:
When it should be something like this:
JavaScript:
// expr1 will always be run but expr2 will only be run if expr1 is falsy
if (expr1 || expr2) { expr3; }
//
JavaScript:
// expr2 will always be run but expr1 will only be run if expr1 is falsy
if (expr2 || expr1) { expr3; }
//
JavaScript:
// exprThatShallAlwaysBeRun won't run if this._var1 is already truthy
this._var1 = this._var1 || exprThatShallAlwaysBeRun;
//
JavaScript:
// Ensures exprThatShallAlwaysBeRun will indeed always be run
var1 = exprThatShallAlwaysBeRun;
this._var1 = this._var1 || var1;
//
Not knowing Javascript ES5 only has global and function scopes(corresponding to global and local variables)
Example - Treating this:
As the same as this:
JavaScript:
someFunction = function(target) {
var index = someOtherFunction(target);
for (var index = 0; index < target; index++) {
yetSomeOtherFunction(index, target);
}
// index is now target - 1 instead of someOtherFunction(target)
target = someOtherFunctionAgain();
yetSomeOtherFunctionAgain(index, target);
} // someFunction
JavaScript:
someFunction = function(target) {
var index = someOtherFunction(target);
for (var i = 0; index < target; index++) {
yetSomeOtherFunction(i, target);
}
target = someOtherFunctionAgain();
yetSomeOtherFunctionAgain(index, target);
} // someFunction
Not knowing return only works for the innermost function
Example - Treating this:
As the same as this:
JavaScript:
someFunction = function(array, val)
var tempArray = array.slice(0);
tempArray.sort(function(a, b) { return a.someProp - b.someProp; });
for (var index = 0, length = tempArray.length; index < length; index++) {
if (tempArray[index].someProp > val) { return tempArray[index]; }
}
return null;
} // someFunction
JavaScript:
someFunction = function(array, val)
var tempArray = array.slice(0);
tempArray.sort(function(a, b) { return a.someProp - b.someProp; });
tempArray.forEach(function(element) { // return in forEach is the same as continue in for
if (element.someProp > val) { return element; }
});
return null;
} // someFunction
Adding properties to Object.prototype without making them not enumerable
Using an old version of DoubleX RMMV Object Properties as an example:
The above code itself seems to have no bugs in isolation, but all objects using for in loop will have troubles. It's because all the above new Object.prototype properties are enumerable.
For instance, adding the above code will cause RMMV to crash upon game start due to this:
Now for (var key in this._cache) will also enumerate all the new Object.prototype properties added by the old version of DoubleX RMMV Object Properties, as they're enumerable. This results in crashes when running bitmap.isError().
To fix that, DoubleX RMMV Object Properties can be updated into this:
It works as properties defined via Object.defineProperties aren't enumerable by default, thus making all the new Object.prototype properties not enumerable.
On a side note: Even just adding something to Object.prototype itself is already playing with fire. It should be the last resort and done with extreme caution. The above mistake's just 1 of the many possible such dangers due to insufficient carefulness.
JavaScript:
/* cond: The function checking whether an object property will be traced
label: The function returning the label of an traced object property */
Object.prototype.trace_obj_prop = function(cond, label) { // New
if (!this._obj_prop_trace) {
this._obj_prop_log = {};
this._obj_prop_trace = {};
}
// Stop tracing the object property if the path would be cyclic
if (this._obj_prop_trace[cond]) {
return;
}
//
this._obj_prop_log[cond] = "";
this._obj_prop_trace[cond] = {};
this.log_obj_prop(cond, label);
}; // Object.prototype.trace_obj_prop
/* cond: The function checking whether an object property will be traced
label: The function returning the label of an traced object property */
Object.prototype.log_obj_prop = function(cond, label) { // New
// Checks if the currently traced object property has object properties
var has_obj_prop = false;
for (var prop in this) {
if (this.hasOwnProperty(prop) && this.is_obj_prop(prop)) {
has_obj_prop = true;
break;
}
}
//
if (has_obj_prop) {
this._obj_prop_log[cond] = "{";
this.traverse_obj_prop_tree(cond, label);
this._obj_prop_log[cond] += "}";
}
}; // Object.prototype.log_obj_prop
/*----------------------------------------------------------------------------
* Label and use all nonempty subtrees to form the object property tree
*----------------------------------------------------------------------------*/
/* cond: The function checking whether an object property will be traced
label: The function returning the label of an traced object property */
Object.prototype.traverse_obj_prop_tree = function(cond, label) { // New
var op = DoubleX_RMMV.Obj_Prop;
for (var prop in this) {
if (this.hasOwnProperty(prop) && this.is_obj_prop(prop)) {
var obj = this[prop];
// Recursively traverse the property tree via Depth First Search
if (op[cond](obj)) {
this._obj_prop_log[cond] += " " + prop + ": " +
op[label](obj) + " ";
this._obj_prop_trace[cond][obj] = [prop];
}
var temp_prop = prop;
if (obj !== null && typeof obj === "object") {
obj.trace_obj_prop(cond, label);
if (Object.keys(obj._obj_prop_trace[cond]).length > 0) {
if (this._obj_prop_trace[cond][obj] === undefined) {
this._obj_prop_trace[cond][obj] = [];
}
this._obj_prop_log[cond] += " " + temp_prop + ": " +
obj._obj_prop_log[cond];
this._obj_prop_trace[cond][obj].push(
obj._obj_prop_trace[cond]);
}
}
//
}
}
}; // Object.prototype.traverse_obj_prop_tree
// prop: The current object property to be traced
Object.prototype.is_obj_prop = function(prop) { // New
// Return false for all object properties added by this plugin
if (prop === "_obj_prop_log" || prop === "_obj_prop_trace") {
return false;
}
if (prop === "trace_obj_prop" || prop === "log_obj_prop") {
return false;
}
return prop !== "traverse_obj_prop_tree" && prop !== "is_obj_prop";
//
}; // Object.prototype.is_obj_prop
For instance, adding the above code will cause RMMV to crash upon game start due to this:
JavaScript:
ImageManager.isReady = function() {
for (var key in this._cache) {
var bitmap = this._cache[key];
if (bitmap.isError()) {
throw new Error('Failed to load: ' + bitmap.url);
}
if (!bitmap.isReady()) {
return false;
}
}
return true;
};
To fix that, DoubleX RMMV Object Properties can be updated into this:
JavaScript:
Object.defineProperties(Object.prototype, {
"trace_obj_prop": {
/* cond: The function checking whether an object property will be traced
* label: The function returning the label of an traced object property
*/
value: function(cond, label) { // New
if (!this._obj_prop_trace) {
this._obj_prop_log = {};
this._obj_prop_trace = {};
}
// Stop tracing the object property if the path would be cyclic
if (this._obj_prop_trace[cond]) { return; }
//
this._obj_prop_log[cond] = "";
this._obj_prop_trace[cond] = {};
this.log_obj_prop(cond, label);
}, // Object.prototype.trace_obj_prop
writable: true,
configurable: true
},
"log_obj_prop": {
/* cond: The function checking whether an object property will be traced
* label: The function returning the label of an traced object property
*/
value: function(cond, label) { // New
// Checks if currently traced object property has object properties
var has_obj_prop = false;
for (var prop in this) {
if (this.hasOwnProperty(prop) && this.is_obj_prop(prop)) {
has_obj_prop = true;
break;
}
}
//
if (!has_obj_prop) { return; }
this._obj_prop_log[cond] = "{";
this.traverse_obj_prop_tree(cond, label);
this._obj_prop_log[cond] += "}";
}, // Object.prototype.log_obj_prop
writable: true,
configurable: true
},
/*------------------------------------------------------------------------
* Label and use all nonempty subtrees to form the object property tree
*------------------------------------------------------------------------*/
"traverse_obj_prop_tree": {
/* cond: The function checking whether an object property will be traced
* label: The function returning the label of an traced object property
*/
value: function(cond, label) { // New
var op = DoubleX_RMMV.Obj_Prop;
for (var prop in this) {
if (this.hasOwnProperty(prop) && this.is_obj_prop(prop)) {
var obj = this[prop];
// Recursively traverse property tree via Depth First Search
if (op[cond](obj)) {
this._obj_prop_log[cond] += " " + prop + ": " +
op[label](obj) + " ";
this._obj_prop_trace[cond][obj] = [prop];
}
var temp_prop = prop;
if (obj === null || typeof obj !== "object") { continue; }
obj.trace_obj_prop(cond, label);
if (Object.keys(obj._obj_prop_trace[cond]).length > 0) {
if (this._obj_prop_trace[cond][obj] === undefined) {
this._obj_prop_trace[cond][obj] = [];
}
this._obj_prop_log[cond] += " " + temp_prop + ": " +
obj._obj_prop_log[cond];
this._obj_prop_trace[cond][obj].push(
obj._obj_prop_trace[cond]);
}
//
}
}
}, // Object.prototype.traverse_obj_prop_tree
writable: true,
configurable: true
},
"is_obj_prop": {
// prop: The current object property to be traced
value: function(prop) { // New
// Return false for all object properties added by this plugin
if (prop === "_obj_prop_log" || prop === "_obj_prop_trace") {
return false;
} else if (prop === "trace_obj_prop" || prop === "log_obj_prop") {
return false;
}
return prop !== "traverse_obj_prop_tree" && prop !== "is_obj_prop";
//
}, // Object.prototype.is_obj_prop
writable: true,
configurable: true
}
});
On a side note: Even just adding something to Object.prototype itself is already playing with fire. It should be the last resort and done with extreme caution. The above mistake's just 1 of the many possible such dangers due to insufficient carefulness.
I'll continue to share more and more later. Let's share Javascript ES5 coding mistakes you know as well :)