Skip to content

Instantly share code, notes, and snippets.

@etsuo
Last active January 6, 2016 18:24
Show Gist options
  • Save etsuo/8d2ebb43c12412502ac1 to your computer and use it in GitHub Desktop.
Save etsuo/8d2ebb43c12412502ac1 to your computer and use it in GitHub Desktop.
Some interesting behaviors of JavaScript to master
// Throw this into a file like test.js
// then use: `node test.js` to run it
'use strict'
// Some inheritence
//******************************************************************************
// Declaring the base object
//*************************************
console.log('\n// Declaring the base object');
function objA() {
this.name = 'objA';
}
objA.prototype.whoAmI = function () {
console.log('I am: ' + this.name);
}
var a = new objA();
a.whoAmI();
// "new" creates a new instsance as you'd expected (the references don't match)
console.log(a !== new objA());
console.log(a === a);
// If you want to understand how == & === work and why this is comparing references, see:
// see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators
// "...If both operands are objects, then JavaScript compares internal references which are not equal when operands refer to different objects in memory."
// Inheriting from the base object
//*************************************
console.log('\n// Inheriting from the base object');
function objB() {
this.name = 'objB';
}
objB.prototype = new objA();
var b = new objB();
b.whoAmI();
// But is it really inheriting
//*************************************
console.log('\n// But is it really inheriting???');
// But is it REALLY inheriting?...like Java / C# inheriting?
// Here we modify the base object by adding the addedLater() function...
objA.prototype.addedLater = function() {
console.log('I was added later');
}
b.addedLater();
// Notice the "b" instance of objB now has objA.addedLater() even though
// we're still working with an an object "b" that was instantiated before
// objA was extended with the new function.
//
// Java/C# copies for inheritence
// Javascript delegates to its prototype, so if x is a y and x lacks a property,
// it delegates to y to see if that has the property... if that doesn't it goes to
// y's prototype... and so forth up the prototype chain.
//
// JavaScript doesn't have the notion of a "Class" - just objects
// But can I override?
//*************************************
console.log('\n// But can I override?');
objB.prototype.whoAmI = function() {
console.log('Who am I? I am: ' + this.name);
}
b.whoAmI();
objA.prototype.whoAmI = function() {
console.log('not going to impact objB since it overrides objA.whoAmI');
}
b.whoAmI();
// Prototype vs "this" function declaration
//******************************************************************************
console.log('\n////////// Prototype vs "this" function declaration');
// What's the difference between "this" and prototype when declaring functions?
function objC() {
this.testA = function(obj) {
console.log(this.testA === obj);
}
}
objC.prototype.testB = function(obj) {
console.log(this.testB === obj)
}
var c1 = new objC(),
c2 = new objC();
c1.testA(c2.testA);
c1.testB(c2.testB);
// value vs reference passing
//******************************************************************************
console.log('\n////////// value vs reference passing');
(function() {
var val = 0; // primitives are passed by value
var ref = {x:0} // objects pass by reference
var arr = [val]; // arrays pass by reference
test(val);
console.log(val);
test(ref);
console.log(ref.x);
test(arr);
console.log(arr);
function test(num) {
if(typeof(num) == 'object')
// check to see if x is a number, if it's not
// assume the object is a array and increment it's 0 index, if it is
// increment the x property;
(isNaN(num.x)) ? num[0]++ : num.x++;
else
// num is not an object, so just assume it's a number
num++;
}
})();
// "this" magic
//******************************************************************************
console.log('\n////////// "this" magic');
// objD is a constructor function if you instantiate it with the new keyword,
// the keyword "this" for the result points to the new object... otherwise, the function's "this"
// points to undefined (if you're using use strict)...
// Read this: http://stackoverflow.com/a/9822631/2305837
// Read this: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new
// When the code new Foo(...) is executed, the following things happen:
//
// 1. A new object is created, inheriting from Foo.prototype.
// 2. The constructor function Foo is called with the specified arguments and this bound to the newly created object. new Foo is equivalent to new Foo(), i.e. if no argument list is specified, Foo is called without arguments.
// 3. The object returned by the constructor function becomes the result of the whole new expression. If the constructor function doesn't explicitly return an object, the object created in step 1 is used instead.
// (Normally constructors don't return a value, but they can choose to do so if they want to override the normal object creation process.)
// new vs. not new (constructor functions vs resulting objects)
//*************************************
// see http://stackoverflow.com/a/14450766/2305837
console.log('\n// new vs. not new (constructor functions vs resulting objects)');
function objD() {}
var d = new objD();
var d2 = {};
// Only constructor functions have prototypes... objects do not
console.log(objD.prototype)
console.log(d.prototype)
console.log(d2.prototype);
// Objects point to their constructors
console.log(objD.constructor)
console.log(d.constructor)
console.log(d2.constructor)
// With d2, notice the result is the root Object object.
// All objects in JavaScript are descended from Object
// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object
// Constructor functions don't have "this"; the resulting constructed object gets "this".
//*************************************
console.log('\n// Constructor functions don\'t have "this"; the resulting constructed object gets "this"');
function objE() {
console.log('\n"this" defined? ' + (this !== undefined));
this.x = 'works';
try {
test('I do not work');
} catch(e) {
console.log('expected error: ' + e);
console.log('"this" in the test function is different than "this" of the outer function"');
console.log('so without binding the outer function\'s "this" to test(), test() has no access to this.x of the outer functon');
}
test.bind(this)('bind');
test.call(this, 'call');
function test(i) {
this.y = 'result: ';
console.log(this.y + i + ' ' + this.x);
}
}
console.log('// So, if you call objE(), you\'ll see that "this" is undefined');
try {
objE(); // notice this isn't being instantiated (new), it's just being called
// as a function... not a function constructor...
} catch(err) {
console.log(err);
}
console.log('\n// But if you instantiate it, you\'ll see that e has "this"');
var e = new objE();
// you can also use .bind to explicitly attach a reference to a specific "this"
console.log('\n// you can also use .bind to explicitly attach a reference to a specific "this"');
objE.bind(this)();
// here, objD isn't being instantiated, but it's being given a "this" context
// "this" here refers to the closure that node creates around test.js
console.log('\n// another way of using bind');
// you could also do one of the following:
var e2 = objE.bind(this);
e2();
console.log('\n// you can also use .call to bind "this", and pass parameters')
objE.call(this, 1, 2, 3); // the constructor function doesn't take three parameters, but if it did... this is how you'd use call with them
// "this" magic in action
//******************************************************************************
console.log('\n////////// "this" magic in action...');
function objF() {
this.pre = 'Test-';
var arr = [1, 2, 3];
arr.forEach(function(a) {
if(this === undefined)
console.log('No "this"');
else
console.log(this.pre + a);
}); // notice you passed in an anonymous function... remember the
// the behavior of "this" and functions above...
console.log('---');
arr.forEach(function(a) {
if(this === undefined)
console.log('No "this"');
else
console.log(this.pre + a);
}, this); // forEach takes a reference to "this" as its last parameter
// and binds it to the function.
console.log('---');
// Alternatively
function iter(a) {
if(this === undefined)
console.log('No "this"');
else
console.log(this.pre + a);
};
var f = iter.bind(this);
arr.forEach(f);
}
new objF();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment