The title may sound a bit confusing. Please allow me to cast Crockford's constructor into the following simple example and create objects using two different ways. (Browser is FireFox.)
var car = function(carSpec) {
var maker = carSpec.maker;
var year = carSpec.year;
var that = {};
that.getMaker = function () {
return maker;
};
that.getYear = function () {
return year;
};
return that;
};
One way to create an object, as Crockford pointed out, is to use Object.create method,
myCar = Object.create(car({maker: 'Nissan', year: 2004}));
console.log(myCar); // Object {}, on FireFox console.
and the methods getMaker and getYear are attached to the __proto__.
The other way is to invoke car and let it return an object
yourCar = car({maker: 'Ford', year: 2010});
console.log(yourCar); // Object { getMaker: car/that.getMaker(), getYear: car/that.getYear() }
and methods getMaker and getYear becomes the own properties of object yourCar.
My questions is: What are the pros and cons of these two ways of object creation from this "Crockford constructor"?
It makes no sense to call Object.create when you already have the complete object that you want. Inheritance is only useful when you have multiple objects that are supposed to share common properties, but in your example getMaker and getYear are own properties of each car instance.
The advantage of having methods in __proto__ or prototype is multiple copies of created objects will point to the same methods. Meaning only one copy of each method will be created. Less(relatively) usage of memory. If the methods are attached to the objects itself then each object will have its own copy of the methods.
Related
My understanding of prototypes is as follows:
let Animal = function() {
this.bark = "woof";
}
Animal.prototype.barkLoud = function() {
return this.bark.toUpperCase();
}
let x = new Animal();
x.barkLoud() = "WOOF";
Everything above makes sense to me but then I saw a tutorial what seemingly appears to be 2 different ways to pass prototypes to an object. Are these the same thing? If so, which approach is better:
let obj = {
age: 45;
__proto__: Animal
}
vs
let obj = {
age: 45;
}
obj.prototype = Object.create(Animal.protoype);
As a rule of thumb, the more underscore (_) characters you see around a property name in JS, the more of implementation details it is - and the more discouraged you are from even querying it, let alone attempting to modify.
While __proto__ is indeed supported by all the existing browsers, its usage to set up a prototype is not recommended. Use Object.create() instead.
BTW, two ways you've showed are not even equivalent. See, __proto__ refers to the prototype object, but Animal is not the one - it's a function. Animal.prototype is an object that will be used as a prototype (__proto__ value) for all the objects created by this function with new` operator.
So the first object won't be able to resolve barkLoud name from the prototype chain:
> obj.barkLoud // undefined
__proto__ way is deprecated by the way and I've never seen it implemented anywhere.
Go with Object.create as the official and recommended way to create/assign prototype of existing object to the newly created object.
So I have been looking at Object.freeze() and Object.seal().
Object.freeze() - will make all existing properties non-writable, and will not allow any new properties to be added.
Object.seal() - "Sealing an object prevents new properties from being added and marks all existing properties as non-configurable."
I am looking for a way to make all existing properties "frozen" (non-writable), but allow new properties to be added.
Is there shorthand for doing that?
The manually way of doing what I want is:
let freezeExistingProps = obj => {
Object.keys(obj).forEach(k => {
Object.defineProperty(obj, k, {
writable: false
});
});
};
The above function works surprisingly well to freeze existing top-level properties on an object (it doesn't overwrite them, just changes them to non-writable), but I am hoping there might be a more official/quicker way to do the above.
You might do the following:
instance -> frozen static proto -> dynamic proto
Some sample:
function freeze(stat,dyn){
Object.setPrototypeOf(stat,dyn);
Object.freeze(stat);
}
var a={unchangeable:1};
var b={changeable:2}
freeze(a,b);
Now have a look at a and change some b props.
Well, if you want to do it in the manner of freeze, then freezing it immediately, and setting up to a prototype of another object might help, but it will return a copy (pointing to the original object as prototype), exactly in the form how you want. there are obviously some pros and cons, as the properties will not be the immediate properties, but we can find it out by its __proto__ if we need all the keys (assuming you have a dedicated use case)
So, just another try
function freezeExistingProps (obj){
var OBJECT = function(){};
Object.freeze(obj)
OBJECT.prototype = obj;
return new OBJECT();
}
You may want to consider cloning your object into a new one with extra attribute. It's also a very good practice (look for immutability).
An example:
const setAge = (person, age) => ({ ...person, age });
const person = {
firstName: 'Luke',
lastName: 'Skywalker',
};
const personWithAge = setAge(person, 24);
Is there any difference in how these functions operate? The first one is more typically of what I think about when thinking of a constructor.
Example 1: using this to name and set properties. Then using new to create a new Book object.
function Book(name, numPages) {
this.name = name;
this.numPages = numPages;
}
var myBook = new Book('A Good Book', '500 pages');
Example 2: returning a object by using new and just calling the function itself.
function Movie(name, numMinutes) {
return { name:name, numMinutes:numMinutes };
}
var best = new Movie('Forrest Gump', '150');
var other = Movie('Gladiator', '180');
I guess what I'm trying to figure out is if these are different in the way they create an object? If so is one better than the other? Are there different situations where one would work better over the other?
The first one is a constructor, and can therefore be extended by a prototype, and you can test via instanceof wether the result is an Instance of this type.
Downside: if you forget the new-keyword your code will blow up (unless you write a workaround for that into each constuctor)
And you can't really use apply() with a constructor to pass an array of arguments, when you instantiate a new Object; on the other hand, don't do that, even if you can/could.
The second one is a factory, not a constructor. Independant wether you use the new-keyword or not.
with this implementation it creates Objects that look the same but don't share a type or prototype (although the underlying JS-engine recognizes them as similar and so they share the same hidden Class as long as they have the same properties, added in the same order, ... different topic)
long story short, neither performance nor memory-footprint suffer from this approach (anymore)
But you can't check wether they are of the same type, and you don't have a shared prototype that may affect all instances (maybe a pro or a con.)
My goto-approach If I need inheritance, is kind of a mix of both:
(if I just need a data-object I usually use a factory and plain objects).
function Book(conf) {
var book = Object.create(Book.prototype);
//a book like this usually has multiple configs/properties
if(typeof conf === "object"){
for(var k in conf) book[k] = conf[k];
}else if(conf){
//assuming that I get at least the name passed
book.name = String(conf);
}
return book;
}
//I have a prototype that can be extended
//with default-values for example; no idea for a good method
//to add to the prototype in this example ;)
Book.prototype.numPages = 0;
//but I can also use it like a plain function; no error if you
var myBook1 = Book("Peter Pan");
var myBook2 = Book({
name: "American Gods",
author: "Neil Gaiman"
});
If I add the following line to the top of the function I can also use that as a method to cast anything into an Instance of Book without cloning already existing instances
function Book(conf) {
//with this simple line I can also use this as a function to cast anything into a "Book"
if(conf instanceof Book) return conf;
var book = Object.create(Book.prototype);
//...
return book;
}
var data = [
"Peter Pan",
{name: "American Gods"},
//...
];
var books = data.map(Book);
In my opinion, I have the benefits of both worlds with this approach.
Basically, when you use new, the JS engine makes a brand new object for you and injects that as the value of this. It also automatically gives you any methods attach to the prototype of the constructor. Using a constructor also allows you to check if an object is an instanceof something more easily.
function MovieA(title) {
this.title = title;
}
MovieA.prototype.getTitle = function() {
return this.title;
};
function MovieB(title) {
return {
title: title
};
}
MovieB.prototype.getTitle = function() {
return this.title;
};
var a = new MovieA('A');
console.log(a instanceof MovieA); // true
console.log(a.getTitle()); // 'A'
var b = MovieB('B');
console.log(b instanceof MovieB); // false
console.log(b.getTitle()); // Uncaught TypeError: b.getTitle is not a function
Everything that new offers you can be attained through other methods but requires more manual labor.
The second method, factories, tend to work better for unit testing, custom object creation and functional programming. It works better for unit testing because if you have a factory producing all of your objects, you can just replace that factory with a mock-up to test different cases.
var Factory = {
makeThing: function() {
return { name: 'thing' };
}
};
// Want to test the case for if makeThing fails inside of some other code
var MockFactory = {
makeThing: function() {
return null;
};
};
As for when you use either, it all depends. Some people don't use new at all. Others exclusively use new. It all depends on if you need any of the things listed above, how much control you need over the creation of objects, when you want to use this or not, etc. In the end, it's all a matter of preference.
The difference is the constructor used to create the object returned.
new Book('A Good Book', '500 pages');
creates a Book object instance, with the instance inheriting properties from Book.prototype, including a constructor property value of Book. The Book.prototype object itself inherits from Object.prototype.
var other = Movie('Gladiator', '180');
uses Movie as a factory function (new not required) and returns an Object object instance, with the instance inheriting properties directly fromObject.prototype, including a constructor property value of Object.
More briefly stated, Object literal syntax creates an Object object.
In a JavaScript code, we currently create a lot of objects using Method1 (see code). Would it be better to use Method2? (using .prototype) What would the benefits be? (Espescially interested in execution speed and memory usage.)
var exampleObjectFunction1 = function(){ alert(this.exampleProperty1);};
var exampleObjectFunction2 = function(){ this.myFunction1(); };
var exeampleProperties = {
exampleProperty1: 'example',
exampleProperty2: 'example',
};
//Method1: Current instanciation method
function exampleObject1(properties) {
this.exampleProperty1 = properties.property1;
this.examplePropertyX = properties.propertyX;
this.exampleObjectFunction1 = exampleObjectFunction1;
this.exampleObjectFunction2 = exampleObjectFunction2;
};
var exampleObject1 = new exampleObject(properties);
//Method2: Using prototype (better?)
function exampleObject2(properties) {
this.exampleProperty1 = properties.property1;
this.examplePropertyX = properties.propertyX;
};
exampleObject2.prototype.exampleObjectFunction1 = exampleObjectFunction1;
exampleObject2.prototype.exampleObjectFunction2 = exampleObjectFunction2;
var exampleObject2 = new exampleObject2(properties);
Edit: I see a lot of "constructor vs prototype" comparisons. But the "constructor" codes there usually redefine the functions inside the constructor while I pre-define them in re-usable variable. I think that this is a different scheme, though I am not quite sure if it's closer to "prototype" or to "constructor".
Let's say you create 1000 objects of exampleObject1 and 1000 objects of exampleObject2.
If you want to override exampleFunction1 for all exampleObject1 objects, you have to iterate through all your objects and change it.
If you want to do it for all exampleObject2 objects, you just change it's prototype in one place.
So, from the point of code extensibility is using of prototype better choice.
Ok am just going through basics of JavaScript and I was learning objects where I came across this example...
JavaScript
var person = {
firstname : "Smith",
lastname : "Bach"
};
And what we write in PHP is
$person = array(
"firstname"=>"Smith",
"lastname"=>"Bach"
);
So is this the same thing or am making a mistake in understanding the concept?
No, objects are more than that.
Object is indeed a map/dictionary, but additionally every object inherits some of the properties (key-value pairs) from another object. That other object is called prototype.
For example:
var o = {
x: 1
};
console.log(o.x === undefined); // false, obviously
console.log(o.toString === undefined); // false, inherited from prototype
Most commonly a prototype is set by creating an object with a constructor function:
var d = new Date();
console.log(d.hasOwnProperty('getYear')); // false, it's inherited
EDIT:
Here's how the prototype works using constructor functions (it's one of the ways to do OOP in JS):
// constructor function
// starts with capital letter, should be called with new
var Person = function (name, age) {
// set properties of an instance
this.name = name;
this.age = age;
};
// functions to be inherited are in the prototype
Person.prototype.sayHello = function () {
return this.name + ' is ' + this.age + ' old';
};
// new:
// - creates the object
// - sets up inheritance from prototype
// - sets the object as the context of the constructor function call (this)
var p = new Person('Jason', 27);
console.log(p.sayHello());
They are associative arrays, but not just associative arrays. There are functions available from the Object prototype (like .toString()) whose names can collide with property names. Objects can be constructed via other functions and given more inherited properties too. (Note that one thing that plain objects don't have is a .length property to count entries, like array objects have. The term "associative array" is probably not the best one to use for JavaScript objects; they're objects and that should be enough once you're familiar with JavaScript.)
edit — what I mean is this:
var o = {};
alert("toString" in o); // alerts "true"
Thus a newly-created empty object appears to have a property called "toString". The issue with JavaScript is that there's only one property accessor operator (well two, but they're two flavors of the same thing), so there's no way to distinguish between accesses to the array's contents and access to the array's API. (Also, in JavaScript it's really not a good idea to think of them using the word "array", as that means something different in JavaScript — arrays are a type of Object with special properties.)
EcmaScript 5 has mechanisms for defining object properties in such a way as to make them immutable and non-iterable, which helps some. It's still problematic if you want to store a property called "toString" in an object.