文章目錄
Is this possible?
|
|
Prototypes explain what happens in this puzzle.
More importantly, prototypes are at the heart of several other JavaScript features. Occasionally people neglect learning them because they seem too unusual. However, the core idea is remarkably simple.
Prototypes
|
|
we can instruct JavaScript to continue searching for our missing property on another object. We can do it with one line of code:
What is that mysterious __proto__
property?
It represents the JavaScript concept of a prototype. Any JavaScript object may choose another object as a prototype.
|
|
let’s think of it as a special __proto__
wire
Any JavaScript object may choose another object as a prototype.
We will discuss what that means in practice very soon. For now, let’s think of it as a special __proto__
wire:
Prototypes in Action
|
|
The Prototype Chain
|
|
Shadowing
|
|
hasOwnProperty( )
If you ever want to check if an object has its own property wire with a certain name, you can call a built-in function called hasOwnProperty
. It returns true
for “own” properties, and does not look at the prototypes. In our last example, both objects have their own teeth
wires, so it is true
for both:
|
|
Assignment
|
|
|
|
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:
-
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
Try running this in your browser’s console:
|
|
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.
This explains why the JavaScript objects seem to have “built-in” properties:
|
|
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
We’ve just learned that all objects created with the {} syntax have the special __proto__
wire set to a default Object Prototype. But we also know that we can customize the __proto__
. You might wonder: can we set it to null?
|
|
The answer is yes — 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:
|
|
You won’t often want to create objects like this, if at all. However, the Object Prototype itself is exactly such an object. It is an object with no prototype.
Polluting the Prototype
Now we know that all JavaScript objects get the same prototype by default. Let’s briefly revisit our example from the module about Mutation:
If JavaScript searches for missing properties on the prototype, and most objects share the same prototype, can we make new properties “appear” on all objects by mutating that prototype? The answer is yes!
|
|
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:
|
|
Mutating a shared prototype like we just did is called prototype pollution.
In the past, prototype pollution was a popular way to extend JavaScript with custom features.
However, over the years the web community realized that it is fragile and makes it hard to add new language features. Prefer to avoid it.
__proto__
跟 prototype 沒關係
You might be wondering: what in the world is the prototype
property? You might have seen prototype in the docs, e.g. in the MDN page titles.
I have bad news: the prototype
property is almost entirely unrelated to the core mechanism of prototypes (which, as you might recall, are __proto__
).
prototype 屬性是跟 new
關鍵字有關係
The prototype
property is mostly relevant to explaining the new
operator.
I believe that this single unfortunate naming choice is the primary reason why so many people are confused by prototypes and give up on learning them.
Why Does This Matter?
You might be wondering: why care about prototypes at all? Will you use them much?
In practice, you probably won’t use them directly.
Don’t get into the habit of writing __proto__
.
These examples only illustrated the mechanics. (In fact, even using the __proto__
syntax directly itself is discouraged.)
Prototypes are a bit unusual, and most people and frameworks never really fully embraced them as a paradigm.
Instead, people often used prototypes as mere building blocks for a traditional “class inheritance” model that’s popular in other programming languages.
In fact, it was so common that JavaScript added a class syntax as a convention that “hides” prototypes out of sight.
till, you will notice prototypes hiding “beneath the surface” of classes and other JavaScript features. For example, here is a snippet of a JavaScript class rewritten with __proto__
to demonstrate what’s happening under the hood.
Personally, I don’t use a lot of classes in my daily coding, and I rarely deal with prototypes directly either. However, it helps to know how those features build on each other, and what happens when I read or set a property on an object.