In javascript, I have an object literal:
var objA = {
doStuff: function() {
// do objA stuff
}
}
I extend objA, and override the doStuff method:
var objB = _.extend(objA, {
doStuff: function() {
// do objB stuff
}
});
However, now when objB.doStuff() is called only objB stuff gets done.
I would like for both objA and objB stuff to be done. I tried the following:
var objB = _.extend(objA, {
doStuff: function() {
this.prototype.doStuff.call(this, arguments);
// do objB stuff
}
});
objB.__proto__ = objA;
However this doesn't work. I guess I don't understand how prototypal inheritance works when extending object literals. So, what is the right way to do this?
I would like for both objA and objB stuff to be done.
You have to call the method of objA explicitly:
var objB = _.extend({}, objA, {
doStuff: function() {
objA.doStuff.apply(this, arguments);
// do objB stuff
}
}, objA);
Note that I'm adding an empty object as first argument, so that first objA's properties are merged and then yours. For more info about how _.extend works, see the documentation.
Object Literals and Prototypal Inheritance?
Object literals currently don't provide a way to set the prototype. You can create an empty object with a specific prototype with Object.create and then merge other properties into it:
var objB = Object.create(objA);
// `merge` is a fictional function that copies properties from object to another
merge(objB, {
doStuff: function() { ... }
});
You can still only call objA's method by explicitly referencing it.
Note: This section probably has to be revised after ES6 is finalized and implement.
In ES6 you will probably be able to set the prototype via the __proto__ property name:
var objB = {
__proto__: objA,
doStuff: function() { }
};
or via Object.setPrototypeOf:
var objB = {...};
Object.setPrototypeOf(objB, objA);
Then you should be able to call objAs method via the super keyword:
var objB = {
__proto__: objA,
doStuff: function() {
super.doStuff();
}
};
Unserscore's _.extend function doesn't return a new object.
It returns the same object extended, with the new properties. So, your objA has the new doStuff function.
Instead, you may want to use _.clone and then rewrite doStuff on objB.
What you are doing in your example isn't actually inheritance, but simply creation and modification. To achieve inheritance in Javascript, it's not actually an OBJECT that you are extending, per se. It's a function. An object doesn't provide direct access to its prototype, a function does. So what you're after is likely something more like:
function ObjA() {
return this;
}
ObjA.prototype.doStuff = function() {
// do objA stuff
};
function ObjB() {
return this;
}
ObjB.prototype = new ObjA();
ObjB.prototype.doStuff = function() {
ObjA.prototype.doStuff.apply(this, arguments);
// do objB stuff
}
var objA = new ObjA(),
objB = new ObjB();
Related
i'm new to JS and got the following problem:
Why is this not working / what is this code doing?
var Test = {};
Test.prop = "test property";
Test.Class1 = function () {
}
Test.Class1.prototype = {
m1: function() {
console.log(this.prop);
}
}
Test.Class1.m1();
My understanding of this code would be:
creating a new object called Test
adding the property prop to Test
creating a new object called Class1 in Test
adding a method m1 to Class1
calling the m1 method of Class1 in Test
In JavaScript, even the prototype property of a function is an object. Prior to creating an object whose prototype is the one you've defined, Test1.Class1.prototype is just a regular object. Basically it works the same way as the following code snippet:
var Test1 = { prototype { m1: function() {} } };
// You're trying to call an undefined function!
Test.m1();
// This is fine
Test1.prototype.m1();
In the other hand, when you use the new operator, you're creating a new object whose prototype is the one set to the constructor function. And here starts the magic:
var Test1 = function() {};
Test1.prototype = {
doStuff: function() {}
};
var object1 = new Test1();
// This works!
object1.doStuff();
When you access a property, JavaScript's runtimes inspect the object to find out if there's a function called doStuff, otherwise it looks for it on the object's prototype, otherwise it looks on the prototype of the prototype and so on...
Actually new operator is a syntactic sugar. ECMA-Script 5 introduced Object.create which makes everything more clear:
var Test1 = {
doStuff: function() {
}
};
// The first parameter of Object.create is
// the object that's going to be the prototype of
// Test1 object!
var object1 = Object.create(Test1);
// This works too!
object1.doStuff();
Probably you should check this other Q&A: How does JavaScript .prototype work?
You need to create an instance of the prototype before you're able to use its methods:
var instance = new Test.Class1();
console.log(instance.m1());
Even as such, Test.prop is not part of the instance, and will not be accessible to m1
Edit: Here's a working example:
var test = function() {
this.prop = "A property";
}
test.prototype.m1 = function() {
console.log(this.prop);
}
var instance = new test();
console.log(instance.m1());
prototype chaining only works for instances created with the new operator.
Otherwise you will have to explicitly call prototype to access the method.
let testClass = new Test.Class1();
testClass.m1();
Also since you are trying to access the prop property.
Test.Class1.prototype = {
m1: function() {
console.log(this.prop);
}
}
The call site is Test.Class1, the prop should be part of Test.Class1 and not on Test
I changed up the names of things, but this should work.
var Person = function(){
this.message = "Hello, world!";
};
Person.prototype = Object.create(Object.prototype);
Person.prototype.constructor = Person;
Person.prototype.greet = function(){
console.log(this.message);
alert(this.message);
};
var tom = new Person();
tom.greet();
I've been reading Is JavaScript's "new" keyword considered harmful? and this Adobe Article on using prototypal inheritance rather than 'new'. The Adobe article has a 'new' example:
function Foo() {
this.name = "foo";
}
Foo.prototype.sayHello = function() {
alert("hello from " + this.name);
};
...that it replaces with:
var foo = {
name: "foo",
sayHello: function() {
alert("hello from " + this.name);
}
};
Here the 'sayHello' method isn't attached to the object's prototype. Doesn't this mean 'sayHello' gets unnecessarily duplicated in memory (I know V8 avoids this, but in older JS engines, for example) as it is copied to all objects that inherit from foo?
Shouldn't it be:
var foo = {
name: "foo",
prototype: {
sayHello: function() {
alert("hello from " + this.name);
}
}
};
Or similar?
The method is not attached to the prototype because this very object becomes the prototype that is attached to newly created objects with Object.create. And you don't need a prototype of the prototype.
Remember that Object.create doesn't deep clone objects. Rather, it is equivalent to something like
Object.create = function(proto) {
function F() {}
F.prototype = proto;
return new F();
}
(actual implementation is more complicated but this short version illustrates the idea)
All newly created objects "inherit" the method from the foo which acts as the prototype and there is no duplication here.
No, it won't get duplicated if you create another object using that as a protoype. The almost equivalent code using Object.create is actually slightly different, you are not using the prototype, you are just creating an object. To use prototypal inheritance, do the following. Note that using new still sets up the prototype chain so the title of the article you linked to is not very accurate and you are still sharing the properties on a single object.
var foo = {
name: "foo",
sayHello: function() {
alert("hello from " + this.name);
}
};
var extended = Object.create(foo);
var extended2 = Object.create(foo);
extended.name = "first";
extended2.name = "second";
extended.sayHello(); // hello from first
extended2.sayHello(); // hello from second
// Methods are shared, outputs true
console.log(extended.sayHello === extended2.sayHello)
// Now if you delete the property again, it will go up the chain
delete extended.name;
extended.sayHello(); // hello from foo
You could also just do
var extended = Object.create(Foo.prototype);
It would get duplicated if you create a constructor to get new instances instead of Object.create or new Foo
function createFoo() {
return {
name: "foo",
sayHello: function() {
alert("hello from " + this.name);
}
}
}
var a = createFoo();
var b = createFoo();
// The function objects are not shared
alert('Are functions the same? ' + a.sayHello === b.createFoo);
They would also not be shared if you use the closure approach to creating objects. Crockford suggests that to create truly private members. I don't use it because it doesn't use the prototype chain and inheritance is tricky to implement without just copying properties.
Function Foo() {
var name = 'foo';
this.sayHello = function () {
alert(name);
};
this.setName = function (newName) {
name = newName;
};
}
var a = new Foo();
var b = new Foo();
console.log(a.sayHello === b.sayHello); // outputs false
Going to answer my own question here, for my own notes and also to help explain to others:
Q. Using Object.create(), should exemplars have methods attached to their prototype property?
A. No, because Object.create(parentObject) itself sets up the parentObject as the prototype of a dynamically-created constructor.
Also: prototype is always a property on constructor Functions - not on regular Objects. Eg:
var someArray = [];
someArray.__proto__ === Array.prototype
Object.create() dynamically creates constructors, setting the prototype on them to the object in its first argument.
If we have a parent object like:
var Animal = function(name) {
this.name = name;
return this;
}
and we use prototype like:
Animal.prototype.Dog = function(){
console.log(this.name);
}
this just works great.
But what I am trying to achieve is to inherit the parent property in child object like
Animal.prototype.Child = {
Dog : function(){
console.log(this.name);
}
}
How can we do this. I am trying to find it for two days. I've also tried:
Animal.prototype.Child = {
that:this,
Dog : function(){
console.log(this.that.name);
}
}
But here that contains the window object not the Animal. Also
Animal.prototype.Child = {
Animal: new Animal('Puppy'),
Dog : function(){
console.log(this.Animal.name);
}
}
is NOT an option here.
Your inheritance chain doesn't look right. You'd create two different constructors. Each constructor creates an object. The inheritance part is setting up the prototype chain and calling "super" within the children class. In other words, you'd do this:
// Constructor for animals
function Animal(name) {
this.name = name;
// no need to return this
// as a constructor returns the instance
// when using the `new` keyword
}
// Public methods of all animals
Animal.prototype.say = function(){
// implement
};
// Constructor for dogs
function Dog(name) {
// Call "super", inherit parent properties
Animal.apply(this, arguments);
}
// Public methods of dogs
Dog.prototype.fetch = function(){
// implement
};
// Setup prototype inheritance chain
// and save a reference to our constructor
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Even though your inheritance didn't look right, this is a common misconception:
Animal.prototype.Child = {
that: this, //<---
...
}
this is the context of a function, and the value depends on how that function gets called. this in the code above is window; notice that there's no function.
In the code below, this is obj:
var obj = {
prop: 'foo',
method: function() {
return this.prop;
}
};
obj.method();
//^ logs "foo" because we are using dot notation (`obj` is the receiver)
If we call the function without dot notation it won't work. Again, this depends only on how the function gets called. This won't work:
var fn = obj.method;
fn(); // won't work
fn.call(obj); //=> will work, we pass the context explicitly
I am trying to extend an object, and I want to modify a method of the first, not to override it. I don't think this is clear so here is an example:
var object1 = {
whatever : function(){
console.log('first object method');
}
}
var object2 = {
whatever : function(){
console.log('second object method');
}
}
var object = $.extend(true, object1, object2);
object.whatever();
This example will output second object method, but what I want is that it ouput first object method and second object method.
Is there a way to do this?
Javascript uses prototypal inheritance, so you can make object2 inherit object1's properties and methods by doing
object2.prototype=object1;
this means that object2 will inherit all the properties and methods of object1, and any overwritten methods can be accessed at object2.prototype.method()
so parent::whatever(); is equivalent to this.prototype.whatever(); when calling from the scope of object2.
var object1 = {
whatever : function(){
console.log('first object method');
}
}
var object2 = {
whatever : function(){
console.log('second object method');
this.prototype.whatever(); // Equivalent to parent::whatever();
}
}
object2.prototype=object1; // Makes object2 inherit object1's properties and methods
object2.whatever(); // "second object method" "first object method"
I have a class definition that is part of a class definition.
var someObject = {
someClass: function() {
this.someMethod = function() {
alert('hello');
};
}
}
I have been told that I should use prototype to add methods, as it then only needs to be created once for all instances of the object. The problem is that it seems I need to add the prototyped method after the constructor function is defined, like this...
var someObject = {
someClass: function() {
}
}
someObject.someClass.prototype.someMethod = function() {
alert('hello');
};
Ideally however I would like to define the prototyped methods within the constructor function like this...
var someObject = {
someClass: function() {
this.prototype.someMethod = function() {
alert('hello');
};
}
}
This causes an error however stating that prototype is null or not an object. Is there a way to accomplish what I would like, or is this not possible?
You can make it work by using arguments.callee or - if you don't overwrite the .prototype property of your constructor function - this.constructor instead of plain this, ie
var someObject = {
someClass: function() {
// this.constructor.prototype should work as well
arguments.callee.prototype.someMethod = function() {
alert('hello');
};
}
};
However, putting the function expression back into the constructor defeats the whole purpose of the exercise - it doesn't matter that you store the reference to the function object in the prototype instead of the instance, you're still creating a new one on each constructor invocation!
One possible solution is using an anonymous constructor instead of an object literal, which gets you an additional scope for free:
var someObject = new (function() {
function someClass() {}
someClass.prototype.someMethod = function() {
alert('hello');
};
this.someClass = someClass;
});
See Paul's answer for an equivalent solution using object literals and a wrapper function, which might be more familiar.
Yes, you can't access prototype property inside the constructor of the class, but you can do a little bit different code notation, so probably it helps to you:
var someObject = {
someClass: (function wrapper() {
var ctor = function(){};
ctor.prototype.someMethod = function(){
alert('hello');
};
return ctor;
})()
}