Guowei Lv

10 minute read

null and undefined

null is a language keyword and undefined is a predefined global object.

undefined represents a system level, unexpected or error-like absence of value.

nullrepresents program-level, normal or expected absence of value.

When writing programs yourself always use null.

global object

When the JavaScript interpreter starts, it creates a new global object and gives it an initial set of properties.

In client-side JavaScript, the window object serves as the global object.

wrapper objects

Whenever you try to refer to a property of a string, number or boolean, JavaScript converts the value to an object as if by calling new String(x).

This object inherits methods and is used to resolve the property reference. Once the property has been resolved, the newly created object is discarded.

The temporary objects created when you access a property are known as wrapper objects.

var s = "test";
s.len = 4; // Create a new S = new String(s); and create a new "len" property and set it to 4, then disgard the newly created S object
var t = s.len; // Create a new S = new String(s); there is no property "len" so return undefined

type conversion idioms

x + "" // Same as String(x)
+x     // Same as Number(x)
!!x    // Same as Boolean(x)

variable declaration

Before use a variable in JavaScript program, you should declare it.

var i, sum;

If you don’t specify an initial value for a variable with the var statement, the variable will be undefined.

function scope and hoisting

In some C-like programming languages, each block of code within curly braces has its own scope, and variables are not visible outside of the block in which they are declared.

This is called block scope, and JavaScript does not have it.

Instead, JavaScript uses function scope: variables are visible within the function in which they are defined and within any functions that are nested within that function.

JavaScript code behaves as if all variable declarations in a function(but not any associated assignments) are “hoisted” to the top of the function.

var scope = "global";
function f() {
  console.log(scope);   // Prints "undefined" not "global"
  var scope = "local";  // Variable initialized here, but defined everywhere
  console.log(scope);   // Prints "local"
}

as if

var scope = "global";
function f() {
  var scope;
  console.log(scope);   // Local variable is declared at the top of the function
  scope = "local";      // It exists here but still has "undefined" value
  console.log(scope);   // Prints "local"
}

object literals

The easiest way to create an object is to include an object literal in your JavaScript code.

var empty = {};
var point = {x: 0, y: 0};

creating objects with new

var o = new Object();      // Create an empty object: same as {}
var a = new Array();       // Create an empty array: same as []
var d = new Date();        // Create a Date object representing the current time
var r = new RegExp("js");  // Create a RegExp object for pattern matching

prototypes

Every JavaScript object has a second JavaScript object associated with it. This second object is known as a prototype, and the first object inherits properties from the prototype.

All objects created by object literals have the same prototype object Object.prototype.

Objects created using the new keyword and a constructor invocation use the value of the property property of the constructor function as their prototype. For example, the object created by new Array() uses Array.prototype as its prototype.

Object.create()

ECMAScript 5 defines a method, Object.create() that creates a new object, using its first argument as the prototype of that object.

var o1 = Object.create({x:1, y:2});        // o1 inherits properties x and y
var o2 = Object.create(null);              // o2 inherits no props or methods
var o3 = Object.create(Object.prototype);  // o3 is like {} or new Object

testing properties

  1. Use the in operator.

Returns true if the object has an own property or an inherited property by that name.

var o = {x: 1};
"x" in o;        // true
"toString" in o; // true
"y" in o;        // false
2. Use method hasOwnProperty()

Returns true only if the property is the object’s own property. It returns false for inherited properties.

var o = {x: 1};
o.hasOwnProperty("x");       // true
o.hasOwnProperty("toString") // false
3. Use method propertyIsEnumerable()

Returns true only if the property is the object’s own property and is enumerable.

enumerating properties

  1. for in loop

Loops through the object’s enumerable properties (own and inherited).

To loop only own properties:

for (p in o) {
  if (!o.hasOwnProperty(p)) continue;
  ...
}

To loop only data properties:

for (p in o) {
  if (typeof p === "function") continue;
  ...
}
2. Object.keys() in ES5

Object.keys() returns an array of the names of the enumerable own properties of an object.

property getters and setters

Properties defined by getters and setters are known as accessor properties.

var o = {
  // An ordinary data property
  data_prop: value,
  
  // An accessor property defined as a pair of functions
  get accessor_prop() {},
  set accessor_prop(value) {}
}

One example use of accessor properties:

var p = {
  // x and y are regular read-write data properties
  x: 1.0,
  y: 1.0,
  
  // r is a read-write accessor property with getter and setter
  get r() {
    return Math.sqrt(this.x*this.x + this.y*this.y);
  },
  set r(newValue) {
    var oldValue = Math.sqrt(this.x*this.x + this.y*this.y);
    var ratio = newValue / oldValue;
    this.x *= ratio;
    this.y *= ratio;
  },
  
  // theta is a read only accessor property with getter only
  get theta() {
    return Math.atan2(this.y, this.x);
  }
};

property attributes

Object’s property has attributes.

Data property has 4 attributes: value, writable, enumerable and configurable.

Accessor property has 4 attributes: get, set, enumerable and configurable.

Get property’s attributes:

// Get attributes of data property
js> Object.getOwnPropertyDescriptor({x: 1}, "x")
{"value":1,"writable":true,"enumerable":true,"configurable":true}

// Get attributes of accessor property
js> Object.getOwnPropertyDescriptor(p, "r")
{"get":get r() {
    return Math.sqrt(this.x*this.x + this.y*this.y);
  },"set":set r(newValue) {
    var oldValue = Math.sqrt(this.x*this.x + this.y*this.y);
    var ratio = newValue / oldValue;
    this.x *= ratio;
    this.y *= ratio;
  },"enumerable":true,"configurable":true}

Set property’s attributes:

// Start with an empty object
js> var o = {};

// Create a non-enumerable property x
js> Object.defineProperty(o, "x", {value: 1, writable: true, enumerable: false, configurable: true})
js> o.x
1
// Verify that x is not enumerable
js> Object.keys(o)
[]

// Set x to be non-writable
js> Object.defineProperty(o, "x", {writable: false})
// Verify that x cannot be written to
js> o.x = 2
js> o.x
1

// Since x is still configurable, we can change its value by this trick
js> Object.defineProperty(o, "x", {value: 2})
js> o.x
2

object attributes

Every object has prototype, class, and extensible attributes.

prototype attribute

An object’s prototype attribute specifies the object from which it inherits properties.

To get an object’s prototype, use Object.getPrototypeOf().

js> var o = Object.create({x: 1})
js> o
{}
js> Object.getPrototypeOf(o)
{"x":1}

class attribute

Useless.

extensible attribute

The extensible attribute of an object specifies whether new properties can be added to the object or not.

array

Remember that arrays are a specialized kind of object. The square brackets used to access array elements work just like the square brackets used to access object properties. JavaScript converts the numeric array index you specify to a string.

What is special about arrays is that when you use property names that are non-negative integers less than 2^32, the array automatically maintains the value of the length property for you.

sparse array

js> var a = [];
undefined
js> a
[]
js> a[1000] = 0;
0
js> a.length
1001

array length

If you set the length property to a non-negative integer n smaller than its current value, any array elements whose index is greater than or equal to n are deleted from the array:

js> a = [1,2,3,4,5]
js> a.length = 3
js> a
[1, 2, 3]

array methods

// Array.join()

js> var a = [1, 2, 3];
js> a.join();
"1,2,3"
js> a.join(" ");
"1 2 3"
js> a.join("");
"123"

// Array.reverse()

js> var a = [1, 2, 3];
js> a.reverse()
js> a
[3, 2, 1]

// Array.sort()
js> var a = ["banana", "cherry", "apple"];
js> a.sort()
js> a
["apple", "banana", "cherry"] // alphabetic order
js> var a = [33, 4, 1111, 222];
js> a.sort()
js> a
[1111, 222, 33, 4] // alphabetic order
js> a.sort(function(a, b) {return a - b;});
[4, 33, 222, 1111] // numerical order

// Array.concat()

js> var a = [1, 2, 3];
js> a.concat(4, 5);
[1, 2, 3, 4, 5]
js> a
[1, 2, 3]
js> a.concat([4, 5]);
[1, 2, 3, 4, 5]
js> a.concat([4, 5], [6, 7]);
[1, 2, 3, 4, 5, 6, 7]
js> a.concat(4, [5, [6, 7]])
[1, 2, 3, 4, 5, [6, 7]] // does not recursively flatten array of array

// Array.slice()

js> var a = [1, 2, 3, 4, 5];
js> a.slice(0, 3);
[1, 2, 3]
js> a.slice(3);
[4, 5]
js> a.slice(1, -1);
[2, 3, 4]
js> a.slice(-3, -2);
[3]

// Array.splice()
// The first argument specifies the array position at which the insertion / deletion is to begin.
// The second argument specifies the number of elements that should be deleted from the array.

js> var a = [1,2,3,4,5,6,7,8]
js> a.splice(4)
[5, 6, 7, 8]
js> a
[1, 2, 3, 4]
js> a.splice(1, 2)
[2, 3]
js> a
[1, 4]
js> a.splice(1, 1)
[4]
js> a
[1]

// These arguments may be followed by any number of additional arguments 
// that specify elements to be inserted into the array, 
// starting at the position specified by the first argument
js> var a = [1,2,3,4,5]
js> a.splice(2, 0, 'a', 'b')
[]
js> a
[1, 2, "a", "b", 3, 4, 5]
js> a.splice(2, 2, [1, 2], 3)
["a", "b"]
js> a
[1, 2, [1, 2], 3, 3, 4, 5]

// push() and pop()
// push addes elements at the end of the array and returns the new length
// pop removes elements at the end of the array and returns the value removed
js> var stack = []
undefined
js> stack.push(1, 2)
2
js> stack
[1, 2]
js> stack.pop()
2
js> stack
[1]
js> stack.push([4, 5])
2
js> stack
[1, [4, 5]]

// unshift() and shift()
// like push() and pop(), except that they insert / remove items from the beginning of the array.

var a = [];           // []
a.unshift(1);         // [1]
a.unshift(22);        // [22, 1]
a.shift();            // [1]
a.unshift(3, [4, 5]); // [3, [4, 5], 1]

forEach()

js> var data = [1,2,3,4,5];
js> var sum = 0;
js> data.forEach(function(value) { sum += value;});
js> sum
15

js> data.forEach(function(v, i, a) {a[i] = v + 1;});
js> data
[2, 3, 4, 5, 6]

map()

js> var a = [1, 2, 3];
js> var b = a.map(function(x) { return x * x;});
js> b
[1, 4, 9]
js> a
[1, 2, 3] // map() returns a new array

filter()

js> var a = [1, 2, 3];
js> var smallValues = a.filter(function(x) { return x < 3; });
js> smallValues
[1, 2]
js> a
[1, 2, 3] // filter() returns a new array

every() and some()

js> var a = [1, 2, 3, 4, 5];
js> a.every(function(x) { return x < 10; });
true
js> a.some(function(x) { return x % 2 === 0; });
true

reduce() and reduceRight()

js> var a = [1, 2, 3, 4, 5];
js> var sum = a.reduce(function(accumulator, value) { return accumulator + value; }, 0);
js> sum
15
js> var a = [2, 3, 4];
js> var big = a.reduceRight(function(accumulator, value) { return Math.pow(value, accumulator); });
js> big // calculate 2 ^ (3 ^ 4)
2.4178516392292583e+24

indexOf() and lastIndexOf()

js> var a = [0, 1, 2, 1, 0];
js> a.indexOf(1); // search from the beginning
1
js> a.lastIndexOf(1); // search from the end
3
js> a.indexOf(3); // not found returns -1
-1

function defination express V.S. function declaration statement

function add(a, b) {
  return a + b;
}

var x = add(1, 2);

function add(a, b) {
  return -1;
}

console.log(x); // this will print -1

// all the function declarations are hoisted to the top level

But function defination expression behaves more logical in this case:

var add = function(a, b) {
  return a + b;
}

console.log(add(1, 2)); // 3

add = function(a, b) {
  return -1;
}

console.log(add(1, 2)); // -1

this and that

It is a common mistake to assume that a nested function invoked as a function can use this to obtain the invocation context of the outer function. But a trick to solve that is:

var o = {
  m: function () {
    var that = this;
    console.log(this === o);
    f();

    function f() {
      console.log(this === o);
      console.log(that === o);
    }
  }
};

o.m();

comments powered by Disqus