Skip to content

Instantly share code, notes, and snippets.

@jamesyang124
Last active August 29, 2015 14:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jamesyang124/c1cbc3c57e0ada4d5593 to your computer and use it in GitHub Desktop.
Save jamesyang124/c1cbc3c57e0ada4d5593 to your computer and use it in GitHub Desktop.
// example object

var obj = {'property': 1234}

Object & Prototype

  1. Everything is object, except null and undefined.

  2. 2.toString(); cannot work is because parser tries to convert it as dot notation in numderical values.

  • (2).toString(); to workaround.
  1. Object as Hashmap. Use {} to instantiate it. This new object inherits from Object.prototype and does not have own properties defined.
  • Accessing it by: foo["property"] or foo.property
  • Insert key by string literal, some parser mis-design so will not able to address property: than the 'property' as key.
  1. Setting the property of object to null or undefined will only remove the value of associated property, but not the key.
  • Use delete to delete an property : delete obj.property
  1. Javascript does not implement the classical inheritance model, instead, use prototype.
function Foo() {
    this.value = 42;
}
Foo.prototype = {
    method: function() {}
};

function Bar() {}

// Set Bar's prototype to a new instance of Foo
Bar.prototype = new Foo();
Bar.prototype.foo = 'Hello World';

// Make sure to list Bar as the actual constructor
Bar.prototype.constructor = Bar;

var test = new Bar(); // create a new bar instance

// The resulting prototype chain
test [instance of Bar]
    Bar.prototype [instance of Foo]
        { foo: 'Hello World' }
        Foo.prototype
            { method: ... }
            Object.prototype
                { toString: ... /* etc. */ }
  1. When accessing the properties of an object, JavaScript will traverse the prototype chain upwards until it finds a property with the requested name.

  2. While the prototype property is used to build the prototype chains, it is still possible to assign any given value to it. However, primitives value will simply get ignored when assigned as a prototype.

function Foo() {}
Foo.prototype = 1; // no effect

Performance of prototype chain

  1. Access non-existent properties will always traverse the full prototype chain.

  2. When iterating over the properties of an object every property that is on the prototype chain will be enumerated.

  3. One mis-feature that is often used is to extend Object.prototype or one of the other built in prototypes. This technique is called monkey patching and breaks encapsulation. While used by popular frameworks such as Prototype, there is still no good reason for cluttering built-in types with additional non-standard functionality.

  4. The only good reason for extending a built-in prototype is to backport the features of newer JavaScript engines; for example, Array.forEach.

Find property in an Object

  1. call hasOwnProperty to lookup only itself and not somewhere in its prototype chain.

  2. It is necessary to use the hasOwnProperty method which all objects inherit from Object.prototype.

  3. hasOwnProperty is the only thing in JavaScript which deals with properties and does not traverse the prototype chain.

// Poisoning Object.prototype
Object.prototype.bar = 1;
var foo = {goo: undefined};

foo.bar; // 1
'bar' in foo; // true

foo.hasOwnProperty('bar'); // false
foo.hasOwnProperty('goo'); // true

// use hasOwnProperty as a property
var foo = {
    hasOwnProperty: function() {
        return false;
    },
    bar: 'Here be dragons'
};

foo.hasOwnProperty('bar'); // always returns false

// Use another Object's hasOwnProperty and call it with 'this' set to foo
({}).hasOwnProperty.call(foo, 'bar'); // true

// It's also possible to use hasOwnProperty from the Object
// prototype for this purpose
Object.prototype.hasOwnProperty.call(foo, 'bar'); // true
function modulus(){
return Math.sqrt(this.re * this.re + this.im * this.im);
}

var o = {
re: 1,
im: -1,
get phase(){
  return Math.atan2(this.im, this.re);
  }
};

Object.defineProperty(o, 'modulus', {
  get: modulus, enumerable:true, configurable:true});

console.log(o.phase, o.modulus); // logs -0.78 1.4142
  1. for in with hasOwnProperty. In old version of ECMA script, when hasOwnProperty is left out, the code is prone to errors in cases where the native prototypes - e.g. Object.prototype - have been extended.
// still the foo from above
for(var i in foo) {
  if (foo.hasOwnProperty(i)) {
      console.log(i);
  }
}

Functions

  1. Hoisted: If you use the var functionName syntax, only the function's declaration (e.g. var functionName;) gets moved at the top of its scope. However, if you use function functionName() syntax, the function declaration and definition (the actual instructions inside the function) get moved to the top of the function's scope.
//case 1 delcation function
example1();
function example1() {
  console.log("Ran the example");
}

// Both function's definition and declaration will hoisted at the top of the scope.
// which is interprete as follow:
var example1;
example1 = function() {
  console.log("Ran the example");
}
example1();

//case 2 declare function as a variable.
example2();
var example2 = function() {
  console.log("Ran the example");
}

// It will only hoist the function varaible declaration, the function definition will not be hoisted.
var example2;
example2();
// fucntion definition is not hoisted.
example2 = function() {
  console.log("Ran the example");
}
  1. Function is a first class variable, so it will be hoisted before execution(similar as primitive variable declaration). It is available everywhere in the scope it was defined. Also, only function can create scope, object cannot.
foo(); // Works because foo was created before this code runs
function foo() {}
  1. Function name resolution
var foo = function bar() {
    bar(); // Works
}
bar(); // ReferenceError
  • bar is not available in the outer scope, since the function only gets assigned to foo; however, inside of bar, the name of the function is always made available in the local scope of the function itself.
  1. Explicitly Setting this. When using the call or apply methods of Function.prototype, the value of this inside the called function gets explicitly set to the first argument of the corresponding function call.
function foo(a, b, c) {}

var bar = {};
foo.apply(bar, [1, 2, 3]); // array will expand to the below
foo.call(bar, 1, 2, 3); // results in a = 1, b = 2, c = 3

Constructors

  1. Inside the constructor - the called function - the value of this refers to a newly created object. The prototype of this new object is set to the prototype of the function object that was invoked as the constructor.

  2. If the function that was called has no explicit return statement, then it implicitly returns the value of this - the new object.

function Bar(){
  this.bla = 1;
}

Bar.prototype.guess = function(){
  console.log("new Bar object's prototype method use var: " + this.bla);
}

var bar = new Bar();
  1. In case for explicitly return statement, the constructor return that value only if that is an Object.
function Bar(){
  return 1;
}
new Bar() // return Object, not value 1.

function Car(){
  return { foo: 1 };
}
new Car() // return object with foo property.
  1. When the new function is omitted, the funrction will not return object. JS put that constructor as a function under global shared namespace, so this will refer to global object. Global object usually refers to window object.
function Bar(){
  this.bla = 1;
}
Bar();
console.log(bla); // 1
  1. To make the consistent behaviour of returning statement from the initialization with or without new keyword, we should explicitly return an Object instead.
function Bar(){
  var bla = 1;
  return {
    // closure for bla
    poo: bla
  }
}
// We return another object,
// it will not associate with Bar object and through its prototype chain.
Bar.prototype = {
  foo: function(){
    console.log("Hi" + this.bla);
  }
};
new Bar();
Bar();
  1. It should also be noted that the call new Bar() does not affect the prototype of the returned object. While the prototype will be set on the newly created object, Bar never returns that new object.

  2. Instead of this, we can make an object inside the function, then call that function to return our customzied object. This makes the setting for private variable more easier, but als left few drawbacks which is listed below.

function Bar(){
  var obj = {};
  obj.foo = 1;
  obj.poo = function(){
    // this keyword now binds to obj.
    console.log(this.foo);
  }
  return obj;
}
var b = Bar();
    1. It uses more memory since the created objects do not share the methods on a prototype.
    1. In order to inherit, the factory needs to copy all the methods from another object or put that object on the prototype of the new object.
    1. Dropping the prototype chain just because of a left out new keyword is contrary to the spirit of the language.

Scope and Namespaces

  1. Only function can create scope.
function test() { // a scope
  for(var i = 0; i < 10; i++) { // not a scope
      // count
  }
  console.log(i); // 10
}
  1. All things are defined in one global shared namespace. Each time a variable is referenced, JS will traverse through all scopes until it finds it. If it finally reached to global scope and cannot find it, then raise a ReferenceError.

  2. Advancely, When curly braces are not used in these three cases: an assignment, return statement or as a function argument, the { ... } notation will get interpreted as a block statement and not as an object literal. This, in conjunction with automatic insertion of semicolons(by compiler), can lead to subtle errors.

  3. Also, forget defind variable inside the function scope with var keyword, may cause it use the variable from global shared namespace.

// global scope
var foo = 42;
function test() {
  // local scope
  foo = 21;
}
test();
foo; // 21
  1. Local variable without var will only be the paramters in a function.
// global scope
var foo = 1;
var bar = 2;
var i = 2;

function test(i) {
    // local scope of the function test
    // local var i override global var i.
    i = 5;

    var foo = 3;
    bar = 4;
}
test(10);
  1. JS will hoisted function declaration and var statements to the top of its enclsoing socpe. In follow code example, it will be hoisted to global shared namespace scope.
// var statements got moved here
var bar, someValue; // default to 'undefined'

// the function declaration got moved up too
function test(data) {
    var goo, i, e; // missing block scope moves these here
    if (false) {
        goo = 1;

    } else {
        goo = 2;
    }
    for(i = 0; i < 100; i++) {
        e = data[i];
    }
}

//bar(); // fails with a TypeError since bar is still 'undefined'
someValue = 42; // assignments are not affected by hoisting
bar = function() {};

test();
  1. In this example, after hoisted applied, the goo var in if block will use local variable goo from the else block. So once entering a new scope, JS firstly hoist function declaration(due to function is a first class object) and variables.
function test(data) {
  if (false) {
      goo = 1;

  } else {
      var goo = 2;
  }
  for(var i = 0; i < 100; i++) {
      var e = data[i];
  }
}
  1. Without hoisted vars or functions, it may cause ReferenceError, but thanks JS hoisted that var, below code works.
// check whether SomeImportantThing has been initialized
if (!SomeImportantThing) {
  var SomeImportantThing = {};
}
  1. Function scope has arguments which is an array contains the arguments. All scope has this to refer to the current obejct for this scope. Caveat: dont use var to declare arguments variable, it cause default arguments will be overriden.

  2. Name resoultion order: find var foo in current scope > function foo in current scope > next outer scope if still cannot find it. Eventually will reach to global shared namespace scope.

  3. Anonymous function can used as expression. We can then leverage this to execute a function immediately. Unnamed functions are considered expressions; so in order to be callable, they must first be evaluated.

(function() {
  // a self contained "namespace"

  window.foo = function() {
      // an exposed closure
  };

})(); // execute the function immediately

// other style to execute function immediately.
!function(){}()
+function(){}()
(function(){}());

Array

  1. Don't use for in in an array. for in is for object to enumerate its properties. Use push() or pop() to get and set last element.

  2. When there is only one argument passed to the Array constructor and when that argument is a Number, the constructor will return a new sparse array with the length property set to the value of the argument. It should be noted that only the length property of the new array will be set this way; the actual indexes of the array will not be initialized.

  3. Literals are preferred to the Array constructor. They are shorter, have a clearer syntax, and increase code readability.

Type

  1. equqlity operator == as weak typing.
""           ==   "0"           // false
0            ==   ""            // true
0            ==   "0"           // true
false        ==   "false"       // false
false        ==   "0"           // true
false        ==   undefined     // false
false        ==   null          // false
null         ==   undefined     // true
" \t\r\n"    ==   0             // true
  1. strict equality operator ===.
""           ===   "0"           // false
0            ===   ""            // false
0            ===   "0"           // false
false        ===   "false"       // false
false        ===   "0"           // false
false        ===   undefined     // false
false        ===   null          // false
null         ===   undefined     // false
" \t\r\n"    ===   0             // false
  1. Once comparing with object, == and === behave differently. - Both operators compare for identity and not equality; that is, they will compare for the same instance of the object, much like pointer comparison in C.
{} === {};                   // false
new String('foo') === 'foo'; // false
new Number(10) === 10;       // false
var foo = {};
foo === foo;                 // true
  1. Use strict equality operator instead of equality operator.

Bad typeof keyword

  1. Don't use it. check the list. Type column refers to the type return by typeof.
Value               Class      Type
-------------------------------------
"foo"               String     string
new String("foo")   String     object
1.2                 Number     number
new Number(1.2)     Number     object
true                Boolean    boolean
new Boolean(true)   Boolean    object
new Date()          Date       object
new Error()         Error      object
[1,2,3]             Array      object
new Array(1, 2, 3)  Array      object
new Function("")    Function   function
/abc/g              RegExp     object (function in Nitro/V8)
new RegExp("meow")  RegExp     object (function in Nitro/V8)
{}                  Object     object
new Object()        Object     object
  1. One way is to check the class of the Object.
function is(type, obj) {
  var clas = Object.prototype.toString.call(obj).slice(8, -1);
  return obj !== undefined && obj !== null && clas === type;
}
Object.prototype.toString();  // return "[object Object]" string
Object.prototype.toString.call('test'); // return "[object string]"

is('String', 'test'); // true
is('String', new String('test')); // true
  1. The only useful for the typeof is to check whether undefined
typeof foo !== 'undefined'

instanceof keyword

  1. It is only useful for checking customed object or native types.
unction Foo() {}
function Bar() {}
Bar.prototype = new Foo();

new Bar() instanceof Bar; // true
new Bar() instanceof Foo; // true

// This just sets Bar.prototype to the function object Foo,
// but not to an actual instance of Foo
Bar.prototype = Foo;
new Bar() instanceof Foo; // false

new String('foo') instanceof String; // true
new String('foo') instanceof Object; // true

'foo' instanceof String; // false
'foo' instanceof Object; // false
  1. One important thing to note here is that instanceof does not work on objects that originate from different JavaScript contexts (e.g. different documents in a web browser), since their constructors will not be the exact same object.

  2. The instanceof operator should only be used when dealing with custom made objects that originate from the same JavaScript context. Just like the typeof operator, every other use of it should be avoided.

Type casting

  1. JS is a weak typed language. Type coercion happens everywhere if we dont specify with strictly equality operator or convertions.
// These are true
new Number(10) == 10; // Number object is converted
                      // to a number primitive via implicit call of
                      // Number.prototype.valueOf method

10 == '10';           // Strings gets converted to Number
10 == '+10 ';         // More string madness
10 == '010';          // And more
isNaN(null) == false; // null converts to 0
                      // which of course is not NaN

// These are false
10 == 010;
10 == '-10';

new Number(10) === 10;     // False, Object and Number
Number(10) === 10;         // True, Number and Number
new Number(10) + 0 === 10; // True, due to implicit conversion
  1. Several tricks to do implicit conversion.
// casting to string
'' + 10 == '10'; // true
// casting to number
+'10' == 10; // true

// casting to boolean
!!'foo';   // true
!!'';      // false
!!'0';     // true
!!'1';     // true
!!'-1'     // true
!!{};      // true
!!true;    // true

Core

Automatic Semicolon Insertion

  1. Don't let JS parser to guess where to put semicolon for the code. Always added to your code. JS does not actually well-designed for omitting semicolon like Ruby did.

The delete operator

  1. It's impossible to delete global variables, functions and some other stuff in JavaScript which have a DontDelete attribute set.

  2. When a variable or a function is defined in a global or a function scope it is a property of either the Activation object or the Global object. Such properties have a set of attributes, one of which is DontDelete. Variable and function declarations in global and function code always create properties with DontDelete, and therefore cannot be deleted.

// global variable:
var a = 1; // DontDelete is set
delete a; // false
a; // 1

// normal function:
function f() {} // DontDelete is set
delete f; // false
typeof f; // "function"

// reassigning doesn't help:
f = 1;
delete f; // false
f; // 1
  1. Explcitly properties can delete by delete operator because it does not set DontDelete attribute for those properties.
// explicitly set property:
var obj = {x: 1};
obj.y = 2;
delete obj.x; // true
delete obj.y; // true
obj.x; // undefined
obj.y; // undefined

// this works fine, except for IE:
// IE (at least 6-8) has some bugs, so the code below doesn't work.
var GLOBAL_OBJECT = this;
GLOBAL_OBJECT.a = 1;
a === GLOBAL_OBJECT.a; // true - just a global var
delete GLOBAL_OBJECT.a; // true
GLOBAL_OBJECT.a; // undefined
  1. Functions' normal arguments, arguments objects and built-in properties also have DontDelete set.
// function arguments and properties:
(function (x) {

  delete arguments; // false
  typeof arguments; // "object"

  delete x; // false
  x; // 1

  function f(){}
  delete f.length; // false
  typeof f.length; // "number"

})(1);
  1. The delete operator often has unexpected behaviour and can only be safely used to delete explicitly set properties on normal objects.

Don't use eval

  1. The eval function will execute a string of JavaScript code in the local scope.
var foo = 1;
function test() {
    var foo = 2;
    var bar = eval;
    bar('foo = 3');
    return foo;
}
test(); // 2  -> now directly called.
foo; // 3
  1. However, eval only executes in the local scope when it is being called directly and when the name of the called function is actually eval.

  2. Never ever use it. Though The timeout functions setTimeout and setInterval can both take a string as their first argument which implicitly use eval.

undefined and null

  1. The value undefined is a type with exactly one value: undefined.

  2. The language also defines a global variable that has the value of undefined; this variable is also called undefined. However, this variable is neither a constant nor a keyword of the language. This means that its value can be easily overwritten.

  3. Below list the case will return undefined type.

- Accessing the (unmodified) global variable undefined.
- Accessing a declared but not yet initialized variable.
- Implicit returns of functions due to missing return statements.
- return statements that do not explicitly return anything.
- Lookups of non-existent properties.
- Function parameters that do not have any explicit value passed.
- Anything that has been set to the value of undefined.
- Any expression in the form of void(expression)
  1. Since the global variable undefined only holds a copy of the actual value of undefined, *assigning a new value to it does not change its type(it is still undefined type and undeinfed value) in global scope. In order to compare something against the value of undefined, it is necessary to retrieve the value of undefined first.

  2. There has two ways to protect the overriden of undefined variable, but both are with the similar idea: add an additional parameter to anonymous wrapper and does not pass argument in.

var undefined = 123;
console.log(undfined); // undefined;
(function(something, foo, undefined) {
  // undefined in the local scope does
  // now again refer to the value `undefined`
})('Hello World', 42);

// var declaration inside the function
var undefined = 123;
(function(something, foo) {
  var undefined;
  ...

})('Hello World', 42);

// undefined in function scope may overriden
(function(){ var undefined = 123; console.log(undefined);})();
// 123
  1. While undefined in the context of the JavaScript language is mostly used in the sense of a traditional null, the actual null is used in some JavaScript internals (like declaring the end of the prototype chain by setting Foo.prototype = null), but in almost all cases, it can be replaced by undefined.

setTimeout and setInterval

  1. When setTimeout is called, it returns the ID of the timeout and schedule foo to run approximately one thousand milliseconds in the future. foo will then be executed once.

  2. setTimeout called exaclty once. But due to JS is single thread and its schedule queue, or the other code may execute then block the thread, it is by no means a safe bet that one will get the exact delay specified in the setTimeout call.

function foo() {}
var id = setTimeout(foo, 1000); // returns a Number > 0
  1. The function that was passed as the first parameter will get called by the global object, which means that this inside the called function refers to the global object.
function Foo() {
  this.value = 42;
  this.method = function() {
      // this refers to the global object
      console.log(this.value); // will log undefined
  };
  setTimeout(this.method, 500);
}
new Foo();
// undefined;
  1. setInterval called the function periodically. Especially with small intervals, result in function calls stacking up.
function foo(){
  // something that blocks for 1 second
}
setInterval(foo, 100);
  1. From above code, when function finished it with 1 sec, the call stack may contains 10 future calls, finally cause stack overflow.

  2. The better approach should put the setInterval inside the function.

function foo(){
  // something that blocks for 1 second
  setTimeout(foo, 100);
}
foo();
  1. We can also manually clean timeout by cleanTimeout or clearInterval with its ID.
var id = setTimeout(foo, 1000);
clearTimeout(id);
  1. To clear all timeout, its necessary to use brute-force to clean it.
for(var i = 0; i < 1000; i++){
  clearTimeout(i);
}
- But there might still be timeouts that are unaffected by this arbitrary number. Another way of doing this is to consider that the ID given to a timeout is incremented by one every time you call setTimeout.

```js
// clear "all" timeouts
var biggestTimeoutId = window.setTimeout(function(){}, 1),
i;
for(i = 1; i <= biggestTimeoutId; i++) {
    clearTimeout(i);
}
```
  1. setTimeout and setInterval can also take a string as first parameter, which implicitly call eval. Because eval executed in global scope, so the string will refere the function defined in global rather then the internal of function.
function foo() {
  // will get called
}

function bar() {
    function foo() {
        // never gets called
    }
    setTimeout('foo()', 1000);
}
bar();
  1. It should never use eval, so don't pass string to trigger it.

  2. when arguments need to be supplied to the function that gets called. An anonymous function should be passed that then takes care of the actual call. Furthermore, the use of setInterval should be avoided because its scheduler is not blocked by executing JavaScript.

- Caveat: Microsoft's Internet Explorer does not pass the arguments directly to the callback. So add a anonymous wrapper to call the function with the paramters will be better.
- See [MDN setTimeout#callback arguments]( https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers.setTimeout#Callback_arguments)
function foo(a, b, c) {}

// NEVER use this
setTimeout('foo(1, 2, 3)', 1000)

// Instead use an anonymous function
setTimeout(function() {
    foo(1, 2, 3);
}, 1000)

Deffered Object

  1. http://tutorials.jenkov.com/jquery/deferred-objects.html
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment