In Javascript, the difference between "Object.create" and "new"

I think the difference is in my head, but I just wanted to be sure.

On the Douglas Crockford Prototype Inheritance page in JavaScript , he says

In a prototype system, objects are inherited from objects. JavaScript, however, lacks a statement that performs this operation. Instead, it has a new statement such that the new f () creates a new object that inherits from f.prototype.

I did not quite understand what he was trying to say in this sentence, so I did some tests. It seems to me that the key difference is that if I create an object based on another object in a clean prototype system, then all the parent elements should be on the prototype of the new object, and not on the newest object.

Here's the test:

var Person = function(name, age) { this.name = name; this.age = age; } Person.prototype.toString = function(){return this.name + ', ' + this.age}; // The old way... var jim = new Person("Jim",13); for (n in jim) { if (jim.hasOwnProperty(n)) { console.log(n); } } // This will output 'name' and 'age'. // The pure way... var tim = Object.create(new Person("Tim",14)); for (n in tim) { if (tim.hasOwnProperty(n)) { console.log(n); } } // This will output nothing because all the members belong to the prototype. // If I remove the hasOwnProperty check then 'name' and 'age' will be output. 

As far as I understand, the difference only becomes apparent when testing members on the object itself?

+6
source share
2 answers

Your assumptions are true, but there is another picture that Douglas does not say much - the prototype can also be used for properties. Your human class might be written as:

 var Person = function(name, age) { this.name = name; this.age = age; } Person.prototype.name = null; //default value if you don't init in ctor Person.prototype.age = null; Person.prototype.gender = "male"; Person.prototype.toString = function(){return this.name + ', ' + this.age;}; 

In this case, repeating the properties of an instance of this class, as in your example, will not lead to an exit for the "gender" property.

EDIT 1: Assigning a name and age in the constructor makes the properties visible hasOwnProperty (thanks @matt to remind me of this). An unrecognized gender property will not be visible until someone sets it in an instance.

EDIT 2: To add to this, I present an alternative inheritance pattern - one that I personally used for very large projects:

 var inherits = function(childCtor, parentCtor) { function tempCtor() {}; tempCtor.prototype = parentCtor.prototype; childCtor.superclass = parentCtor.prototype; childCtor.prototype = new tempCtor(); childCtor.prototype.constructor = childCtor; }; var Person = function(name){ this.name = name; } Person.prototype.name = ""; Person.prototype.toString = function(){ return "My name is " + this.name; } var OldPerson = function(name, age){ OldPerson.superclass.constructor.call(this); this.age = age }; inherits(OldPerson, Person); OldPerson.prototype.age = 0; OldPerson.prototype.toString = function(){ var oldString = OldPerson.superclass.toString.call(this); return oldString + " and my age is " + this.age; } 

This is a fairly common template with a slight twist - the parent class is attached to the child via the superclass property, allowing you to access methods / properties overridden by the child. Technically, you can replace OldPerson.superclass with Person , however this is not ideal. If you have ever changed OldPerson to inherit from a class other than Person, you will also need to update all references to Person.

EDIT 3: To bring this complete circle, here is the version of the inherits function that uses Object.create and performs the functions exactly as I described earlier:

 var inherits = function(childCtor, parentCtor) { childCtor.prototype = Object.create(parentCtor.prototype); childCtor.superclass = parentCtor.prototype; }; 
+3
source

EDIT: This answer was originally a response to @ jordancpaul's answer, which he has since corrected. I will leave a part of my answer that will help explain the important difference between prototype properties and instance properties:

In some cases, properties are shared among all instances, and you need to be very careful when you declare prototype properties. Consider this example:

Person.prototype.favoriteColors = []; //Do not do this!

Now, if you create a new instance of Person using either Object.create or new , it does not work as you might expect ...

 var jim = new Person("Jim",13); jim.favoriteColors.push('red'); var tim = new Person("Tim",14); tim.favoriteColors.push('blue'); console.log(tim.favoriteColors); //outputs an array containing red AND blue! 

This does not mean that you can never declare the properties of a prototype, but if you do, you and every developer who works on your code should be aware of this trap. In this case, if you prefer to declare the properties of the prototype for any reason, you can do:

Person.prototype.favoriteColors = null

And initialize it with an empty array in the constructor:

 var Person = function(name, age) { ... this.favoriteColors = []; } 

The general rule when using this method is that the default values ​​for simple literal properties (strings, numbers, logical values) can be set directly on the prototype, but any property that inherits from Object (including arrays and dates) must be set to null and then initialized in the constructor.

A safer way is to only declare methods in the prototype and always declare properties in the constructor.

Anyway, the question was about Object.create ...

The first argument passed to Object.create is set as the prototype of the new instance. Best use:

 var person = { initialize: function(name, age) { this.name = name; this.age = age; return this; }, toString: function() { return this.name + ', ' + this.age; } }; var tim = Object.create(person).initialize("Tim",14); 

Now the result will be the same as in your first example.

As you can see, this is a different philosophical approach from the more classic OOP style in Javascript. With Object.create, the emphasis is on creating new objects from existing objects, not on the constructor. Initialization becomes a separate step.

Personally, I have mixed feelings about the Object.create approach; this is very nice for inheritance because of the second parameter, which you can use to add additional properties to an existing prototype, but it is also more detailed and makes instance checks no longer work (an alternative in this example would be to check person.isPrototypeOf(tim) ).

The main reason I say that Object.create is verbose due to the second parameter, but there are some useful libraries that address:

https://github.com/Gozala/selfish

https://github.com/Raynos/pd

(other)

Hope this was more informative than entanglement!

+3
source

Source: https://habr.com/ru/post/914404/


All Articles