Speaking of JavaScript's prototype chain, there is a legendary ancient diagram:

(Image from JavaScript Object Layout)
Starting from the Relationships in the Diagram
Two groups for functions, three groups for objects, plus two derived groups—there are a total of 7 groups of relationships in the diagram.
First, let's look at functions:
/**
* 函数的两组关系
*/
function Cat() {}
// 第零组关系
// 实例的constructor 是 构造函数
new Cat().constructor === Cat
// 构造函数的constructor 是 创建构造函数的东西
Cat.constructor === Function
// 第一组关系:实例的__proto__ 指向 构造函数的prototype
// 这组很容易记住,想想原型链继承的关键操作
new Cat().__proto__ === Cat.prototype
// 第二组关系:构造函数的prototype的constructor 还是自身
Cat.prototype.constructor === Cat
Next, let's look at objects:
/**
* 对象的三组关系
*/
// 第一组关系仍然成立
new Object().__proto__ === Object.prototype
// 第二组关系仍然成立
Object.prototype.constructor === Object
// 第三组关系:Object的prototype的__proto__ 是 null
Object.prototype.__proto__ === null
Finally, the derived ones:
/**
* 衍生的两组关系
*/
// 第四组关系:构造函数是new Function得到的
// 所以 构造函数的__proto__ 指向 Function的prototype
Cat.__proto__ === Function.prototype
// 同上
Object.__proto__ === Function.prototype
// 第五组关系:本来也应该同上,太变态了单独拿出来
// Function也是new Function得到的!!
// Function.constructor === Function 也就是说Function的构造函数是它自己,所以
// Function的__proto__ 指向 Function的prototype
Function.__proto__ === Function.prototype
// 第六组关系:Function的prototype的__proto__ 是 Object的prototype
Function.prototype.__proto__ === Object.prototype
How to Understand the Triangular Relationship of prototype, __proto__, and constructor?
Understand it like this:
- A constructor is a machine that creates instances.
- What properties an instance gains during creation depends on what properties are hidden in the machine.
- This bunch of properties is hidden in the
prototypeproperty of the constructor (called the prototype object). - When
newing an instance, the__proto__is attached to the instance so that it can trace back to this bunch of properties.
Special cases:
- A constructor and its
prototypehave a two-way relationship; the property that points back is calledconstructor.
How to Distinguish Between prototype and __proto__?
Essentially, there is only one key difference: who uses it.
prototypeis the prototype object for use by instances; only constructors have aprototype.__proto__is a property that points to an object's own prototype object; all objects have a__proto__.- The difference between
x.prototypeandx.__proto__is that the former is for use by instances (it's useless unless younew x), while the latter is for the object's own use.
Both deal with prototypes, so how to distinguish the names?
prototypehides a bunch of properties for use by (subclass) instances; let's call it the prototype object.__proto__links the prototype chain together; let's tentatively call it the prototype pointer.
How Does instanceof Determine the Relationship Between an Instance and a Class (Constructor)?
MDN has a sentence that explains instanceof exceptionally accurately:
The instanceof operator tests to see if the prototype property of a constructor appears anywhere in the prototype chain of an object.
(Excerpted from instanceof)
I won't translate it, as changing even one word seems redundant. According to this sentence, we can implement an instanceof ourselves:
function insOf(obj, Ctor) {
let proto = obj;
// while (proto = obj.__proro__) {
while (proto = Object.getPrototypeOf(proto)) {
if (Ctor.prototype === proto) {
return true;
}
}
return false;
}
// Case 1
class A {}
insOf(new A(), A)
// Case 2
function Cat() {}
insOf(new Cat(), Cat)
insOf(new Cat(), Object)
A Bit More Depth into constructor and Inheritance
From the 0th group of relationships, we know that an instance's constructor is the constructor:
// 实例的constructor 是 构造函数
new Cat().constructor === Cat
So where does the instance's constructor property come from?
That's right, it is copied from the constructor's prototype object when new Cat() creates the instance:
new Cat().constructor === Cat.prototype.constructor
Therefore, in classic ES5 inheritance:
function extend(Sub, Super) {
// 1.把Super.prototype包进一个匿名对象的原型,返回这个匿名对象
var proto = Object.create(Super.prototype);
// 2.修正constructor属性
proto.constructor = Sub;
// 3.让子类实例获得匿名对象原型属性的访问权
Sub.prototype = proto;
}
Changing or not changing the constructor pointer doesn't affect the result of the instanceof operator; changing it is just to make the constructor property value correct:
// 如果不改constructor,子类实例的constructor仍然是A,而不是B
function Sub() {}
function Super() {}
var proto = Object.create(Super.prototype);
Sub.prototype = proto;
// 这就会显得很奇怪(实例的constructor不是构造函数了)
new Sub().constructor !== Sub;
// 所以,改过来
proto.constructor = Sub;
// 正常了
new Sub().constructor === Sub;
Some Trivia
1. Arrow functions do not have a prototype object (so arrow functions cannot be used as constructors)
(() => 1).prototype === undefined
2. Don't look too deeply into the prototypes of native objects; they aren't very consistent
Math.__proto__ === Object.prototype
Math.max.__proto__ === Function.prototype
Window.__proto__ === EventTarget
console.__proto__ === ?
3. What is the use of comparing a function's prototype?
In the highly downloaded is module on npm, there is a line of ancient code:
if (type === '[object Function]') {
return value.prototype === other.prototype;
}
You're not misreading; it compares the prototype of two functions to judge their equality. A quick thought tells you it's not very reliable, for example:
(x => x).prototype === (x => x + 1).prototype === undefined
So, I asked the library author. After a few rounds, I found that comparing prototype can be used to determine if two constructors/Classes are the same (but note that prototype is mutable):
class Dog {}
class Cat {}
function Button() {}
function Panel() {}
Dog.prototype !== Cat.prototype
Button.prototype !== Panel.prototype
If a function is not a constructor or a Class, comparing the prototype is meaningless. In JavaScript where OOP is not very prevalent, comparing a function's prototype in a library has very limited use.
No comments yet. Be the first to share your thoughts.