Just-Javascript-Reading-Notes icon indicating copy to clipboard operation
Just-Javascript-Reading-Notes copied to clipboard

[Just JavaScript] 09. Prototypes

Open allenGKC opened this issue 4 years ago • 2 comments

09. Prototypes

Prototypes

let human = {
  teeth: 32
};

let gwen = {
  age: 19
};

According to the rules we’ve learned, if we read it, we get undefined:

console.log(gwen.teeth); // undefined

But we can modify the codes like this:

let human = {
  teeth: 32
};

let gwen = {
  // We added this line:
  __proto__: human,
  age: 19
};

Prototypes in Action

Thanks to that __proto__ : human line, the answer is different now:

let human = {
  teeth: 32
};

let gwen = {
  // "Look for other properties here"
  __proto__: human,
  age: 19
};

console.log(gwen.teeth); // 32

The Prototype Chain

A prototype isn’t a special “thing” in JavaScript. A prototype is more like a relationship. An object may point at another object as its prototype.

let mammal = {
  brainy: true,
};

let human = {
  __proto__: mammal,
  teeth: 32
};

let gwen = {
  __proto__: human,
  age: 19
};

console.log(gwen.brainy); // true

This sequence of objects to “visit” is known as our object’s prototype chain.

Shadowing

let human = {
  teeth: 32
};

let gwen = {
  __proto__: human,
  // This object has its own teeth property:
  teeth: 31
};

console.log(human.teeth); // 32
console.log(gwen.teeth); // 31

In other words, once we find our property, we stop the search.

Assignment

Consider this example:

let human = {
  teeth: 32
};

let gwen = {
  __proto__: human,
  // Note: no own teeth property
};

gwen.teeth = 31;

console.log(human.teeth); // ?
console.log(gwen.teeth); // ?

Before the assignment, both expressions result in 32:

Then we need to execute this assignment:

gwen.teeth = 31;

So gwen.teeth = 31 creates a new own property called teeth on the object that gwen points at. It doesn’t have any effect on the prototype:

The result is:

console.log(human.teeth); // 32
console.log(gwen.teeth); // 31

When we read a property that doesn’t exist on our object, then we’ll keep looking for it on the prototype chain. If we don’t find it, we get undefined. But when we write a property that doesn’t exist on our object, that will create that property on our object. Generally saying, prototypes will not play a role.

The Object Prototype

let obj = {};
console.log(obj.__proto__); // Play with it!

Surprisingly, obj.__proto__ is not null or undefined! Instead, you’ll see a curious object with a bunch of properties, including hasOwnProperty.

We’re going to call that special object the Object Prototype:

At first, this might be a bit mindblowing. Let that sink in. All this time we were thinking that {} creates an “empty” object. But it’s not so empty, after all! It has a hidden __proto__ wire that points at the Object Prototype by default.

let human = {
  teeth: 32
};
console.log(human.hasOwnProperty); // (function)
console.log(human.toString); // // (function)

These “built-in” properties are nothing more than normal properties that exist on the Object Prototype. Our object’s prototype is the Object Prototype, which is why we can access them. (Their implementations are inside the JS engine.)

An Object with No Prototype

let weirdo = {
  __proto__: null
};

This will produce an object that truly doesn’t have a prototype, at all. As a result, it doesn’t even have built-in object methods:

console.log(weirdo.hasOwnProperty); // undefined
console.log(weirdo.toString); // undefined

Polluting the Prototype

let obj = {};
obj.__proto__.smell = 'banana';

We mutated the Object Prototype by adding a smell property to it. As a result, both detectives now appear to be using a banana-flavored perfume:

console.log(sherlock.smell); // "banana"
console.log(watson.smell); // "banana"

Mutating a shared prototype like we just did is called prototype pollution.

Recap

  • When reading obj.prop, if obj doesn’t have a prop property, JavaScript will look for obj.__proto__.prop, then it will look for obj.__proto__.__proto__.prop, and so on, until it either finds our property or reaches the end of the prototype chain.
  • When writing to obj.prop, JavaScript will usually write to the object directly instead of traversing the prototype chain.
  • We can use obj.hasOwnProperty('prop') to determine whether our object has an own property called prop. In other words, it means there is a property wire called prop attached to that object directly.
  • We can “pollute” a prototype shared by many objects by mutating it. We can even do this to the Object Prototype — the default prototype for {} objects! But we shouldn’t do that unless we’re pranking our colleagues.
  • You probably won’t use prototypes much directly in practice. However, they are fundamental to how JavaScript objects work, so it is handy to understand their underlying mechanics. Some advanced JavaScript features, including classes, can be expressed in terms of prototypes.

allenGKC avatar Jul 09 '20 14:07 allenGKC