This commit is contained in:
Ilya Kantor 2016-11-14 23:41:18 +03:00
parent f99574f53b
commit b0976b5253
153 changed files with 590 additions and 533 deletions

View file

@ -0,0 +1,44 @@
We can use such approach if we are sure that `"constructor"` property has the correct value.
For instance, if we don't touch the default `"prototype"`, then this code works for sure:
```js run
function User(name) {
this.name = name;
}
let user = new User('John');
let user2 = new user.constructor('Pete');
alert( user2.name ); // Pete (worked!)
```
It worked, because `User.prototype.constructor == User`.
..But if someone, so to say, overwrites `User.prototype` and forgets to recreate `"constructor"`, then it would fail.
For instance:
```js run
function User(name) {
this.name = name;
}
*!*
User.prototype = {}; // (*)
*/!*
let user = new User('John');
let user2 = new user.constructor('Pete');
alert( user2.name ); // undefined
```
Why `user2.name` is `undefined`?
Here's how `new user.constructor('Pete')` works:
1. First, it looks for `constructor` in `user`. Nothing.
2. Then it follows the prototype chain. The prototype of `user` is `User.prototype`, and it also has nothing.
3. The value of `User.prototype` is a plain object `{}`, it's prototype is `Object.prototype`. And there is `Object.prototype.constructor == Object`. So it is used.
At the end, we have `let user2 = new Object('Pete')`. The built-in `Object` constructor ignores arguments, it always creates an empty object -- that's what we have in `user2` after all.

View file

@ -0,0 +1,15 @@
importance: 5
---
# Create an object with the same constructor
Imagine, we have an arbitrary object `obj`, created by a constructor function -- we don't know which one, but we'd like to create a new object using it.
Can we do it like that?
```js
let obj2 = new obj.constructor();
```
Give an example of a constructor function for `obj` which lets such code work right. And an example that makes it work wrong.

View file

@ -0,0 +1,89 @@
# The "constructor" property
[todo make me more interesting]
Every function by default already has the `"prototype"` property.
It's an object of this form:
```js
function Rabbit() {}
Rabbit.prototype = {
constructor: Rabbit
};
```
Here, `Rabbit.prototype` is assigned manually, but the same object is its value by default.
We can check it:
```js
function Rabbit() {}
// Rabbit.prototype = { constructor: Rabbit }
alert( Rabbit.prototype.constructor == Rabbit ); // true
```
Here's the picture:
![](function-prototype-constructor.png)
Naturally, the `"constructor"` property becomes available to all rabbits through the `[[Prototype]]`:
```js run
function Rabbit() {}
let rabbit = new Rabbit();
alert(rabbit.constructor == Rabbit); // true
```
![](rabbit-prototype-constructor.png)
We can use it to create a new object using the same constructor as the existing one:
```js run
function Rabbit(name) {
this.name = name;
alert(name);
}
let rabbit = new Rabbit("White Rabbit");
let rabbit2 = new rabbit.constructor("Black Rabbit");
```
That may come in handy when we have an object, but don't know which constructor was used for it (e.g. it comes from a 3rd party library), and we need to create the same.
Probably the most important thing about `"constructor"` is that...
**JavaScript itself does not use the `"constructor"` property at all.**
Yes, it exists in the default `"prototype"` for functions, but that's literally all about it. No language function relies on it and nothing controls its validity.
It is created automatically, but what happens with it later -- is totally on us.
In particular, if we assign our own `Rabbit.prototype = { jumps: true }`, then there will be no `"constructor"` in it any more.
Such assignment won't break native methods or syntax, because nothing in the language uses the `"constructor"` property. But we may want to keep `"constructor"` for convenience or just in case, by adding properties to the default `"prototype"` instead of overwriting it as a whole:
```js
function Rabbit() {}
// Not overwrite Rabbit.prototype totally
// just add to it
Rabbit.prototype.jumps = true
// the default Rabbit.prototype.constructor is preserved
```
Or, alternatively, recreate it manually:
```js
Rabbit.prototype = {
jumps: true,
*!*
constructor: Rabbit
*/!*
};
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB