Defining a JavaScript Prototype

Defining a Javascript prototype

Am I correct in assuming that Option 2 results in trashing certain functions that are implicitly bound to the prototype?

Yes, exactly. Though the only implicitly bound property is the constructor property, which you seldom do need.

What are the functional differences?

Option 1 is just extending the existing prototype. If there are already Person instances inheriting from the prototype object, they will be able to use the sayName method as well. With option 2, the new prototype will only be used for objects that are instantiated after the overwriting.

Are there any benefits for choosing one over the other?

These should be self-explaining now. Option 1 (extending) is considered cleaner, and is a must if you're modifying foreign/unknown/native prototypes. Try to avoid option 2.

If you still like the object literal syntax better, you should consider using Object.assign to extend the existing prototype:

Object.assign(Person.prototype, {
sayName: function(name) {
alert(name);
}
});

You may need to polyfill Object.assign for pre-ES6 environments. Alternatively, $.extend or _.extend work just as well. Surely also your favourite library comes with a helper function for this.

Is it ok to define a prototype function on Object in Javascript?

The only safe way to add to Object.prototype is to use the ES5 method Object.defineProperty to create a non-enumerable property:

Object.defineProperty(Object.prototype, 'doSomething', {
value: function(p) { ... },
enumerable: false, // actually the default
});

This ensures that the new property doesn't appear if you do for (key in object).

Note that this function cannot be reliably shimmed because non-enumerable properties didn't exist in previous versions of ECMAScript / JavaScript.

In any event, if your method only applies to HTML page elements, you would be better adding this to Element.prototype instead of to Object.prototype, although older (IE) browsers may complain if you do this.

Prototype and constructor in JavaScript (plain English)?

Constructor and protoypes in plain English?

Constructor functions create objects and assign prototypes to them. A prototype is an object with various properties that an object can inherit through the prototype chain. As always, examples help:

function Foo() {
}
Foo.prototype.answer = 42;

var f = new Foo();
console.log(f.answer); // "42"

Foo is a constructor function. When you use new Foo, the object that Foo.prototype points to will become the prototype of the object that gets created. When you do f.answer, since f doesn't have its own property with the name answer, the JavaScript engine looks at f's prototype to see if it has one. Since it does, it uses the value from the prototype and we see "42" in the console. This is how properties are resolved: By looking at an object seeing if it has a property with the given name, and if not, going to its prototype to see if it has the property, and if not going to its prototype, and so on.

Note that a consequence of the above is that adding properties to a prototype after an object has been created using that prototype works just fine; you can use those new properties via the object:

function Foo() {
}
Foo.prototype.answer = 42;

var f = new Foo();
console.log(f.question); // "undefined", neither `f`, nor `Foo.prototype`, nor
// `Object.prototype` has a `question` property

Foo.prototype.question = "Life, the Universe, and Everything";
console.log(f.question); // "Life, the Universe, and Everything"

As of ES5, constructor functions are no longer the only way you can assign prototypes to objects. Now you can also do it via Object.create. The above is roughly equivalent to this:

var fooProto = {
answer: 42
};
var f = Object.create(fooProto);
console.log(f.answer); // "42"

What is the purpose behind using Prototypes and constructors?

To share characteristics between objects. The properties of a prototype can be functions or data, both of which the objects using that prototype have access to and can reuse.

Re your comment below:

I understood the part about sharing characteristics, but could I get some more detailing in to it

Well, consider a Circle constructor:

function Circle(radius) {
this.r = radius;
}
Circle.prototype.radius = function() {
return this.r;
};
Circle.prototype.diameter = function() {
return this.r * 2;
};
Circle.prototype.circumference = function() {
return 2 * Math.PI * this.r;
};
Circle.prototype.area = function() {
return Math.PI * this.r * this.r;
};

All objects constructed by Circle will get Circle.prototype as their prototype, and so they all have the handy diameter, circumference, et. al. functions.

var c1 = new Circle(3);
console.log(c1.area()); // 28.274333882308138
console.log(c1.circumference()); // 18.84955592153876

var c2 = new Circle(5);
console.log(c2.area()); // 78.53981633974483
console.log(c2.circumference()); // 31.41592653589793

They share those properties in a memory-efficient way: Each instance doesn't have its own copy of those properties (which would mean keeping each property name and its value in each obejct); instead, they just have a reference to their prototype, which they share, which has those properties.

How to define prototype method inside class definition (externally) in Javascript?

I can think of two ways of adding external functions as own properties:

  1. Set it as a property of the this inside the constructor of the class.
  2. Set it as a public property of the class, this is the proposal.

The downside is that, all the instances will not share the same function object as it becomes an own property. Own properties are used for holding state not functions.

To add in prototype, which is where methods in ES6 class are added, so that all instances share the same function object. You have to add it to the prototype property of the class. This works as class is a syntactic sugar of the function constructor and every function has a prototype property which is inherited by all instances of that class.

class Foo {  constructor() {    // ...code    this.bar = bar; //becomes own property  }    //As a public field, becomes own property  baz = baz;  }function bar(){  console.log("from bar");}function baz(){  console.log("from baz");}function foobar(){ console.log("from foobar");}
const foo = new Foo();foo.bar();foo.baz();//Set it directly on the prototype prop of the ClassFoo.prototype.foobar = foobar; foo.foobar();

Defining methods via prototype vs using this in the constructor - really a performance difference?

See http://jsperf.com/prototype-vs-this

Declaring your methods via the prototype is faster, but whether or not this is relevant is debatable.

If you have a performance bottleneck in your app it is unlikely to be this, unless you happen to be instantiating 10000+ objects on every step of some arbitrary animation, for example.

If performance is a serious concern, and you'd like to micro-optimise, then I would suggest declaring via prototype. Otherwise, just use the pattern that makes most sense to you.

I'll add that, in JavaScript, there is a convention of prefixing properties that are intended to be seen as private with an underscore (e.g. _process()). Most developers will understand and avoid these properties, unless they're willing to forgo the social contract, but in that case you might as well not cater to them. What I mean to say is that: you probably don't really need true private variables...

How does JavaScript .prototype work?

Every JavaScript object has an internal "slot" called [[Prototype]] whose value is either null or an object. You can think of a slot as a property on an object, internal to the JavaScript engine, hidden from the code you write. The square brackets around [[Prototype]] are deliberate, and are an ECMAScript specification convention to denote internal slots.

The value pointed at by the [[Prototype]] of an object, is colloquially known as "the prototype of that object."

If you access a property via the dot (obj.propName) or bracket (obj['propName']) notation, and the object does not directly have such a property (ie. an own property, checkable via obj.hasOwnProperty('propName')), the runtime looks for a property with that name on the object referenced by the [[Prototype]] instead. If the [[Prototype]] also does not have such a property, its [[Prototype]] is checked in turn, and so on. In this way, the original object's prototype chain is walked until a match is found, or its end is reached. At the top of the prototype chain is the null value.

Modern JavaScript implementations allow read and/or write access to the [[Prototype]] in the following ways:

  1. The new operator (configures the prototype chain on the default object returned from a constructor function),
  2. The extends keyword (configures the prototype chain when using the class syntax),
  3. Object.create will set the supplied argument as the [[Prototype]] of the resulting object,
  4. Object.getPrototypeOf and Object.setPrototypeOf (get/set the [[Prototype]] after object creation), and
  5. The standardized accessor (ie. getter/setter) property named __proto__ (similar to 4.)

Object.getPrototypeOf and Object.setPrototypeOf are preferred over __proto__, in part because the behavior of o.__proto__ is unusual when an object has a prototype of null.

An object's [[Prototype]] is initially set during object creation.

If you create a new object via new Func(), the object's [[Prototype]] will, by default, be set to the object referenced by Func.prototype.

Note that, therefore, all classes, and all functions that can be used with the new operator, have a property named .prototype in addition to their own [[Prototype]] internal slot. This dual use of the word "prototype" is the source of endless confusion amongst newcomers to the language.

Using new with constructor functions allows us to simulate classical inheritance in JavaScript; although JavaScript's inheritance system is - as we have seen - prototypical, and not class-based.

Prior to the introduction of class syntax to JavaScript, constructor functions were the only way to simulate classes. We can think of properties of the object referenced by the constructor function's .prototype property as shared members; ie. members which are the same for each instance. In class-based systems, methods are implemented the same way for each instance, so methods are conceptually added to the .prototype property; an object's fields, however, are instance-specific and are therefore added to the object itself during construction.

Without the class syntax, developers had to manually configure the prototype chain to achieve similar functionality to classical inheritance. This led to a preponderance of different ways to achieve this.

Here's one way:

function Child() {}
function Parent() {}
Parent.prototype.inheritedMethod = function () { return 'this is inherited' }

function inherit(child, parent) {
child.prototype = Object.create(parent.prototype)
child.prototype.constructor = child
return child;
}

Child = inherit(Child, Parent)
const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'

...and here's another way:

function Child() {}
function Parent() {}
Parent.prototype.inheritedMethod = function () { return 'this is inherited' }

function inherit(child, parent) {
function tmp() {}
tmp.prototype = parent.prototype
const proto = new tmp()
proto.constructor = child
child.prototype = proto
return child
}

Child = inherit(Child, Parent)
const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'

The class syntax introduced in ES2015 simplifies things, by providing extends as the "one true way" to configure the prototype chain in order to simulate classical inheritance in JavaScript.

So, similar to the code above, if you use the class syntax to create a new object like so:

class Parent { inheritedMethod() { return 'this is inherited' } }
class Child extends Parent {}

const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'

...the resulting object's [[Prototype]] will be set to an instance of Parent, whose [[Prototype]], in turn, is Parent.prototype.

Finally, if you create a new object via Object.create(foo), the resulting object's [[Prototype]] will be set to foo.

Check if an object has a user defined prototype?

Assuming you want to find out whether an object is an instance of a custom constructor function, you can just compare its prototype against Object.prototype:

function hasUserPrototype(obj) {
return Object.getPrototypeOf(obj) !== Object.prototype;
}

Or if you maintain the constructor property properly:

function hasUserPrototype(obj) {
return obj.constructor !== Object;
}

This would also work in browsers which don't support Object.getPrototypeOf.

But both solutions would return true also for other native objects, like functions, regular expressions or dates. To get a "better" solution, you could compare the prototype or constructor against all native prototypes/constructors.


Update:

If you want to test whether a function has a user defined prototype value, then I'm afraid there is no way to detect this. The initial value is just a simple object with a special property (constructor). You could test whether this property exists (A.prototype.hasOwnProperty('constructor')), but if the person who set the prototype did it right, they properly added the constructor property after changing the prototype.

Advantages of using prototype, vs defining methods straight in the constructor?

Methods that inherit via the prototype chain can be changed universally for all instances, for example:

function Class () {}
Class.prototype.calc = function (a, b) {
return a + b;
}

// Create 2 instances:
var ins1 = new Class(),
ins2 = new Class();

// Test the calc method:
console.log(ins1.calc(1,1), ins2.calc(1,1));
// -> 2, 2

// Change the prototype method
Class.prototype.calc = function () {
var args = Array.prototype.slice.apply(arguments),
res = 0, c;

while (c = args.shift())
res += c;

return res;
}

// Test the calc method:
console.log(ins1.calc(1,1,1), ins2.calc(1,1,1));
// -> 3, 3

Notice how changing the method applied to both instances? This is because ins1 and ins2 share the same calc() function. In order to do this with public methods created during construction, you'd have to assign the new method to each instance that has been created, which is an awkward task. This is because ins1 and ins2 would have their own, individually created calc() functions.

Another side effect of creating methods inside the constructor is poorer performance. Each method has to be created every time the constructor function runs. Methods on the prototype chain are created once and then "inherited" by each instance. On the flip side of the coin, public methods have access to "private" variables, which isn't possible with inherited methods.

As for your function Class() {} vs var Class = function () {} question, the former is "hoisted" to the top of the current scope before execution. For the latter, the variable declaration is hoisted, but not the assignment. For example:

// Error, fn is called before the function is assigned!
fn();
var fn = function () { alert("test!"); }

// Works as expected: the fn2 declaration is hoisted above the call
fn2();
function fn2() { alert("test!"); }


Related Topics



Leave a reply



Submit