How to access a superclass instance variable from the subclass? - javascript

everybody!
Suppose that I have this class in JavaScript:
function Animal()
{
this.name = "name";
}
Animal.prototype.someMethod =
function ()
{
}
and this subclass:
function Cat()
{
Animal.call(this);
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
Cat.prototype.someMethod =
function ()
{
// I want to access the superclass "name" instance variable here
}
What's the syntax to access the superclass "name" instance variable from the overriden method in the Cat class?
Thank you.
Marcos
UPDATED: Well, if you want to see the real code, here it is. The problem is with the abc variable (just a test variable that I was using).
var pesquisaAcervo;
$(
function ()
{
carregadoBase();
if ($("#form\\:tipoPesquisa").val() == "SIMPLES")
{
pesquisaAcervo = new PesquisaAcervoSimples();
}
else
{
pesquisaAcervo = new PesquisaAcervoAvancada();
}
pesquisaAcervo.paginaCarregada();
}
);
// --- PesquisaAcervo ----------------------------------------------------------
function PesquisaAcervo()
{
$("*:visible[id^='form:materiaisPesquisa']").
change(this.materialMudado).keyup(this.materialMudado);
this.abc = 10;
}
PesquisaAcervo.prototype.paginaCarregada =
function ()
{
$("#cabecalhoPesquisa a").click(this.exibirDicasPesquisa);
$("#cabecalhoPesquisa select").
change(function () {$("#form").submit();}).
keyup(function () {$(this).change();});
$("*:visible[class*='foco']").focus().select();
};
PesquisaAcervo.prototype.materialMudado =
function ()
{
};
PesquisaAcervo.prototype.exibirDicasPesquisa =
function ()
{
};
// --- PesquisaAcervoSimples ---------------------------------------------------
function PesquisaAcervoSimples()
{
PesquisaAcervo.call(this);
$("#form\\:campos").change(
function ()
{
$("#textoCampo").text($("#form\\:campos :selected").text() + ":");
}
).keyup(function () {$(this).change();}).change();
$("#pesquisaSimples a").click(
function ()
{
pesquisaAcervo = new PesquisaAcervoAvancada();
$("#pesquisaSimples").parent().hide();
$("#pesquisaAvancada").parent().show();
$("#form\\:tipoPesquisa").val("AVANCADO");
}
);
}
PesquisaAcervoSimples.prototype = new PesquisaAcervo();
PesquisaAcervoSimples.prototype.constructor = PesquisaAcervoSimples;
PesquisaAcervoSimples.prototype.materialMudado =
function ()
{
alert(this.abc); // "undefined" here
};
// --- PesquisaAcervoAvancada --------------------------------------------------
function PesquisaAcervoAvancada()
{
PesquisaAcervo.call(this);
}
PesquisaAcervoAvancada.prototype = new PesquisaAcervo();
PesquisaAcervoAvancada.prototype.constructor = PesquisaAcervoAvancada;

Your actual code reveals the problem. The issue is with how you're calling materialMudado. It's being invoked as the callback for an event. The keyword this inside the callback will refer to the target of the event (which has no abc property), not to the object that the function "belongs" to.
Here's a simple demonstration:
function Test() {};
Test.prototype.callback = function() {
alert(this);
}
var t = new Test();
$(document).click(t.callback);
Output (after clicking page):
[object HTMLDocument]
Compare to this:
function Test() {};
Test.prototype.callback = function() {
alert(this);
}
var t = new Test();
$(document).click(function() {
t.callback();
});
Output:
[object Object]
In this second example we close over the variable t, retaining a reference to it.
Applying this to your example produces something like this:
function PesquisaAcervo() {
var that = this;
var callback = function() {
that.materialMudado();
};
$("*:visible[id^='form:materiaisPesquisa']").
change(callback).keyup(callback);
this.abc = 10;
}

this.name should work. I don't see you overriding the name property in your Cat function so you should be able to just do this.name and the protopical chain will do the work to find the first instance of this property which should be Animal.name.

There is no such thing as an override for an instance variable. An instance variable is just a property on the this object. You can read it with:
var x = this.name;
or assign to it with:
this.name = "foo";
this.name will access the name whether you have an instance of an Animal object or an instance of a Cat object.
If you want to assign to the name property in the Cat constructor, you can just do so with
this.name = "Cat";
Once you have a working instance of an object, properties are just properties and there is no distinction for whether a property was created by a superclass or a subclass. They're just properties of the object at that point and you access all of them the same way with the this.propertyName syntax.

Just use the this keyword:
function Animal()
{
this.name = "name";
}
Animal.prototype.someMethod2 =
function ()
{
}
function Cat()
{
Animal.call(this);
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
Cat.prototype.someMethod =
function ()
{
alert(this.name);// I want to access the superclass "name" instance variable here
}
var c = new Cat();
c.someMethod();
Add this code to the bottom, I've just added an alert to your someMethod method...
In your example, Cat derives everything from Animal, so it has access to the name variable

Related

Javascript prototype method "Cannot set property"

I'm always getting Cannot set property 'saySomething' of undefined but why?
Am I making a mistake somewhere?
var Person = new Object();
Person.prototype.saySomething = function ()
{
console.log("hello");
};
Person.saySomething();
Debugging tip: You get this ..of undefined errors when you try to access some property of undefined.
When you do new Object(), it creates a new empty object which doesn't have a prototype property.
I am not sure what exactly are we trying to achieve here but you can access prototype of function and use it.
var Person = function() {};
Person.prototype.saySomething = function() {
console.log("hello");
};
var aperson = new Person();
aperson.saySomething();
The prototype property exists on functions, not on instantiated objects.
var Person = new Object();
console.log(Person.prototype); // undefined
var Person2 = function () {}
console.log(Person2.prototype); // {}
This is useful because things put on the prototype of a function will be shared by all object instances created with that function (by using new).
var Person = function() {};
Person.prototype.saySomething = function() {
console.log("hello");
};
console.log(
new Person().saySomething === Person.prototype.saySomething // true. they are the same function
);
If all you want is to add a method to the person object, there's no need for a prototype:
var Person = {};
Person.saySomething = function() {
console.log("hello");
};
Person.saySomething();
You can even use object literal syntax:
var Person = {
saySomething: function() {
console.log("hello");
}
};
Person.saySomething();
i was trying out some code thought of posting it, might help others.
<script>
var MODULE = {};
MODULE = (function (my) {
my.anotherMethod = function () {
console.log("hello ");
};
my.newMethod = function(){
console.log("hi new method ");
}
return my;
}(MODULE));
MODULE.anotherMethod();
MODULE.newMethod();
</script>
And please not var MODULE ={}, if this is not initialized with {} then it give cannot set property.
I know i am late to the party but as you see there is no satisfying answer available to the question so i am providing my own.
In your case when you write
var Person = new Object();
you are creating an instance of Object type.
You can add a property using prototype property to the Object, not to the instance of Object.which you can use by the instance laterly.
so you can define like
Object.prototype.saySomething = function ()
{
console.log("hello");
};
now you can call it like this.
Person.saySomething();
You can check here.
var Person = function(name) {
this.canTalk = true;
this.name = name;
};
Person.prototype.greet = function() {
if (this.canTalk) {
console.log('Hi, I am ' + this.name);
}
};
bob = new Person('bob');
bob.greet();

call parent ctor from ctor of inheriting class

Is it valid to instead of doing this
function Animal(name, numLegs) {
this.name = name;
this.numLegs = numLegs;
}
Animal.prototype.sayName = function() {
console.log("Hi my name is " + this.name);
};
function Penguin(name) {
this.name = name;
this.numLegs = 2;
}
Penguin.prototype = new Animal();
var penguin = new Penguin("Tux");
penguin.sayName();
do that?
function Animal(name, numLegs) {
this.name = name;
this.numLegs = numLegs;
}
Animal.prototype.sayName = function() {
console.log("Hi my name is " + this.name);
};
function Penguin(name) {
return new Animal(name, 2);
}
Penguin.prototype = new Animal();
var penguin = new Penguin("Tux");
penguin.sayName();
I find the second version more elegant and hoped both version were equivalent in their results, but for the second one codeacademy tells me
Oops, try again. Make sure to create a new Penguin instance called penguin!
while the first one is accepted.
This is not the correct way to call the parent constructor:
function Penguin(name) {
return new Animal(name, 2);
}
The correct way is as follows:
function Penguin(name) {
Animal.call(this, name, 2);
}
The reason is because of the way new works:
Let's say you have a function called ABC.
When you execute new ABC JavaScript creates an instance of ABC.prototype and binds it to this inside of the function ABC which is why you can add properties to this inside ABC.
The constructor function returns this by default unless you return another object explicitly.
The reason Codecademy complains about your code is because you're returning a new Animal(name, 2) which is not an instanceof Penguin.
As I said before the correct way to call the parent constructor is to use ParentConstructor.call(this, arg1, arg2, ...). In this case we are setting the this inside the parent constructor to the same value as this inside the current constructor (which is the instance created by new).
If you want to write elegant code then try this on for size:
function defclass(prototype) {
var constructor = prototype.constructor;
var instance = prototype.instance = function () {};
constructor.prototype = instance.prototype = prototype;
return constructor;
}
function extend(parent, keys) {
var supertype = keys.super = parent.prototype;
var prototype = new supertype.instance;
for (var key in keys) prototype[key] = keys[key];
return defclass(prototype);
}
Using defclass and extend you could rewrite your code as follows:
var Animal = defclass({
constructor: function (name, numLegs) {
this.name = name;
this.numLegs = numLegs;
},
sayName: function () {
console.log("Hi my name is " + this.name);
}
});
var Penguin = extend(Animal, {
constructor: function (name) {
this.super.constructor.call(this, name, 2);
}
});
var penguin = new Penguin("Tux");
penguin.sayName();
How cool is that?
I think the difference is that constructor functions don't return a value. So if you call
new Penguin('bla')
it is not the function Penguin that returns the new Object, it is the new that returns the new Object. So if you let Penguin() return a new Object this will conflict with the new-keyword.
If you want to call the parent-constructor, you can do that as follows:
function Penguin(name) {
Animal.call(this, name, 2);
}
Just additionally: When you assign the prototype of Animal to its sub-prototype Penguin, you call the Function in your example without its paramers. There is a cleaner way to do that:
Penguin.prototype = Object.create(Animal.prototype);
After that you have lost the constructor function of Penguin so you need to reassign it like this:
Penguin.prototype.constructor = Animal;
This is explained in detail here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/A_re-introduction_to_JavaScript
and
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript
In the second example you are NOT returning an instance of Penguin but an instance of Animal. if you want to add more functionality to penguin you need to decorate the Animal class with extra functionality.
function Penguin(name) {
var self = new Animal(name, 2);
self.penguinFunction = function (){
//do something here
}
return self;
}

javascript this in 'static method'

Class.method = function () { this.xx }
Class.prototype.method = function () { this.xx }
var clazz = new Class();
clazz.method();
When I call the 4th line this in the function will refer to clazz
But when Class.method() is executed, what will this refer to?
this within the Class.prototype.method function will still refer to the Class instance. This isn't a static method, a static (i.e. one per class) method would be something like:
Class.method = function () {
// I am a static method
};
For example:
var Example = function () {
this.name = "DefaultName";
};
Example.prototype.setName = function (name) {
this.name = name;
}
var test = new Example();
test.setName("foo");
console.log(test.name); // "foo"
If you call .method() on your constructor function itself (without new), this will still be bound to the Class object. The this value always depends on the type of invocation, since you are calling the function from within an object (= a method), this will be bound to that context.
Class = function() {
this.xx = "hello";
}
Class.method = function () { this.xx }
Class.prototype.method = function () { alert(this.xx) }
var clazz=new Class();
clazz.method(); // display "hello";
Class.method() // undefined
it will refer to the object calling the Class.method function.

Is it possible to append functions to a JS class that have access to the class's private variables?

I have an existing class I need to convert so I can append functions like my_class.prototype.my_funcs.afucntion = function(){ alert(private_var);} after the main object definition. What's the best/easiest method for converting an existing class to use this method? Currently I have a JavaScript object constructed like this:
var my_class = function (){
var private_var = '';
var private_int = 0
var private_var2 = '';
[...]
var private_func1 = function(id) {
return document.getElementById(id);
};
var private_func2 = function(id) {
alert(id);
};
return{
public_func1: function(){
},
my_funcs: {
do_this: function{
},
do_that: function(){
}
}
}
}();
Unfortunately, currently, I need to dynamically add functions and methods to this object with PHP based on user selected settings, there could be no functions added or 50. This is making adding features very complicated because to add a my_class.my_funcs.afunction(); function, I have to add a PHP call inside the JS file so it can access the private variables, and it just makes everything so messy.
I want to be able to use the prototype method so I can clean out all of the PHP calls inside the main JS file.
Try declaring your "Class" like this:
var MyClass = function () {
// Private variables and functions
var privateVar = '',
privateNum = 0,
privateVar2 = '',
privateFn = function (arg) {
return arg + privateNum;
};
// Public variables and functions
this.publicVar = '';
this.publicNum = 0;
this.publicVar2 = '';
this.publicFn = function () {
return 'foo';
};
this.publicObject = {
'property': 'value',
'fn': function () {
return 'bar';
}
};
};
You can augment this object by adding properties to its prototype (but they won't be accessible unless you create an instance of this class)
MyClass.prototype.aFunction = function (arg1, arg2) {
return arg1 + arg2 + this.publicNum;
// Has access to public members of the current instance
};
Helpful?
Edit: Make sure you create an instance of MyClass or nothing will work properly.
// Correct
var instance = new MyClass();
instance.publicFn(); //-> 'foo'
// Incorrect
MyClass.publicFn(); //-> TypeError
Okay, so the way you're constructing a class is different than what I usually do, but I was able to get the below working:
var my_class = function() {
var fn = function() {
this.do_this = function() { alert("do this"); }
this.do_that = function() { alert("do that"); }
}
return {
public_func1: function() { alert("public func1"); },
fn: fn,
my_funcs: new fn()
}
}
var instance = new my_class();
instance.fn.prototype.do_something_else = function() {
alert("doing something else");
}
instance.my_funcs.do_something_else();
As to what's happening [Edited]:
I changed your my_funcs object to a private method 'fn'
I passed a reference to it to a similar name 'fn' in the return object instance so that you can prototype it.
I made my_funcs an instance of the private member fn so that it will be able to execute all of the fn methods
Hope it helps, - Kevin
Maybe I'm missing what it is you're trying to do, but can't you just assign the prototype to the instance once you create it? So, first create your prototype object:
proto = function(){
var proto_func = function() {
return 'new proto func';
};
return {proto_func: proto_func};
}();
Then use it:
instance = new my_class();
instance.prototype = proto;
alert(instance.prototype.proto_func());

Calling method using JavaScript prototype

Is it possible to call the base method from a prototype method in JavaScript if it's been overridden?
MyClass = function(name){
this.name = name;
this.do = function() {
//do somthing
}
};
MyClass.prototype.do = function() {
if (this.name === 'something') {
//do something new
} else {
//CALL BASE METHOD
}
};
I did not understand what exactly you're trying to do, but normally implementing object-specific behaviour is done along these lines:
function MyClass(name) {
this.name = name;
}
MyClass.prototype.doStuff = function() {
// generic behaviour
}
var myObj = new MyClass('foo');
var myObjSpecial = new MyClass('bar');
myObjSpecial.doStuff = function() {
// do specialised stuff
// how to call the generic implementation:
MyClass.prototype.doStuff.call(this /*, args...*/);
}
Well one way to do it would be saving the base method and then calling it from the overriden method, like so
MyClass.prototype._do_base = MyClass.prototype.do;
MyClass.prototype.do = function(){
if (this.name === 'something'){
//do something new
}else{
return this._do_base();
}
};
I'm afraid your example does not work the way you think. This part:
this.do = function(){ /*do something*/ };
overwrites the definition of
MyClass.prototype.do = function(){ /*do something else*/ };
Since the newly created object already has a "do" property, it does not look up the prototypal chain.
The classical form of inheritance in Javascript is awkard, and hard to grasp. I would suggest using Douglas Crockfords simple inheritance pattern instead. Like this:
function my_class(name) {
return {
name: name,
do: function () { /* do something */ }
};
}
function my_child(name) {
var me = my_class(name);
var base_do = me.do;
me.do = function () {
if (this.name === 'something'){
//do something new
} else {
base_do.call(me);
}
}
return me;
}
var o = my_child("something");
o.do(); // does something new
var u = my_child("something else");
u.do(); // uses base function
In my opinion a much clearer way of handling objects, constructors and inheritance in javascript. You can read more in Crockfords Javascript: The good parts.
I know this post is from 4 years ago, but because of my C# background I was looking for a way to call the base class without having to specify the class name but rather obtain it by a property on the subclass. So my only change to Christoph's answer would be
From this:
MyClass.prototype.doStuff.call(this /*, args...*/);
To this:
this.constructor.prototype.doStuff.call(this /*, args...*/);
if you define a function like this (using OOP)
function Person(){};
Person.prototype.say = function(message){
console.log(message);
}
there is two ways to call a prototype function: 1) make an instance and call the object function:
var person = new Person();
person.say('hello!');
and the other way is... 2) is calling the function directly from the prototype:
Person.prototype.say('hello there!');
This solution uses Object.getPrototypeOf
TestA is super that has getName
TestB is a child that overrides getName but, also has
getBothNames that calls the super version of getName as well as the child version
function TestA() {
this.count = 1;
}
TestA.prototype.constructor = TestA;
TestA.prototype.getName = function ta_gn() {
this.count = 2;
return ' TestA.prototype.getName is called **';
};
function TestB() {
this.idx = 30;
this.count = 10;
}
TestB.prototype = new TestA();
TestB.prototype.constructor = TestB;
TestB.prototype.getName = function tb_gn() {
return ' TestB.prototype.getName is called ** ';
};
TestB.prototype.getBothNames = function tb_gbn() {
return Object.getPrototypeOf(TestB.prototype).getName.call(this) + this.getName() + ' this object is : ' + JSON.stringify(this);
};
var tb = new TestB();
console.log(tb.getBothNames());
function NewClass() {
var self = this;
BaseClass.call(self); // Set base class
var baseModify = self.modify; // Get base function
self.modify = function () {
// Override code here
baseModify();
};
}
An alternative :
// shape
var shape = function(type){
this.type = type;
}
shape.prototype.display = function(){
console.log(this.type);
}
// circle
var circle = new shape('circle');
// override
circle.display = function(a,b){
// call implementation of the super class
this.__proto__.display.apply(this,arguments);
}
If I understand correctly, you want Base functionality to always be performed, while a piece of it should be left to implementations.
You might get helped by the 'template method' design pattern.
Base = function() {}
Base.prototype.do = function() {
// .. prologue code
this.impldo();
// epilogue code
}
// note: no impldo implementation for Base!
derived = new Base();
derived.impldo = function() { /* do derived things here safely */ }
If you know your super class by name, you can do something like this:
function Base() {
}
Base.prototype.foo = function() {
console.log('called foo in Base');
}
function Sub() {
}
Sub.prototype = new Base();
Sub.prototype.foo = function() {
console.log('called foo in Sub');
Base.prototype.foo.call(this);
}
var base = new Base();
base.foo();
var sub = new Sub();
sub.foo();
This will print
called foo in Base
called foo in Sub
called foo in Base
as expected.
Another way with ES5 is to explicitely traverse the prototype chain using Object.getPrototypeOf(this)
const speaker = {
speak: () => console.log('the speaker has spoken')
}
const announcingSpeaker = Object.create(speaker, {
speak: {
value: function() {
console.log('Attention please!')
Object.getPrototypeOf(this).speak()
}
}
})
announcingSpeaker.speak()
No, you would need to give the do function in the constructor and the do function in the prototype different names.
In addition, if you want to override all instances and not just that one special instance, this one might help.
function MyClass() {}
MyClass.prototype.myMethod = function() {
alert( "doing original");
};
MyClass.prototype.myMethod_original = MyClass.prototype.myMethod;
MyClass.prototype.myMethod = function() {
MyClass.prototype.myMethod_original.call( this );
alert( "doing override");
};
myObj = new MyClass();
myObj.myMethod();
result:
doing original
doing override
function MyClass() {}
MyClass.prototype.myMethod = function() {
alert( "doing original");
};
MyClass.prototype.myMethod_original = MyClass.prototype.myMethod;
MyClass.prototype.myMethod = function() {
MyClass.prototype.myMethod_original.call( this );
alert( "doing override");
};
myObj = new MyClass();
myObj.myMethod();

Categories

Resources