These notes gather a few basic but easy-to-misuse points in JavaScript: falsy values, typeof, the behavior of || and &&, and the four function invocation patterns that affect the value of this.
1. The six falsy values
JavaScript treats the following six values as false in conditional contexts:
- false
- 0
- null
- undefined
- NaN
- ''
Everything else is truthy.
2. Using typeof() to inspect types
Common results returned by typeof include:
- number
- string
- boolean
- undefined
- function
- object
One detail worth remembering: when the value is an array or null, typeof returns object.
3. Operators || and &&
The logical operators are often useful not only for Boolean expressions, but also for safely reading values and setting defaults.
fight.equipment // undefined
fight.equipment.model // throw "TypeError"
fight.equipment && fight.equipment.model // undefined
var middle = stooge["middle-name"] || "none";
In the first group, accessing fight.equipment.model directly causes an error if fight.equipment is missing. Using && avoids that problem because evaluation stops when the left side is falsy.
The second example uses || to provide a fallback value. If stooge["middle-name"] is falsy, middle becomes "none".
There is also a small syntax point here: when reading properties from an object, dot notation is usually preferred. Bracket notation is needed when the property name contains characters such as a hyphen.
4. Four JavaScript invocation patterns
Calling a function pauses the current execution flow and passes control and arguments to the called function. Besides the formal parameters declared by the function, every function also receives two additional values: this and arguments.
The value of this is especially important in object-oriented JavaScript, and it depends on how the function is invoked. There are four invocation patterns: method invocation, function invocation, constructor invocation, and apply invocation. Their main difference lies in how this is initialized.
Method invocation pattern
When a function is stored as a property of an object, it is called a method. When that method is invoked, this is bound to the object. If the call expression includes property access—either through a dot expression or a subscript expression—then it is treated as a method invocation.
var myObject = {
value: 0,
increment: function (inc) {
this.value += typeof inc === 'number' ? inc : 1;
}
}
myObject.increment();
console.log(myObject.value);
myObject.increment(2);
console.log(myObject.value);
Here, increment is called as a method of myObject, so this points to myObject.
Function invocation pattern
When a function is not invoked as a property of an object, it is called as a plain function.
var sum = add(3, 4); // sum的值为7
In this invocation pattern, this is bound to the global object. This is considered a design mistake in the language. Ideally, when an inner function is called, its this should still be bound to the outer function’s this. Because that is not how the language works, methods cannot directly rely on inner functions to help them operate on the same object: the inner function receives the wrong this value.
A common workaround is to store this in another variable before entering the inner function. By convention, that variable is often named that.
For example:
var add = function(a, b) {
return a + b;
}
var myObject = {
value:3
};
myObject.func = function() {
console.log(myObject.value);
var helper = function() {
this.value = add(this.value, this.value);
}
// 函数调用模式
helper();
}
// 方法调用模式
myObject.func();
console.log(myObject.value);
In this example, myObject.func() is a method invocation, so this inside func points to myObject. But helper() is called as a plain function. As a result, this inside helper points to the global object—window in a browser, or global in Node—rather than to myObject.
The usual fix is:
var add = function(a, b) {
return a + b;
}
var myObject = {
value:3
};
myObject.func = function() {
var that = this;
var helper = function() {
that.value = add(that.value, that.value);
}
// 函数调用模式
helper();
}
// 方法调用模式
myObject.func();
console.log(myObject.value);
With this approach, helper can still access the intended object through that.
One way to understand it: in method invocation, this is preserved; in function invocation, it can be lost. The variable that acts as a bridge. Since helper is defined inside func, it can access variables from func’s scope, including that, which holds the original this from myObject.
Constructor invocation pattern
If a function is called with the new prefix, JavaScript creates a new object linked to the function’s prototype member, and this is bound to that new object.
// 创造一个名为Quo的构造器函数。它构造一个带有status属性的对象
var Quo = function (string) {
this.status = string;
}
// 给Quo的所有实例提供一个名为get_status的公共方法。
Quo.prototype.get_status = function () {
return this.status;
}
// 构造一个Quo实例
var myQuo = new Quo('ok');
// 输出测试
console.log(myQuo.get_status());
A function intended to be called with new is called a constructor function. This constructor style is not recommended by the book’s author.
Apply invocation pattern
The apply method allows a function to be called with an array of arguments. It also lets us choose the value bound to this. apply takes two parameters: the first is the value to bind to this, and the second is an array of arguments.
// 创造一个包含status成员的对象。
var statusObject = {
status: 'A-ok'
}
// statusObject并没有继承自Quo.prototype,但我们可以在statusObject上调用get_status方法
// 尽管statusObject没有一个名为get_status的方法。
var status = Quo.prototype.get_status.apply(statusObject);
console.log(status);
In this example, statusObject is explicitly bound to this, so get_status can read its status property even though statusObject itself does not inherit from Quo.prototype and does not define a get_status method.
The topic can be extended further to compare call, apply, and bind, but that deserves a separate note.