I just cleared my mind on JavaScript objects and my question is really simple for most of the people here. I feel comfortable with the JavaScript object literal notation like:
var Obj = {
/**
* #type {string}
*/
name: '',
/**
* setName
* Set the `name` property.
*
* #param param
*/
set setName (param) {
this.name = param;
}
};
The only limit I found is that if I want to create two completely separate objects in the same page, with this notation I can't.
var a = Obj;
a.setName = "John";
var b = Obj;
b.setName = "Peter";
// OUTPUT
// a.name -> Peter
// b.name -> Peter
Set var whatever = Obj is just useless, 'cause it doesn't instantiate n separate objects and it just overwrites the code above. Using new keyword such as var a = new Obj doesn't work neither (probably I'm using it in the wrong way?). The solution I came up with is returning the object inside a function, like:
function Obj() {
return {
/**
* #type {string}
*/
name: '',
/**
* setName
* Set the `name` property.
*
* #param param
*/
set setName (param) {
this.name = param;
}
}
}
This way I can create two different objects and correctly access to their properties and methods:
var a = Obj();
a.setName = "John";
var b = Obj();
b.setName = "Peter";
// OUTPUT
// a.name -> John
// b.name -> Peter
So, my question are:
Is what I've done conceptually right?
Is there a more correct/efficient way to achieve it?
Your concept of a function that returns an Object instance is valid, but your implementation is very brittle because it is only set up to work with specific properties. Read on for more details on various ways to create instances and a more flexible way to return objects...
var a = Obj; doesn't create a new object. It just assigns a the memory address of the existing object Obj.
var myObj = {}; // Object instance is created and memory location is stored in myObj
var a = myObj; // No new object is created. a and myObj point to the same object
console.log("Are 'a' and 'myObj' both pointing to the same object?", a === myObj); // true
If you want to design a single object and then make more of that object, you need to be able to create "instances" of an object. You can't do that directly with an object literal:
var myObj = {
someProp:10
};
var myNewObj = new myObj(); // Fails because an object literal can't be instantiated
But, you can do it with the Object.create() method, which takes your Obj concept to fruition:
// Object instance is created and memory location is stored in myObj
var myObj = {
someProp: "default",
// "Methods" are just properties with functions as their value
someMethod: function(input){
// The || syntax that follows allows for a default value for the method
// if no argument is passed to the method.
this.name = input || "default";
}
};
// Create a new Object instance and set myObj as the prototype of the instance.
// This means that the new instance will inherit from that prototype:
var a = Object.create(myObj);
console.log(a.someProp); // "default";
a.someProp = "something specific";
a.someMethod("Test");
myObj.someMethod();
console.log(a.name, myObj.name); // "Test" "default"
console.log(a.someProp, myObj.someProp); // "something specific", "default"
Instances can be explicitly made with the new operator and a constructor function:
function foo(){
this.someProp = "something";
}
var a = new foo(); // Unique instance of foo
var b = new foo(); // Separate and distinct instance of foo
a.someProp = 10;
b.someProp = 20;
console.log(a.someProp, b.someProp); // 10 20
Or, the new operator and a class:
class foo{
constructor(val) {
this.someProp = val;
}
}
var a = new foo(10); // Unique instance of foo
var b = new foo(20); // Separate and distinct instance of foo
console.log(a.someProp, b.someProp); // 10 20
Have you tried with Object.create() ?
var b = { setName : "Mongo" };
a = Object.create(b);
a.setName = "John";
b.setName = "Peter";
console.log(a.setName);
console.log(b.setName);
Related
I'm not very well aquainted with javascript inheritance, and I'm trying to make one object inherit from another, and define its own methods:
function Foo() {}
Foo.prototype = {
getColor: function () {return this.color;},
};
function FooB() {}
FooB.prototype = new Foo();
FooB.prototype = {
/* other methods here */
};
var x = new FooB().getColor();
However, the second one overwrites the first one(FooB.prototype = new Foo() is cancelled out). Is there any way to fix this problem, or am I going in the wrong direction?
Thanks in advance, sorry for any bad terminology.
Each object can only have one prototype, so if you want to add to the prototype after inheriting (copying) it, you have to expand it instead of assigning a new prototype. Example:
function Foo() {}
Foo.prototype = {
x: function(){ alert('x'); },
y: function(){ alert('y'); }
};
function Foo2() {}
Foo2.prototype = new Foo();
Foo2.prototype.z = function() { alert('z'); };
var a = new Foo();
a.x();
a.y();
var b = new Foo2();
b.x();
b.y();
b.z();
One solution would be:
function FooB() {}
var p = new Foo();
p.methodA = function(){...}
p.methodB = function(){...}
p.methodC = function(){...}
...
FooB.prototype = p;
Update: Regarding expanding with an existing object. You can always copy the existing properties of one object to another one:
FooB.prototype = new Foo();
var proto = {
/*...*/
};
for(var prop in proto) {
FooB.prototype[prop] = proto[prop];
}
As long as proto is a "plain" object (i.e. that does not inherit from another object) it is fine. Otherwise you might want to add if(proto.hasOwnProperty(prop)) to only add non-inherited properties.
You can use an extend function which copies the new members to the prototype object.
function FooB() {}
FooB.prototype = new FooA();
extend(FooB.prototype, {
/* other methods here */
});
extend
/**
* Copies members from an object to another object.
* #param {Object} target the object to be copied onto
* #param {Object} source the object to copy from
* #param {Boolean} deep whether the copy is deep or shallow
*/
function extend(target, source, deep) {
for (var i in source) {
if (deep || Object.hasOwnProperty.call(source, i)) {
target[i] = source[i];
}
}
return target;
}
I am new to Javascript sorry if this sounds as an easy question.
If I have following constructor function:
function Person(x,y){
this.name = x;
this.surname = y;
}
I am curious whether properties name and surname are considered own properties of objects of type Person or inherited?
e.g.
var x = new Person("John", "Doe");
I did some tests using hasOwnProperty which suggest they are considered own properties rather than inherited, just wanted to verify.
Yes, they are own properties. When instantiating an object with new Person, an object will be created, your function Person will be called, and this inside Person refers to this new object. You're then explicitly directly setting properties on this object. In essence, no different than this:
function person(obj) {
obj.name = 'Foo';
obj.surname = 'Bar';
}
var o = {};
person(o);
o.name // Foo
The easiest way for you to check this is to just try it and see.
But yes, they are considered own properties. Inside the constructor function, this is a reference to the newly constructed object (when Person is called with new).
It's essentially the same thing as doing
var x = {};
x.name = 'foo'
console.log(x.hasOwnProperty('name')); // true
Compare that to a prototype property:
function Person(name) {
this.name = name;
}
Person.prototype.brain = 'meat-like';
var p = new Person('Bob');
console.log(p.name); // Bob
console.log(p.brain); // meat-like
console.log(p.hasOwnProperty('name')); // true
console.log(p.hasOwnProperty('brain')); // false
this is prototypal model in javascript.
unlike use constructor function with new keyword in this pattern we are using
existing object to create a new object as shown below
var answerprototype = {
name:'ss',
get:function fn1()
{
return "--";
}
}
var lifeanswer = Object.create(answerprototype);
this is how implement private ,public members in javascript using classical pattern
function Restaurant()
{
//private varible
var myPrivateVar = 'aaa';
//public variable
this.myPublicVar = 'bbbb'
//private method
// Only visible inside Restaurant()
var private_stuff = function()
{
return "private metho";
}
//public method
// use_restroom is visible to all
this.use_restroom = function()
{
return "public method"
}
}
//create object
var r1 = new Restaurant();
r1.myPrivateVar //return undefined
r1.myPublicVar //return "bbbb"
r1.private_stuff() //error
r1.use_restroom() //return "public method"
this is how implement static members in javascript using classical pattern
function Shape(shapeName)
{
//instance field
this.ShapeName = shapeName;
//static field
Shape.Count = ++Shape.Count;
//static method
Shape.ShowCount = function()
{
return Shape.Count;
}
}
var shape1 = new Shape("circle");
var shape1 = new Shape("rectangle");
var shape1 = new Shape("Triangle");
Shape.ShowCount(); //return 3
i want to implement static members and private ,public members in javascript using prototypal pattern(not using new keyword with constructor function)
how to do it
In JavaScript, private doesn't mean what you think it means. You have to create a closure which is where you trap your "private" values.
Using a function is the traditional way to create a closure
function makeProto(chainTo) {
const secret = 'foo';
return Object.assign(Object.create(chainTo || null), {
get hello() {return secret;}
});
}
let fizz = makeProto();
fizz.hello; // "foo"
Object.prototype.hasOwnProperty.call(fizz, 'hello'); // true
With ES6, you can use the block-scoped let or const in combination with the function scoped var to achieve something similar
;{
let foo = 'bar';
var obj = {
get baz() {return foo;}
};
}
obj.baz; // "bar"
foo; // ReferenceError
Please remember that as soon as you inherit from one of these objects, all descendant objects will share the same references to these values, not create new ones
I wrote ;{ /* ... */ } so if you try the code in your console it doesn't attempt to interpret this pattern as an Object literal, but instead a code block. In production the initial ; is not necessary.
var someObject = function(arg) {
this.property = function() {
// do something with the argument
return arg;
}();
};
var obj = new someObject(some argument);
// object.property instanceof "someObject" should be true
When property of someObject is used, a new instance of newObject should be created. For example, when I use the native DOM Element's nextSibling property, a new DOM Element object instance is returned. I wonder if it is possible to create a similar structure. Or would such cause infinite recursion?
Strictly speaking, this is possible in ES5 (all latest browsers, yes that includes IE).
ES5 specifies getters and setters via the get and set keyword or the Object.defineProperty function so you can make functions behave like properties (think innerHTML). Here's how you can do it:
function Mother () {
this.name = '';
Object.defineproperty(this,'child',{
get: function(){
return new Mother();
}
});
}
So the object can now create new instances of itself simply by reading the child property:
var a = new Mother();
a.name = 'Alice';
b = a.child;
b.name = 'Susan';
alert(a.name) // Alice
alert(b.name) // Susan
a instanceof Mother; // true
b instanceof Mother; // true
Having said that, your observation about DOM elements is wrong. The DOM is simply a tree structure. You can create a similar structure yourself using old-school javascript:
function MyObject () {}
var a = new MyObject();
var b = new MyObject();
var c = new MyObject();
a.children = [b,c];
b.nextSibling = c;
c.prevSibling = b;
// now it works like the DOM:
b.nextSibling; // returns c
a.children[1]; // returns c
b.nextSibling.prevSibling instanceof MyObject; // true
No, that's not possible. You could set function to the property, but anyway, you will need to invoke function somehow (with property() notation or with call/apply), because function it's an object itself, and only () or call/apply say to interpreter that you want to execute code, but not only get access to function's object data.
Your understanding of the nextSibling property in the DOM is incorrect. It does not create a new DOMElement, it simply references an existing DOM Node.
When you create a sibling of an element to which you have a reference (e.g., via jQuery or document.createElement), the browser knows to update sibling and parent/child references.
So, the behavior you're trying to emulate doesn't even exist.
As others have intimated, simply accessing a property on an object is not sufficient to get the Javascript interpreter to "do" anything (other than deference the name you're looking up). You'll need property to be a function.
nextSibling doesn't return a new element, it returns an existing element which is the next sibling of the target element.
You can store an object reference as a property of another object just like you can store primitive values.
function SomeObject(obj) {
this.obj = obj;
}
var someObject = new SomeObject(new SomeObject());
someObject.obj instanceof SomeObject //true
However if you want to create a new instance of SomeObject dynamically when accessing someObject.obj or you want to return an existing object based on conditions that shoul be re-evaluated every time the property is accessed, you will need to use a function or an accessor.
function SomeObject(obj) {
this.obj = obj;
}
SomeObject.prototype.clone = function () {
//using this.constructor is a DRY way of accessing the current object constructor
//instead of writing new SomeObject(...)
return new this.constructor(this.obj);
};
var someObject = new SomeObject(new SomeObject());
var someObjectClone = someObject.clone();
Finally with accessors (be aware that they aren't cross-browser and cannot be shimmed)
function SequentialObj(num) {
this.num = num;
}
Object.defineProperty(SequentialObj.prototype, 'next', {
get: function () {
return new this.constructor(this.num + 1);
},
configurable: false
});
var seq = new SequentialObj(0);
console.log(seq.next); //SequentialObj {num: 1}
console.log(seq.next.next.next); //SequentialObj {num: 3}
If you want this.property() to return a new someObject you can write the class as follows:
var someObject = function(arg) {
this.arg = arg;
};
someObject.prototype.property = function(arg) {
// do something with the argument
return new someObject(arg||this.arg);
}();
var obj = new someObject(/*some argument*/);
// object.property instanceof "someObject" should be true
If you want it to return some already instantiated version you can write the code as follows:
var someObject = (function() {
var previous;
function(arg) {
this.arg = arg;
this.propertyBefore = previous;//refers to the someObject created before this one
if(previous) previous.property = this; //before.property now references this class
//this.property will be undefined until another instance of someObject is created
previous = this;
};
})()
var obj = new someObject(/*some argument*/);// returns someObject already created earlier (similar to nextSibling)
One small note - its best practice in javascript to declare class names with a capitalized name (SomeObject rather than someObject)
I find this is most recommended way to do inheritance in javascript.
function extend(Child, Parent) {
var F = function(){};
F.prototype = Parent.prototype;
Child.prototype = new F();
}
what if I already have methods in child's prototype, aren't they will overwrite, shouldn't we preserve them.
function extend(Child, Parent) {
var c = child.prototype;
var oldProto = new C();
var F = function(){};
F.prototype = Parent.prototype;
Child.prototype = new F();
for(var i in oldProto ){
Child.prototype[i] = oldProto[i]
}
}
I'm not sure if this is any good to you, but it's well important to remember: prototypes are not the same things as classes. What you're doing is trying to make JS behave like a traditional OO language, which is trying to teach a dolphin to dance ballet, or forcing a tiger to become vegan: Admirable, but destined to end in tears.
I can't really see why you'd want to use the extend function to do whatever it is you're trying to do. Why not simply use this:
function Parent()
{};
function Child()
{};
//augment parent proto
Parent.prototype.parentMethod1 = function()
{};
//set Child's proto to Parent
Child.prototype = new Parent();
Child.prototype.constructor = Child;
//Then augment the Child's prototype
Child.prototype.childMethod1 = function()
{};
var foo = new Child();
foo.parentMethod1();//works
foo.childMethod1();//works, too
IMO, this solves the problem entirely. Sure, it's a tad more verbose, but OOP always is.
The pattern you're trying to achieve is called multiple inheritance. And it's highly not recommended for the use because of the issue you're experiencing, called diamond problem. Just use mixin pattern instead.
The code below is the one of the best I have seen for doing inheritance in JavaScript.
Object.create(proto [, propertiesObject ]) is discussed on MDN here.
Below, Jon defines a base empty object called ExtendBase then adds a function property called extend which is not enumerable which takes as its argument a single new object.
That object should contain enumerable properties such as methods and data that will be added to the base object.
He gets all the enumerable properties from the passed object, then creates an array of the necessary descriptors to pass into Object.create using those properties' names. He then uses the parent object as the prototype and resultant descriptors as new properties to be added to the child object directly in the Object.create() call.
As you can see, you can use an object argument with properties, including methods, to extend a parent without losing that passed object's properties with the result being a child object with the parent as the prototype and the enumerable objects of the passed object added directly to the child.
However, this maintains a clean prototype chain while intending to extend parent objects using other objects which are created sanely to extend the parent into a new child in a way that makes sense:
Live sample here (Press F12 in Chrome for console output, or use FireBug in FireFox, etc.)
JavaScript:
// Original Author: FireFly - Jonas Höglund - ##javascript channel
// on irc.freenode.net - see THANKS File. Updated to private data
// members and passable initial parameters by Scott Sanbar
///////////////
// Library code
///////////////
var ExtendBase = {};
Object.defineProperty(ExtendBase, 'extend', {
enumerable:false, value:function (obj) {
'use strict';
var descs = {};
Object.getOwnPropertyNames(obj).forEach(function (key) {
descs[key] = Object.getOwnPropertyDescriptor(obj, key)
});
return Object.create(this, descs);
}
});
///////////////
// Sample Usage
///////////////
function PersonObj(nam) {
return {
name:new function () {
var name = nam;
this.set = function (value) {
name = value;
};
this.get = function () {
return name;
}
},
// A person can tell you its name.
talk:function () {
return "Hello, I'm " + this.name.get();
}
}
}
;
function WorkingPersonObj(occ) {
return {
occupation:new function () {
var occupation = occ;
this.set = function (value) {
occupation = value;
};
this.get = function () {
return occupation;
}
},
// A working person also tells you their occupation when they talk.
talk:function () {
return Person.talk.call(this) + " and I am a " + this.occupation.get();
}
}
}
;
var hush = {
hush:function () {
return "I am supposed to be quiet";
}
};
var Person = ExtendBase.extend(new PersonObj('Harry'));
var WorkingPerson = Person.extend(new WorkingPersonObj('wizard'));
var wp1 = WorkingPerson.extend(hush);
console.log(wp1.talk()); // "Hello, I'm Harry and I am a wizard"
console.log(wp1.hush()); // "I am supposed to be quiet"
wp1.name.set("Elijah");
wp1.occupation.set("prophet");
console.log(wp1.talk()); // "Hello, I'm Elijah and I am a prophet"
console.log(wp1.name.get());
console.log(wp1.occupation.get());