继承和原型链

JavaScript is a bit confusing for developers experienced in class-based languages (like Java or C++), as it is dynamic and does not provide a class implementation per se (the class keyword is introduced in ES2015, but is syntactical sugar, JavaScript remains prototype-based).

When it comes to inheritance, JavaScript only has one construct: objects. Each object has a private property which holds a link to another object called its prototype . That prototype object has a prototype of its own, and so on until an object is reached with null as its prototype. By definition, null has no prototype, and acts as the final link in this prototype chain .

Nearly all objects in JavaScript are instances of Object which sits on the top of a prototype chain.

While this confusion is often considered to be one of JavaScript's weaknesses, the prototypal inheritance model itself is, in fact, more powerful than the classic model. It is, for example, fairly trivial to build a classic model on top of a prototypal model.

Inheritance with the prototype chain

继承特性

JavaScript objects are dynamic "bags" of properties (referred to as own properties ). JavaScript objects have a link to a prototype object. When trying to access a property of an object, the property will not only be sought on the object but on the prototype of the object, the prototype of the prototype, and so on until either a property with a matching name is found or the end of the prototype chain is reached.

Following the ECMAScript standard, the notation someObject.[[Prototype]] is used to designate the prototype of someObject . Since ECMAScript 2015, the [[Prototype]] is accessed using the accessors Object.getPrototypeOf() and Object.setPrototypeOf() . This is equivalent to the JavaScript property __proto__ which is non-standard but de-facto implemented by many browsers.

It should not be confused with the func .prototype property of functions, which instead specifies the [[Prototype]] to be assigned to all instances of objects created by the given function when used as a constructor. The Object.prototype property represents the Object prototype object.

Here is what happens when trying to access a property:

// Let's create an object o from function f with its own properties a and b:
let f = function () {
   this.a = 1;
   this.b = 2;
}
let o = new f(); // {a: 1, b: 2}
// add properties in f function's prototype
f.prototype.b = 3;
f.prototype.c = 4;
// do not set the prototype f.prototype = {b:3,c:4}; this will break the prototype chain
// o.[[Prototype]] has properties b and c.
// o.[[Prototype]].[[Prototype]] is Object.prototype.
// Finally, o.[[Prototype]].[[Prototype]].[[Prototype]] is null.
// This is the end of the prototype chain, as null,
// by definition, has no [[Prototype]].
// Thus, the full prototype chain looks like:
// {a: 1, b: 2} ---> {b: 3, c: 4} ---> Object.prototype ---> null
console.log(o.a); // 1
// Is there an 'a' own property on o? Yes, and its value is 1.
console.log(o.b); // 2
// Is there a 'b' own property on o? Yes, and its value is 2.
// The prototype also has a 'b' property, but it's not visited.
// This is called Property Shadowing
console.log(o.c); // 4
// Is there a 'c' own property on o? No, check its prototype.
// Is there a 'c' own property on o.[[Prototype]]? Yes, its value is 4.
console.log(o.d); // undefined
// Is there a 'd' own property on o? No, check its prototype.
// Is there a 'd' own property on o.[[Prototype]]? No, check its prototype.
// o.[[Prototype]].[[Prototype]] is Object.prototype and there is no 'd' property by default, check its prototype.
// o.[[Prototype]].[[Prototype]].[[Prototype]] is null, stop searching,
// no property found, return undefined.
			

Code Link

Setting a property to an object creates an own property. The only exception to the getting and setting behavior rules is when there is an inherited property with a getter or a setter .

继承方法

JavaScript does not have "methods" in the form that class-based languages define them. In JavaScript, any function can be added to an object in the form of a property. An inherited function acts just as any other property, including property shadowing as shown above (in this case, a form of method overriding ).

When an inherited function is executed, the value of this points to the inheriting object, not to the prototype object where the function is an own property.

var o = {
  a: 2,
  m: function() {
    return this.a + 1;
  }
};
console.log(o.m()); // 3
// When calling o.m in this case, 'this' refers to o
var p = Object.create(o);
// p is an object that inherits from o
p.a = 4; // creates a property 'a' on p
console.log(p.m()); // 5
// when p.m is called, 'this' refers to p.
// So when p inherits the function m of o,
// 'this.a' means p.a, the property 'a' of p
			

在 JavaScript 中使用原型

Let's look at what happens behind the scenes in a bit more detail.

In JavaScript, as mentioned above, functions are able to have properties. All functions have a special property named prototype . Please note that the code below is free-standing (it is safe to assume there is no other JavaScript on the webpage other than the below code). For the best learning experience, it is highly recommended that you open a console, navigate to the "console" tab, copy-and-paste in the below JavaScript code, and run it by pressing the Enter/Return key. (The console is included in most web browser's Developer Tools. More information is available for Firefox 开发者工具 , Chrome DevTools ,和 Edge DevTools .)

function doSomething(){}
console.log( doSomething.prototype );
//  It does not matter how you declare the function, a
//  function in JavaScript will always have a default
//  prototype property.
//  (Ps: There is one exception that arrow function doesn't have a default prototype property)
var doSomething = function(){};
console.log( doSomething.prototype );
			

As seen above, doSomething() has a default prototype property, as demonstrated by the console. After running this code, the console should have displayed an object that looks similar to this.

{
    constructor: ƒ doSomething(),
    __proto__: {
        constructor: ƒ Object(),
        hasOwnProperty: ƒ hasOwnProperty(),
        isPrototypeOf: ƒ isPrototypeOf(),
        propertyIsEnumerable: ƒ propertyIsEnumerable(),
        toLocaleString: ƒ toLocaleString(),
        toString: ƒ toString(),
        valueOf: ƒ valueOf()
    }
}
			

We can add properties to the prototype of doSomething() , as shown below.

function doSomething(){}
doSomething.prototype.foo = "bar";
console.log( doSomething.prototype );
			

This results in:

{
    foo: "bar",
    constructor: ƒ doSomething(),
    __proto__: {
        constructor: ƒ Object(),
        hasOwnProperty: ƒ hasOwnProperty(),
        isPrototypeOf: ƒ isPrototypeOf(),
        propertyIsEnumerable: ƒ propertyIsEnumerable(),
        toLocaleString: ƒ toLocaleString(),
        toString: ƒ toString(),
        valueOf: ƒ valueOf()
    }
}
			

We can now use the new operator to create an instance of doSomething() based on this prototype. To use the new operator, simply call the function normally except prefix it with new . Calling a function with the new operator returns an object that is an instance of the function. Properties can then be added onto this object.

Try the following code:

function doSomething(){}
doSomething.prototype.foo = "bar"; // add a property onto the prototype
var doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value"; // add a property onto the object
console.log( doSomeInstancing );
			

This results in an output similar to the following:

{
    prop: "some value",
    __proto__: {
        foo: "bar",
        constructor: ƒ doSomething(),
        __proto__: {
            constructor: ƒ Object(),
            hasOwnProperty: ƒ hasOwnProperty(),
            isPrototypeOf: ƒ isPrototypeOf(),
            propertyIsEnumerable: ƒ propertyIsEnumerable(),
            toLocaleString: ƒ toLocaleString(),
            toString: ƒ toString(),
            valueOf: ƒ valueOf()
        }
    }
}
			

As seen above, the __proto__ of doSomeInstancing is doSomething.prototype . But, what does this do? When you access a property of doSomeInstancing , the browser first looks to see if doSomeInstancing has that property.

doSomeInstancing does not have the property, then the browser looks for the property in the __proto__ of doSomeInstancing (a.k.a. doSomething.prototype). If the __proto__ of doSomeInstancing has the property being looked for, then that property on the __proto__ of doSomeInstancing is used.

Otherwise, if the __proto__ of doSomeInstancing does not have the property, then the __proto__ of the __proto__ of doSomeInstancing is checked for the property. By default, the __proto__ of any function's prototype property is window.Object.prototype . So, the __proto__ of the __proto__ of doSomeInstancing (a.k.a. the __proto__ of doSomething.prototype (a.k.a. Object.prototype )) is then looked through for the property being searched for.

If the property is not found in the __proto__ of the __proto__ of doSomeInstancing, then the __proto__ of the __proto__ of the __proto__ of doSomeInstancing is looked through. However, there is a problem: the __proto__ of the __proto__ of the __proto__ of doSomeInstancing does not exist. Then, and only then, after the entire prototype chain of __proto__ 's is looked through, and there are no more __proto__ s does the browser assert that the property does not exist and conclude that the value at the property is undefined .

Let's try entering some more code into the console:

function doSomething(){}
doSomething.prototype.foo = "bar";
var doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value";
console.log("doSomeInstancing.prop:      " + doSomeInstancing.prop);
console.log("doSomeInstancing.foo:       " + doSomeInstancing.foo);
console.log("doSomething.prop:           " + doSomething.prop);
console.log("doSomething.foo:            " + doSomething.foo);
console.log("doSomething.prototype.prop: " + doSomething.prototype.prop);
console.log("doSomething.prototype.foo:  " + doSomething.prototype.foo);
			

This results in the following:

doSomeInstancing.prop:      some value
doSomeInstancing.foo:       bar
doSomething.prop:           undefined
doSomething.foo:            undefined
doSomething.prototype.prop: undefined
doSomething.prototype.foo:  bar
			

Different ways to create objects and the resulting prototype chain

Objects created with syntax constructs

var o = {a: 1};
// The newly created object o has Object.prototype as its [[Prototype]]
// o has no own property named 'hasOwnProperty'
// hasOwnProperty is an own property of Object.prototype.
// So o inherits hasOwnProperty from Object.prototype
// Object.prototype has null as its prototype.
// o ---> Object.prototype ---> null
var b = ['yo', 'whadup', '?'];
// Arrays inherit from Array.prototype
// (which has methods indexOf, forEach, etc.)
// The prototype chain looks like:
// b ---> Array.prototype ---> Object.prototype ---> null
function f() {
  return 2;
}
// Functions inherit from Function.prototype
// (which has methods call, bind, etc.)
// f ---> Function.prototype ---> Object.prototype ---> null
			

With a constructor

A "constructor" in JavaScript is "just" a function that happens to be called with the new operator .

function Graph() {
  this.vertices = [];
  this.edges = [];
}
Graph.prototype = {
  addVertex: function(v) {
    this.vertices.push(v);
  }
};
var g = new Graph();
// g is an object with own properties 'vertices' and 'edges'.
// g.[[Prototype]] is the value of Graph.prototype when new Graph() is executed.
			

With Object.create

ECMAScript 5 introduced a new method: Object.create() . Calling this method creates a new object. The prototype of this object is the first argument of the function:

var a = {a: 1};
// a ---> Object.prototype ---> null
var b = Object.create(a);
// b ---> a ---> Object.prototype ---> null
console.log(b.a); // 1 (inherited)
var c = Object.create(b);
// c ---> b ---> a ---> Object.prototype ---> null
var d = Object.create(null);
// d ---> null
console.log(d.hasOwnProperty);
// undefined, because d doesn't inherit from Object.prototype
			

delete Operator with Object.create and new operator

使用 Object.create of another object demonstrates prototypical inheritance with the delete operation:

var a = {a: 1};
var b = Object.create(a);
console.log(a.a); // print 1
console.log(b.a); // print 1
b.a=5;
console.log(a.a); // print 1
console.log(b.a); // print 5
delete b.a;
console.log(a.a); // print 1
console.log(b.a); // print 1(b.a value 5 is deleted but it showing value from its prototype chain)
delete a.a;
console.log(a.a); // print undefined
console.log(b.a); // print undefined
			

new operator has a shorter chain in this example:

function Graph() {
  this.vertices = [4,4];
}
var g = new Graph();
console.log(g.vertices); // print [4,4]
g.vertices = 25;
console.log(g.vertices); // print 25
delete g.vertices;
console.log(g.vertices); // print undefined
			

With the class keyword

ECMAScript 2015 introduced a new set of keywords implementing . The new keywords include class , 构造函数 , static , extends ,和 super .

'use strict';
class Polygon {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}
class Square extends Polygon {
  constructor(sideLength) {
    super(sideLength, sideLength);
  }
  get area() {
    return this.height * this.width;
  }
  set sideLength(newLength) {
    this.height = newLength;
    this.width = newLength;
  }
}
var square = new Square(2);
			

Performance

The lookup time for properties that are high up on the prototype chain can have a negative impact on the performance, and this may be significant in the code where performance is critical. Additionally, trying to access nonexistent properties will always traverse the full prototype chain.

Also, when iterating over the properties of an object, every enumerable property that is on the prototype chain will be enumerated. To check whether an object has a property defined on itself and not somewhere on its prototype chain, it is necessary to use the hasOwnProperty method which all objects inherit from Object.prototype . To give you a concrete example, let's take the above graph example code to illustrate it:

console.log(g.hasOwnProperty('vertices'));
// true
console.log(g.hasOwnProperty('nope'));
// false
console.log(g.hasOwnProperty('addVertex'));
// false
console.log(g.__proto__.hasOwnProperty('addVertex'));
// true
			

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

Note: It is not enough to check whether a property is undefined . The property might very well exist, but its value just happens to be set to undefined .

Bad practice: Extension of native prototypes

One misfeature 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.js, there is still no good reason for cluttering built-in types with additional non-standard 功能。

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

Summary of methods for extending the prototype chain

Here are all 4 ways and their pros/cons. All of the examples listed below create exactly the same resulting inst object (thus logging the same results to the console), except in different ways for the purpose of illustration.

Name 范例 Pro(s) Con(s)
New-initialization
function foo(){}
foo.prototype = {
  foo_prop: "foo val"
};
function bar(){}
var proto = new foo;
proto.bar_prop = "bar val";
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop);
							
Supported in every browser imaginable (support goes all the way back to IE 5.5!). Also, it is very fast, very standard, and very JIST-optimizable. In order to use this method, the function in question must be initialized. During this initialization, the constructor may store unique information that must be generated per-object. However, this unique information would only be generated once, potentially leading to problems. Additionally, the initialization of the constructor may put unwanted methods onto the object. However, both these are generally not problems at all (in fact, usually beneficial) if it is all your own code and you know what does what where.
Object.create
function foo(){}
foo.prototype = {
  foo_prop: "foo val"
};
function bar(){}
var proto = Object.create(
  foo.prototype
);
proto.bar_prop = "bar val";
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop);
							
function foo(){}
foo.prototype = {
  foo_prop: "foo val"
};
function bar(){}
var proto = Object.create(
  foo.prototype,
  {
    bar_prop: {
      value: "bar val"
    }
  }
);
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop)
							
Support in all in-use-today browsers which are all non-microsoft browsers plus IE9 and up. Allows the direct setting of __proto__ in a way that is one-time-only so that the browser can better optimize the object. Also allows the creation of objects without a prototype via Object.create(null) . Not supported in IE8 and below. However, as Microsoft has discontinued extended support for systems running these old browsers, this should not be a concern for most applications. Additionally, the slow object initialization can be a performance black hole if using the second argument because each object-descriptor property has its own separate descriptor object. When dealing with hundreds of thousands of object descriptors in the form of object, there can arise a serious issue with lag.

Object.setPrototypeOf

function foo(){}
foo.prototype = {
  foo_prop: "foo val"
};
function bar(){}
var proto = {
  bar_prop: "bar val"
};
Object.setPrototypeOf(
  proto, foo.prototype
);
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop);
							
function foo(){}
foo.prototype = {
  foo_prop: "foo val"
};
function bar(){}
var proto;
proto=Object.setPrototypeOf(
  { bar_prop: "bar val" },
  foo.prototype
);
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop)
							
Support in all in-use-today browsers which are all non-microsoft browsers plus IE9 and up. Allows the dynamic manipulation of an objects prototype and can even force a prototype on a prototype-less object created with Object.create(null) . Should-be-deprecated and ill-performant. Making your Javascript run fast is completely out of the question if you dare use this in the final production code because many browsers optimize the prototype and try to guess the location of the method in the memory when calling an instance in advance, but setting the prototype dynamically disrupts all these optimizations and can even force some browsers to recompile for deoptimization your code just to make it work according to the specs. Not supported in IE8 and below.
__proto__
function foo(){}
foo.prototype = {
  foo_prop: "foo val"
};
function bar(){}
var proto = {
  bar_prop: "bar val",
  __proto__: foo.prototype
};
bar.prototype = proto;
var inst = new bar;
console.log(inst.foo_prop);
console.log(inst.bar_prop);
							
var inst = {
  __proto__: {
    bar_prop: "bar val",
    __proto__: {
      foo_prop: "foo val",
      __proto__: Object.prototype
    }
  }
};
console.log(inst.foo_prop);
console.log(inst.bar_prop)
							
Support in all in-use-today browsers which are all non-microsoft browsers plus IE11 and up. Setting __proto__ to something that is not an object only fails silently. It does not throw an exception. Grossly deprecated and non-performant. Making your Javascript run fast is completely out of the question if you dare use this in the final production code because many browsers optimize the prototype and try to guess the location of the method in the memory when calling an instance in advance, but setting the prototype dynamically disrupts all these optimizations and can even force some browsers to recompile for deoptimization your code just to make it work according to the specs. Not supported in IE10 and below.

prototype and Object.getPrototypeOf

JavaScript is a bit confusing for developers coming from Java or C++, as it's all dynamic, all runtime, and it has no classes at all. It's all just instances (objects). Even the "classes" we simulate are just a function object.

You probably already noticed that our function A has a special property called prototype . This special property works with the JavaScript new operator. The reference to the prototype object is copied to the internal [[Prototype]] property of the new instance. For example, when you do var a1 = new A() , JavaScript (after creating the object in memory and before running function A() with this defined to it) sets a1.[[Prototype]] = A.prototype . When you then access properties of the instance, JavaScript first checks whether they exist on that object directly, and if not, it looks in [[Prototype]] . This means that all the stuff you define in prototype is effectively shared by all instances, and you can even later change parts of prototype and have the changes appear in all existing instances, if you wanted to.

If, in the example above, you do var a1 = new A(); var a2 = new A(); then a1.doSomething would actually refer to Object.getPrototypeOf(a1).doSomething , which is the same as the A.prototype.doSomething you defined, i.e. Object.getPrototypeOf(a1).doSomething == Object.getPrototypeOf(a2).doSomething == A.prototype.doSomething .

In short, prototype is for types, while Object.getPrototypeOf() is the same for instances.

[[Prototype]] is looked at recursively , i.e. a1.doSomething , Object.getPrototypeOf(a1).doSomething , Object.getPrototypeOf(Object.getPrototypeOf(a1)).doSomething etc., until it's found or Object.getPrototypeOf returns null.

So, when you call

var o = new Foo();
			

JavaScript actually just does

var o = new Object();
o.[[Prototype]] = Foo.prototype;
Foo.call(o);
			

(or something like that) and when you later do

o.someProp;
			

it checks whether o has a property someProp . If not, it checks Object.getPrototypeOf(o).someProp , and if that doesn't exist it checks Object.getPrototypeOf(Object.getPrototypeOf(o)).someProp , and so on.

结论

It is essential to understand the prototypal inheritance model before writing complex code that makes use of it. Also, be aware of the length of the prototype chains in your code and break them up if necessary to avoid possible performance problems. Further, the native prototypes should never be extended unless it is for the sake of compatibility with newer JavaScript features.

Metadata

  1. JavaScript
  2. 教程:
  3. 完整初学者
    1. JavaScript 基础
    2. JavaScript 第一步
    3. JavaScript 构建块
    4. 引入 JavaScript 对象
  4. JavaScript 指南
    1. 介绍
    2. 语法和类型
    3. 控制流程和错误处理
    4. 循环和迭代
    5. 函数
    6. 表达式和运算符
    7. 数字和日期
    8. 文本格式
    9. 正则表达式
    10. Indexed collections
    11. Keyed collections
    12. Working with objects
    13. 对象模型的细节
    14. Using promises
    15. 迭代器和生成器
    16. Meta programming
    17. JavaScript 模块
  5. 中间体
    1. Client-side JavaScript frameworks
    2. 客户端侧 Web API
    3. 重新介绍 JavaScript
    4. JavaScript 数据结构
    5. 相等比较和相同
    6. 闭包
  6. 高级
    1. 继承和原型链
    2. 严格模式
    3. JavaScript 类型数组
    4. 内存管理
    5. 并发模型和事件循环
  7. 参考:
  8. 内置对象
    1. AggregateError
    2. Array
    3. ArrayBuffer
    4. AsyncFunction
    5. Atomics
    6. BigInt
    7. BigInt64Array
    8. BigUint64Array
    9. Boolean
    10. DataView
    11. Date
    12. Error
    13. EvalError
    14. Float32Array
    15. Float64Array
    16. Function
    17. Generator
    18. GeneratorFunction
    19. Infinity
    20. Int16Array
    21. Int32Array
    22. Int8Array
    23. InternalError
    24. Intl
    25. JSON
    26. Map
    27. Math
    28. NaN
    29. Number
    30. Object
    31. Promise
    32. Proxy
    33. RangeError
    34. ReferenceError
    35. Reflect
    36. RegExp
    37. Set
    38. SharedArrayBuffer
    39. String
    40. Symbol
    41. SyntaxError
    42. TypeError
    43. TypedArray
    44. URIError
    45. Uint16Array
    46. Uint32Array
    47. Uint8Array
    48. Uint8ClampedArray
    49. WeakMap
    50. WeakSet
    51. WebAssembly
    52. decodeURI()
    53. decodeURIComponent()
    54. encodeURI()
    55. encodeURIComponent()
    56. escape()
    57. eval()
    58. globalThis
    59. isFinite()
    60. isNaN()
    61. null
    62. parseFloat()
    63. parseInt()
    64. undefined
    65. unescape()
    66. uneval()
  9. 表达式 & 运算符
    1. 算术运算符
    2. 赋值运算符
    3. Bitwise operators
    4. 逗号运算符
    5. Comparison operators
    6. 条件 (三元) 运算符
    7. Destructuring assignment
    8. Function expression
    9. Grouping operator
    10. Logical operators
    11. Nullish coalescing operator
    12. Object initializer
    13. 运算符优先级
    14. Optional chaining
    15. Pipeline operator
    16. 特性访问器
    17. 传播句法
    18. 异步函数表达式
    19. await
    20. class expression
    21. delete operator
    22. function* 表达式
    23. in operator
    24. instanceof
    25. new operator
    26. new.target
    27. super
    28. this
    29. typeof
    30. void 运算符
    31. yield
    32. yield*
  10. 语句 & 声明
    1. async function
    2. block
    3. break
    4. class
    5. const
    6. continue
    7. debugger
    8. do...while
    9. empty
    10. export
    11. for
    12. for await...of
    13. for...in
    14. for...of
    15. 函数声明
    16. function*
    17. if...else
    18. import
    19. import.meta
    20. label
    21. let
    22. return
    23. switch
    24. throw
    25. try...catch
    26. var
    27. while
    28. with
  11. 函数
    1. 箭头函数表达式
    2. 默认参数
    3. 方法定义
    4. 其余参数
    5. 自变量对象
    6. getter
    7. setter
    1. Class fields
    2. 构造函数
    3. extends
    4. static
  12. 错误
    1. Error: Permission denied to access property "x"
    2. InternalError: too much recursion
    3. RangeError: argument is not a valid code point
    4. RangeError: invalid array length
    5. RangeError: invalid date
    6. RangeError: precision is out of range
    7. RangeError: radix must be an integer
    8. RangeError: repeat count must be less than infinity
    9. RangeError: repeat count must be non-negative
    10. ReferenceError: "x" is not defined
    11. ReferenceError: assignment to undeclared variable "x"
    12. ReferenceError: can't access lexical declaration`X' before initialization
    13. ReferenceError: deprecated caller or arguments usage
    14. ReferenceError: invalid assignment left-hand side
    15. ReferenceError: reference to undefined property "x"
    16. SyntaxError: "0"-prefixed octal literals and octal escape seq. are deprecated
    17. SyntaxError: "use strict" not allowed in function with non-simple parameters
    18. SyntaxError: "x" is a reserved identifier
    19. SyntaxError: JSON.parse: bad parsing
    20. SyntaxError: Malformed formal parameter
    21. SyntaxError: Unexpected token
    22. SyntaxError: Using //@ to indicate sourceURL pragmas is deprecated. Use //# instead
    23. SyntaxError: a declaration in the head of a for-of loop can't have an initializer
    24. SyntaxError: applying the 'delete' operator to an unqualified name is deprecated
    25. SyntaxError: for-in loop head declarations may not have initializers
    26. SyntaxError: function statement requires a name
    27. SyntaxError: identifier starts immediately after numeric literal
    28. SyntaxError: illegal character
    29. SyntaxError: invalid regular expression flag "x"
    30. SyntaxError: missing ) after argument list
    31. SyntaxError: missing ) after condition
    32. SyntaxError: missing : after property id
    33. SyntaxError: missing ; before statement
    34. SyntaxError: missing = in const declaration
    35. SyntaxError: missing ] after element list
    36. SyntaxError: missing formal parameter
    37. SyntaxError: missing name after . operator
    38. SyntaxError: missing variable name
    39. SyntaxError: missing } after function body
    40. SyntaxError: missing } after property list
    41. SyntaxError: redeclaration of formal parameter "x"
    42. SyntaxError: return not in function
    43. SyntaxError: test for equality (==) mistyped as assignment (=)?
    44. SyntaxError: unterminated string literal
    45. TypeError: "x" has no properties
    46. TypeError: "x" is (not) "y"
    47. TypeError: "x" is not a constructor
    48. TypeError: "x" is not a function
    49. TypeError: "x" is not a non-null object
    50. TypeError: "x" is read-only
    51. TypeError: 'x' is not iterable
    52. TypeError: More arguments needed
    53. TypeError: Reduce of empty array with no initial value
    54. TypeError: X.prototype.y called on incompatible type
    55. TypeError: can't access dead object
    56. TypeError: can't access property "x" of "y"
    57. TypeError: can't assign to property "x" on "y": not an object
    58. TypeError: can't define property "x": "obj" is not extensible
    59. TypeError: can't delete non-configurable array element
    60. TypeError: can't redefine non-configurable property "x"
    61. TypeError: cannot use 'in' operator to search for 'x' in 'y'
    62. TypeError: cyclic object value
    63. TypeError: invalid 'instanceof' operand 'x'
    64. TypeError: invalid Array.prototype.sort argument
    65. TypeError: invalid arguments
    66. TypeError: invalid assignment to const "x"
    67. TypeError: property "x" is non-configurable and can't be deleted
    68. TypeError: setting getter-only property "x"
    69. TypeError: variable "x" redeclares argument
    70. URIError: malformed URI sequence
    71. Warning: -file- is being assigned a //# sourceMappingURL, but already has one
    72. Warning: 08/09 is not a legal ECMA-262 octal constant
    73. Warning: Date.prototype.toLocaleFormat is deprecated
    74. Warning: JavaScript 1.6's for-each-in loops are deprecated
    75. Warning: String.x is deprecated; use String.prototype.x instead
    76. Warning: expression closures are deprecated
    77. Warning: unreachable code after return statement
  13. 杂项
    1. JavaScript technologies overview
    2. 词汇语法
    3. JavaScript 数据结构
    4. Enumerability and ownership of properties
    5. Iteration protocols
    6. 严格模式
    7. Transitioning to strict mode
    8. Template literals
    9. 弃用特征