Pseudo-classical inheritance with privacy? - javascript

In JavaScript: The Good Parts, Crockford argues that one of the downsides of using the pseudo-classical pattern of inheritance is that it publicly exposes instance variables.
For example:
var Ball = function(width, color) {
this.width = width;
this.color = color;
}
var redBall = new Ball(5, "red");
redBall.width = 12; // Changes width to 12
Now, what if I want the width of the ball to be private?
Here's what I've tried:
var Ball = function(width, color) {
this.getWidth = function() { return width; }
this.color = color;
}
var redBall = new Ball(5, "red");
Problem with that is we can still change this.getWidth and there may be prototype methods that rely on it.
What about ...
var Ball = function(width, color) {
return {
getWidth: function() { return width; },
color: color
}
}
var redBall = new Ball(5, "red");
Problem with that is that the prototype methods no longer have access to the instance variables. It's also closer to the functional pattern of inheritance, but with more indirection using the new operator.
So how do I achieve privacy using the pseudo-classical inheritance pattern? Is this even possible?

To answer your question; the only way to have intace specific private members is to have both the members and privileged functions (functions that can access them) in the same scope. This means they all have to be in the constructor body (var my private...this.myPrivileged=function(){ console.log (myPrivate...) or in a IIFE with a closure object keeping track of instances and their privates.
When returning an object private you already loose privacy because the calling code can mutate your private value. To prevent this you have to deep copy the value and return that.
To have privates on the prototype the privates will be shared. This because the instance is not known when declaring your prototype and private members.
This is because JavaScript doesn't have a private modifier and only simulates them through closures.
One pattern that can use prototype for instance specific protected variables is using Crockford's box example.
All protecteds are put in a box that can only be opened with a key, the key is available through closures to all prototype members defined in the IIFE.
Because the instance isn't known when creating prototype you have to invoke initProtecteds from the instance in order to create instance specific protected members.
Minimal code with an example protected instance member called medicalHistory is used in Animal.
function makeBox(key){
var ret = {};
return {
get : function(pKey){
if(pKey===key){
return ret;
}
return false;
}
}
};
var Person = function(args){
args = args || {};
this.name = args.name || "Nameless Person";
this.initProtecteds();
};
//using IIFE to define some members on Person.prototype
// these members and only these members have access to
// the passed object key (through closures)
// later the key is used to create a box for each instance
// all boxes use the same key so instances of same type
// can access each other's protected members and instances
// inheriting from Person can do so too, extending parent methods
// will be trickier, no example for that is given in this code
(function(key){
//private shared member
var privateBehavior = function(instance,args){
//when you invoke this from public members you can pass
// the instance or use call/apply, when using call/apply
// you can refer to this as the current instance, when
// passing it as an argument then instance will
// be the current instance
console.log("private shared invoked");
};
//set default _protecteds to false so init knows
// it has not been initialised and needs to be shadowed
// with a box
Person.prototype._protecteds=false;
Person.prototype.getMedicalHistory = function(){
//Maybe run some code that will check if you can access
// medical history, invoking a private method
privateBehavior(this,{});
var protectedObject = this._protecteds.get(key);
//if medicalHistory is an object the calling code
// can now mutate it
return protectedObject.medicalHistory;
};
Person.prototype.hasSameDesease = function(person){
//this Person instance should be able to see
// medical history of another Person instance
return person._protecteds.get(key);
};
Person.prototype.getArr = function(){
//Returns protecteds.get(key).arr so we can
// mutate it and see if protecteds are instance
// specific
return this._protecteds.get(key).arr;
};
Person.prototype.initProtecteds = function(){
//only create box if it hasn't been created yet
if(this._protecteds!==false)
return;
//use the same key for all instance boxes, one instance
// can now open another instance's box
this._protecteds=makeBox(key);
//retreive the object held by the box
var protectedObject = this._protecteds.get(key);
//add protected members by mutating the object held
// by the box
protectedObject.medicalHistory = "something";
protectedObject.arr = [];
//protectedObject is no longer needed
protectedObject=null;
};
}({}));
var Animal = function(){
this.initProtecteds();
};
(function(key){
Animal.prototype._protecteds=false;
Animal.prototype.initProtecteds = function(){
if(this._protecteds!==false)
return;
this._protecteds=makeBox(key);
var protectedObject = this._protecteds.get(key);
protectedObject.medicalHistory = "something";
};
}({}));
var Employee = function(args){
//re use Person constructor
Person.call(this,args);
};
//set up prototype part of inheritance
Employee.prototype = Object.create(Person.prototype);
//repair prototype.constructor to point to the right function
Employee.prototype.constructor = Employee;
var ben = new Person({name:"Ben"});
var nameless = new Person();
console.log(ben.getMedicalHistory());//=something
//key is in closure and all privileged methods are in that closure
// since {} !== {} you can't open the box unless you're in the closure
// or modify the code/set a breakpoint and set window.key=key in the closure
console.log(ben._protecteds.get({}));//=false
//One Person instance can access another instance's protecteds
// Objects that inherit from Person are same
console.log(ben.hasSameDesease(nameless));//=Object { medicalHistory="something"}
var lady = new Animal();
//An Animal type object cannot access a Person protected members
console.log(ben.hasSameDesease(lady));//=false
var jon = new Employee({name:"Jon"});
console.log(ben.hasSameDesease(jon));//=Object { medicalHistory="something"}
//making sure that protecteds are instance specific
ben.getArr().push("pushed in ben");
console.log(jon.getArr());
console.log(nameless.getArr());
console.log(ben.getArr());

This is interesting to consider.
To me (and I consider myself a student of js), it looks like only private member functions have access to the object's private variables. This is because it creates a closure around the var:
var Ball = function(width, color) {
var width = width;
this.color = color;
this.getWidth=function(){return width}
this.specialWidthCalc=function(x){ width = width + x;}
}
So programmers can do:
var redBall = new Ball(5, "red");
consoloe.log( redBall.getWidth() );
redBall.specialWidthCalc(3);
consoloe.log( redBall.getWidth() );
I am unable to create a prototype which has access to width.

Related

Javascript - how do you add properties to an object constructor function

How do I add properties to a constructor function in JavaScript? For example. If I have the following function.
function Hotel(name)
{
this.name = name;
};
var hotel1 = new Hotel('Park');
can I add a "local" variable that can be used locally within the class as if it were private with the same notation using the keyword "this". Of course it would not be private since objects created will be able to use it correct?
Can I do something like this. Do I use the this keyword or do I use the var keyword
which one is it? I have example 2 on the function constructor on the bottom
1. var numRooms = 40;
2. this.numRooms = 40;
3. numRooms : 40,
function Hotel(name)
{
this.name = name;
this.numRooms = 40;
};
I know that if I want a function within the object constructor I need to use the this word. Will that work as well for normal variables as I have asked above.
function Hotel(name)
{
this.name = name;
this.numRooms = 40;
this.addNumRoomsPlusFive = function()
{
return this.numRooms + 5;
}
};
You can simple add a private variable to your constructor:
function Hotel(name) {
var private = 'private';
this.name = name;
};
But if you will use your Hotel function without a new operator, all properties and functions which was attached to this will become global.
function Hotel(name) {
var private = 'private';
this.name = name;
};
var hotel = Hotel('test');
console.log(name); // test
It is good idea to return an object in constructor function:
function Hotel(name) {
var
private_var = 'private',
private_func = function() {
// your code
};
retur {
name: 'name',
public_func: private_func
}
};
var hotel = Hotel('test');
console.log(name); // undefined
So if you will use Hotel constructor without new operator no global variable will be created. This is possible only if the return value is an object. Otherwise, if you try to return anything that is not an object, the constructor will proceed with its usual behaviour and return this.
can I add a "local" variable that can be used locally within the class as if it were private with the same notation using the keyword "this".
Yes we can:
// API implementation in the library
function Hotel(name) {
// only our library code knows about the actual value
const numRooms = 'privateNoRoomsVar';
this.name = name;
this[numRooms] = 40;
this.addNumRoomsPlusFive = function() {
return this[numRooms] + 5;
}
};
// from the library user's perspective
const hotel = new Hotel('Transylvania');
console.log('rooms+5 =', hotel.addNumRoomsPlusFive());
console.log('hotel.numRooms =', hotel.numRooms); // undefined
// also, users don't have access to 'numRooms' variable so they can't use hotel[numRooms].
If a user looks at the source code and finds out the value privateNoRoomsVar, then they can misuse the API.
For that we need to use symobls:
// API implementation in the library
function Hotel(name) {
// no one can duplicate a symbol so the variable is really private
const numRooms = Symbol();
this.name = name;
this[numRooms] = 40;
this.addNumRoomsPlusFive = function() {
return this[numRooms] + 5;
}
};
// from the library user's perspective
const hotel = new Hotel('Transylvania');
console.log('rooms+5 =', hotel.addNumRoomsPlusFive());
console.log('hotel.numRooms =', hotel.numRooms); // undefined
// there is no way users will get access to the symbol object so the variable remains private.
Private class features, #privateField, are supported by all the browsers so we don’t have to worry about this anymore.
// API implementation in the library
class Hotel {
// private field
#numRooms = 40;
constructor(name) {
this.name = name;
}
addNumRoomsPlusFive() {
return this.#numRooms + 5;
}
};
// from the library user's perspective
const hotel = new Hotel('Transylvania');
console.log('rooms+5 =', hotel.addNumRoomsPlusFive());
console.log('hotel.numRooms =', hotel.numRooms); // undefined
//console.log('hotel.numRooms =', hotel.#numRooms); // throws error
Javascript historically creates objects from prototypes of other objects. It was a result of EMCA2015, that you have a distinct syntax of a class that specifies an object. As an aside, if you mouse over the table in that link it gives dates of when the feature was implemented.
A javascript object created by the new operator is more or less a combination of an associative array ( what you make with let avar={}; ) that can access the function level scopes it is defined in. The keys of the array are its properties. According to its creator, Javascript was created to be an easy to use program language without a hierarchy of types. One of the ways it accomplished this is by more or less considering its mapping type to be equivalent to the prototypical Object which object oriented programming languages describe.
Adding properties in 2022
function AProtoype(arg1, arg2, arg3){
//this defines a property
this.pa=arg1;
/* unicorns in this section */
let x = 1;
/*
a getter which has the same syntax as a property
but returns x from the scope which it references and
not the object.
*/
get getx() => x;
}
let object = new AProtoype(2,3,4);
Is equivalent to the following code for the purposes of data access but not inheritance and typing. The new operator also sets variables on an object that are used for these purposes.
function NewObject(arg1, arg2, arg3){
let prototype = {};
/*dragons in this section, as you are not using the this keyword to accomplish things*/
prototype.pa = arg1;
Object.defineProperty(prototype, "getx", {get:()=>x});
return prototype;
}
//If you do this instead of using the new operator it is an anti-pattern.
//And like all anti-patterns: "But it works!"
let object = NewObject(2,3,4);
The relevant property defining methods where in some sense supported as early as 2010, 2011. I do not have a contemporary source to that time to confirm if you could pull off what I'm doing though, and you'd only want to if all else failed and it needed to run on Internet Explorer 9. In the event all else is failing, you may want to read the documentation for Object.create, which is also of interest because more or less provides an api to make new objects.
Now, for a fun time and horror, you can also define a function that returns this, and get an object back with an equivalent binding of that function. The horror comes when it is an object in global scope, and you rename a property of that object; as Javascript will resolve the name collision by happily writing on whatever it finds if it can. You can then use this to re-implement the prototype pattern that javascripts new operator is built off of conceptually, for the sake of science.
When you use a "constructor function" in Javascript, any properties defined on the instance using the this keyword become public. This is unavoidable, because Javascript objects have no concept of private properties - if it exists, it can be accessed directly as object.property.
For example, if you tried to do as in the following snippet, mimicking a typical getter/setter pattern with a private variable in Java or C# (note that even if this worked, this is not idiomatic Javascript):
function MyObject(privateVar) {
this.privateVar = privateVar;
}
MyObject.prototype.getVar = function() {
return this.privateVar;
};
MyObject.prototype.setVar = function(newVal) {
this.privateVar = newVal;
};
then while you can indeed use the getter and setter to do as you expect, you can also just access and set the private variable directly! Demonstration:
function MyObject(privateVar) {
this.privateVar = privateVar;
}
MyObject.prototype.getVar = function() {
return this.privateVar;
};
MyObject.prototype.setVar = function(newVal) {
this.privateVar = newVal;
};
var obj = new MyObject(1);
// using public getter/setter
console.log(obj.getVar()); // 1
obj.setVar(2);
console.log(obj.getVar()); // 2
// using private variable directly - not intended to work
console.log(obj.privateVar); // 2
obj.privateVar = 3;
console.log(obj.getVar()); // 3 (using public API to get it to show that the direct update to the private variable also affects the intended public methods)
There is though a way to mimic the effect of private variables. They're not actually object properties - because, as I have just demonstrated, such are intrinsically public - but the same can be mimicked by:
not using a "constructor function" at all, but a regular function that happens to return an object. This is all a constructor function really does, anyway - the difference in JS is only syntactic, that you do not need to use the new keyword when you call the function. (Although you still can, if you really prefer - any function that returns an object can be called with new and behave in the same way as without it, although performance will likely suffer a little as the function would then construct a brand new object and throw it away. See MDN for a justification of these statements, particularly step 4.)
inside this function, using a regular variable as the private variable. This variable will be completely inaccessible from outside by the simple rules of scope, but you can still have the returned object retain access to it by the "magic" of closures.
Here is the above getter/setter example translated to this procedure, as well as demonstrations of it working. (I hasten to add again though, that this wouldn't be considered idiomatic code in Javascript.)
function makeObjectWithPrivateVar(privateVar) {
function getPrivateVar() {
return privateVar;
}
function setPrivateVar(newVal) {
privateVar = newVal;
}
return { getPrivateVar, setPrivateVar };
}
var obj = makeObjectWithPrivateVar(1);
// getter
console.log(obj.getPrivateVar()); // 1
// setter
obj.setPrivateVar(2);
// getter again to observe the change
console.log(obj.getPrivateVar()); // 2
// but how could we access the private var directly??
// answer, we can't
console.log(obj.privateVar); // undefined
console.log(privateVar); // ReferenceError, privateVar is not in scope!
Note finally though that it's rare in modern Javascript to use the function-based constructors in this style, since the class keyword makes it easier to mimic traditional class-based languages like Java if you really want to. And in particular, more recent browsers support private properties directly (you just have to prefix the property name with a #), so the initial code snippet translated into a class and using this feature, will work fine:
class MyObject {
#privateVar
constructor(privateVar) {
this.#privateVar = privateVar;
}
getVar() {
return this.#privateVar;
}
setVar(newVal) {
this.#privateVar = newVal;
}
}
var obj = new MyObject(1);
// using public getter/setter
console.log(obj.getVar()); // 1
obj.setVar(2);
console.log(obj.getVar()); // 2
// using private variable directly - now doesn't work
console.log(obj.privateVar); // undefined, it doesn't exist
// console.log(obj.#privateVar); // error as it's explicitly private, uncomment to see error message
Usually it's performed using closures:
var Hotel = (function() {
var numrooms=40; // some kind of private static variable
return function(name) { // constructor
this.numrooms = numrooms;
this.name = name;
};
}());
var instance = new Hotel("myname");

What is the Javascript equivalent of a Class variable? [duplicate]

This question already has answers here:
Using Class Variables in Javascript
(2 answers)
Closed 7 years ago.
Edit: Thanks #Aadit M Shah for confirming.
function Apple () {}
Apple.someVar = null; // Class variable
Apple.prototype.someVar = null; // Instance variable
In class-based languages (i.e. Java) static members are created with a special syntax and can be used as if they were members of the class itself.
In JavaScript there is no special syntax to denote static properties. However you can implement such a classy behaviour by using constructor function and adding properties to it. Constructors, like all other functions, are objects and can have properties.
Let's see an example:
We have a constructor for Car, a static method for Car and an "instance" (prototype delegation) method for Car.
// constructor
var Car = function () {};
// a static method
Car.isShiny = function () {
return "bling bling";
};
// "instance" method added to the prototype
Car.prototype.setPrice = function (price) {
this.price = price;
};
As expected we can call the static method and the "instance" method.
// calling a static method
Car.isShiny(); // "bling bling"
// creating an instance and calling a method
var ford = new Car();
ford.setPrice(5000);
Calling an "instance" method statically won't work.
Similarly, calling a static method on an instance won't work.
typeof Car.setPrice; // "undefined"
typeof ford.isShiny; // "undefined"
There is a workaround for calling a static method on an instance. Define a prototype method, which references the static method.
Car.prototype.isShiny = Car.isShiny;
ford.isShiny(); // "bling bling"
However, this comes with a little complication that we need to be aware of. The keyword this, if used in the static method, will refer to either the Car constructor function or the ford instance, depending on who is the caller.
It is possible to have the same method called statically and non-statically. With the help of instanceof we can know how the method was called.
// constructor
var Car = function (price) {
this.price = price;
};
// a static method
Car.isShiny = function () {
// this always works
var msg = "bling bling";
if (this instanceof Car) {
// this only works if called non-statically
msg += ", it costs $" + this.price + '!';
}
return msg;
};
// a normal method added to the prototype
Car.prototype.isShiny = function () {
return Car.isShiny.call(this);
};
// static call
Car.isShiny(); // "bling bling"
//non static call
var benz = new Car('9999.99');
benz.isShiny(); // "bling bling, it costs $9999.99!"
The example illustrates the use of public static members. In JavaScript you can implement private static members too.
Reference: JavaScript Patterns book.
JavaScript is a functional language and features of classical programming languages are not directly supported but can be achieved by the concept of closures.
In the code snippet you gave, both Apple.someVar and Apple.prototype.someVar will have the same value for all objects created from the Apple constructor function, i.e. like a class variable.
To achieve the functionality of instance variables, use closures.
You can look online for more help on closures, here's reference http://javascript.crockford.com/private.html
I am providing a small code snippet to to make it clear.
function Apple(property){
var color = property.color; //instance variable
this.getColor = function(){
return color;
};
}
Apple.type = "fruit"; //class variable
var redApple = new Apple({color:'red'});
var greenApple = new Apple({color:'green'});
both the variables redApple and greenApple share the same property 'type' but each has its own color property.
tl;dr
function Apple () {}
Apple.someVar = null; // property on the constructor - doesn't affect instances
Apple.prototype.someVar = null; // the default value of the `someVar` property on instances returned by the constructor when using the `new` keyword
Here is a more verbose example to see it in action:
function Apple () {}
Apple.color = 'blue'; // this doesn't do what you think it does.
console.log('Apple.color:', Apple.color); // blue
console.log('---');
// basic instantiation of the apples
var goldenDelicious = new Apple();
var grannySmith = new Apple();
var fuji = new Apple();
console.log('goldenDelicious.color:', goldenDelicious.color); // undefined
console.log('grannySmith.color:', grannySmith.color); // undefined
console.log('fuji.color:', fuji.color); // undefined
console.log('---');
fuji.color = 'red';
Apple.prototype.color = 'green';
// overrides color properties of all apples that have not
// had their color set - even those of instances already created
// This is because their value is the default, and we are
// modifying that default.
console.log('goldenDelicious.color:', goldenDelicious.color); // green
console.log('grannySmith.color:', grannySmith.color); // green
console.log('fuji.color:', fuji.color); // red
console.log('---');
// assign some actual colors
goldenDelicious.color = 'yellow';
grannySmith.color = 'green';
fuji.color = 'red';
console.log('goldenDelicious.color:', goldenDelicious.color); // yellow
console.log('grannySmith.color:', grannySmith.color); // green
console.log('fuji.color:', fuji.color); // red
console.log('---');
Apple.prototype.color = 'orange'; // all new apples will default to orange
var honeyCrisp = new Apple();
console.log('goldenDelicious.color:', goldenDelicious.color); // yellow
console.log('grannySmith.color:', grannySmith.color); // green
console.log('fuji.color:', fuji.color); // red
console.log('honeyCrisp.color:', honeyCrisp.color); // orange

Classes in JavaScript

I am defining a class in JavaScript
function Pen(parent){
this.color = "#0000ff";
this.stroke = 3;
this.oldPt;
this.oldMidPt;
this.isActive = false;
this.parent = parent; //app that owns this pen
this.points = [];
this.curShape;
console.log(this);
return(this);
}
In the console.log statement I am getting way more than just this class, I am getting all kinds of information about basically everything else going on. Why is that?
the keyword this is dependent on the caller, so if you are initializing the function without the "new" keyword "this" might very well be referencing the window and not the object.
Try:
function Pen(parent){
var context = this;
this.color = "#0000ff";
this.stroke = 3;
this.oldPt;
this.oldMidPt;
this.isActive = false;
this.parent = parent; //app that owns this pen
this.points = [];
this.curShape;
console.log(context);
return(this);
}
var pen = new Pen();
Because your "class" inherits (transparently, in your case) from other javascript base classes. If you want to introspect only the properties you've created on your object, use hasOwnProperty(keyName) to filter those out.
Javascript is prototype based and not class based. Every new custom object as default
has pre built functionlity provided by the base object called Object.
So everytime you ask if a property or method belongs to a given object, in the worst
case it will fall until the Object object, and if that property/method is not there
then it will throw an error. Therefore when you are using the log function it is
printing all the object structure including properties and method of its "base" class.

Reason for using `prototype` instead of `this`

I am using Lightbox2
https://github.com/lokesh/lightbox2/blob/master/js/lightbox.js
And I don't understand why all the inner members of Lightbox are prototyped (Lightbox.prototype.init) and not simply members (Lightbox.init)?
If they are specific to each instance of lightbox wouldn't it be easier to use this.init?
Confused? Don't be...
Think of it this way:
Lightbox is your class definition, but it's not yet an instance.
Whatever you put directly on the class is like a static member:
Lightbox.staticFunc = function() {
// "this" will not point to instance object
};
Whatever you put on its prototype is a shared instance member:
Lightbox.prototype.instanceFunc = function() {
// "this" will point to object instance so members can be accessed
};
When you create an instance of a class, all instance members are accessible throught this keyword, but static ones through class definition:
var someData = Lightbox.staticFunc();
var l = new Lightbox();
l.instanceFunc();
Does this clear you understanding of prototype members?
Lightbox code then
The code that you've been looking at means this:
// this is a constructor that accesses instance properties (using "this")
// ------
// since properties are accessed via "this.something" means that they are
// not shared between instances but are part of one particular instance
// ------
function Lightbox(options) {
this.options = options;
this.album = [];
this.currentImageIndex = void 0;
this.init();
}
// adding an instance method that will be accessible to lightbox object instance
// that's why it can also access instance members (using "this")
// ------
// all functions that are defined on the prototype are shared between
// all instances so they consume less resources because not every
// object instance created them separately.
// ------
Lightbox.prototype.init = function() {
this.enable();
return this.build();
};
But some parts of this code are a bit confusing i.e.
LightboxOptions = (function() {
function LightboxOptions() {
this.fileLoadingImage = 'images/loading.gif';
this.fileCloseImage = 'images/close.png';
this.resizeDuration = 700;
this.fadeDuration = 500;
this.labelImage = "Image";
this.labelOf = "of";
}
return LightboxOptions;
})();
LightboxOptions class is contained within a function closure even though it doesn't define any private data, so the outer immediately executing function could be omitted in this example while having identical results:
LightboxOptions = function() {
this.fileLoadingImage = 'images/loading.gif';
this.fileCloseImage = 'images/close.png';
this.resizeDuration = 700;
this.fadeDuration = 500;
this.labelImage = "Image";
this.labelOf = "of";
};
It would of course be possible to define those functions in a constructor using this but then they wouldn't be shared between instances hence every object instance would define the same function hence consuming more resources. So this is not the same although from the execution point it does look the same:
CustomClass = function() {
this.prop = true;
};
CustomClass.prototype.method = function() { alert("I'm shared."); };
is slightly different than:
CustomClass = function() {
this.prop = true;
this.method = function() { alert("I'm duplicated in every instance."); };
};
The later consumes more resources while function is defined for every object instance.
...and a bit more to completely clear this thing
Suppose we have this class definition:
var C = function() {
this.prop = true;
this.method = function() { console.log("Per instance method"); };
}
C.prototype.method = function() { console.log("Shared instance method"); };
What happens here if we call these lines of code
var a = new C();
var b = new C();
a.method();
b.method();
delete a.method;
a.method();
b.method();
What do you think the output would be? You should get at least a little confused what happens after delete? Which method will get deleted? Per instance? Shared? Both? Well as it should be per instance method gets deleted on object instance a, that's why afterwards it reports that the shared method has been called. But only on a. b still has its own per instance method.
So without any further ado, output looks like this:
Per instance method // a.method
Per instance method // b.method
Shared instance method // a.method
Per instance method // b.method
What about prototype properties
These are different. When you create an object instance all those properties get copied to every object and are not shared. So whatever you do on them within the realm of a particular object will not get reflected to others.
If you'd then delete such property on a particular object it would still be available with its initial value as it was when object got instantiated.
var C = new function() {};
C.prototype.prop = 1;
var a = new C();
var b = new C();
a.prop = 10; // does not change the value of "b.prop"
delete a.prop; // "a.prop" is now back to 1
If they are specific to each instance of lightbox wouldn't it be
easier to use this.init?
They shouldn't be that's why they are putting everything in prototype object. When you use prototype, all methods still become available to you only that they do not become instance members.
JavaScript works with prototype chain, when it sees a method, it searches through the prototype chain till it finds the specified method. This process goes till the final Object object if not found in the middle.
You should only create instance members (via this) that you think are reasonable or needed because it adds an overhead (computational waste) if you put unnecessary methods using this keyword eg instance members.

When to use the word "prototype" in adding new properties to an object in javascript?

I don't understand in JavaScript when to use the word "prototype" vs. using simple "dot" notation without the word "prototype". Can someone look at these code blocks and help me understand when you'd want to use one over the other?
with "prototype":
function employee(name,jobtitle)
{
this.name=name;
this.jobtitle=jobtitle;
}
var fred=new employee("Fred Flintstone","Caveman");
employee.prototype.salary=null;
fred.salary=20000;
console.log(fred.salary);
without "prototype":
function employee(name,jobtitle,salary)
{
this.name=name;
this.jobtitle=jobtitle;
this.salary=salary;
}
var fred=new employee("Fred Flintstone","Caveman", 20000);
console.log(fred.salary);
JavaScript objects have a property which is a pointer to another object. This pointer is the object's prototype. Object instances by default share the same prototype:
function Employee(name){
this.name = name;
}
Employee.prototype.company = "IBM";
Employee.prototype.who = function(){
console.log("My name is", this.name, "I work for", this.company);
}
var bob = new Employee('Bob');
var jim = new Employee('Jim');
// bob and jim are seperate objects, but each is linked to the same 'prototype' object.
jim.who(); // jim doesn't have a property called 'who', so it falls back to it's 'prototype', where who exists
// My name is Jim I work for IBM
bob.who();
// My name is Bob I work for IBM
// Bob leaves IBM for Microsoft
bob.company = "Microsoft"; // bob now has a property called 'company'. The value of which is 'Microsoft', which overrides bob's prototype property of the same name.
bob.who();
// My name is Bob I work for Microsoft
Employee.prototype.company = 'Facebook';
jim.who();
// My name is Jim I work for Facebook
bob.who(); // Bob is not affected by the change.
// My name is Bob I work for Microsoft
delete bob.company;
bob.who(); // bob no longer has it's own property 'company', so like jim, it drops down to the prototype object.
// My name is Bob I work for Facebook
The issues around JS and inheritance may be complex, but the answer to your question is relatively simple. Consider this code:
function Klass() { }
var obj1 = new Klass();
var obj2 = new Klass();
Now, if you add a property to obj1, that property exists only on obj1. Likewise obj2.
If you add a property to Klass, that property likewise exists only on Klass (the function object). It doesn't affect obj1 and obj2 at all.
But if you add a property to Klass.prototype, that property will then be present on both obj1 and obj2, as well as any future objects created via new Klass. If you then change the value of the property on the prototype, the changed value will be what you see on all those objects.
You could add code inside the body of the Klass function to add properties to this; that will then cause any future Klass objects to get those properties. But each object would have its own copy - which can add up, memory-wise, especially when the properties are methods -and those copies would not be affected by future changes to the body of Klass.
ES5's Object.create almost removes the need to hassle around with .prototype anymore.
So, to pick up #Gerry's example, you can go like
var Mammal = {
walk: function() {}
};
var Dog = Object.create(Mammal, {
bark: {
value: function() {}
}
}); // create a new object which [[prototype]] refers to Mammal
Dog.walk();
Dog.bark();
The prototype object is a little tricky to understand; however this article on OOP JavaScript can help shed some light.
In a nutshell, the prototype object provides a blueprint for a 'recipient' object - all you have to do is point the recipient's prototype property at your blueprint object. Note that you can have as many recipients of a prototype blueprint object as you like (so Car and Train can both point to a common Vehicle prototype object).
You are free to define both properties and functions in a prototype object which will any recipient objects will be able to use, eg:
var vehiclePrototype = {
// A property which will be supplied to the recipient
cost: 0,
// A method which will be supplied the recipient
move: function () {
// Your prototype can refer to 'this' still.
console.log("Moving " + this.name);
};
}
You can now create a Car which makes use of the vechiclePrototype:
// Factory method for creating new car instances.
function createCar(name) {
// Define the Car's constructor function
function Car(name) {
this.name = name;
}
// Point the car's prototype at the vechiclePrototype object
Car.prototype = vechiclePrototype;
// Return a new Car instance
return new Car(name);
}
// Create a car instance and make use of the Prototype's methods and properties
var mustang = createCar(mustang);
mustang.cost = 5000;
mustang.move();
A new Train object could be created in a similar fashion:
function createTrain(whilstleSound) {
// Define the Train's constructor function
function Train(name) {
this.whilstleSound = whilstleSound;
}
// Point the train's prototype at the vechiclePrototype object
Train.prototype = vechiclePrototype;
// Return a new Train instance
return new Train(name);
}
var ic125 = new Train("pooop pooop");
ic125.move();
One of the big advantages of using Prototypical inheritance all instances of both Car and Train share the exact same move function (instead of creating multiple instances of the same function) which results in a significant memory saving if there are many instances of these objects.
Ignore new, ignore .prototype they are just confusing notions. If you really want prototypical inheritance use Object.create but most of the time inheritance is completely overkill. (prototypical inheritance should only be used as an optimization technique).
When building classes just create objects and extend them.
var Walker = {
walk: function() {}
}
var Eater = {
eat: function () {}
}
var Dog = extend({}, Eater, Walker, {
bark: function () {},
sniffBehind: function () {}
})
function dog(dogName) {
return extend({}, Dog, {
name: dogName
})
}
var steveTheDog = dog("steve")
console.log(steveTheDog.name === "steve")
Use any arbitrary arity extend function you want, _.extend, jQuery.extend, pd.extend, etc.
pd implements extend as follows
function extend(target) {
[].slice.call(arguments, 1).forEach(function(source) {
Object.getOwnPropertyNames(source).forEach(function (name) {
target[name] = source[name]
})
})
return target
}
With prototype, you can extend in a 'cleaner' way, because you are separating the logic inside your constructor function from the properties and methods that define your object.
var Mammal = function() { ... };
Mammal.prototype = {
walk: function() { ... }
};
var Dog = function() { ... };
for (var prop in Mammal.prototype) {
Dog.prototype[prop] = Mammal.prototype[prop];
}
Dog.prototype.bark = function() { ... };
However, the above without prototype could look like this:
var Mammal = function() {
this.walk = function() { ... };
};
var Dog = function() {
Mammal.apply(this);
this.bark = function() { ... };
};
This way of extending Objects is however made irrelevant by modern JavaScript libraries like Underscore.js, or could be written much cleaner with the help of for example jQuery too.
You could use the word prototype to define some functionality across the application for particular type (Array, Function, Number of custom type)
For example you can extend all arrays with property sum:
const arrayPrototype = Array.prototype
Object.defineProperty(arrayPrototype, 'sum', {
get() { return this.reduce((a,b) => a + b, 0) }
})
When you've executed the chunk of code, all arrays have the property:
console.log([1,3,-1,10].sum) // prints 13

Categories