This is a follow-up question from the previous: Why is the parent prototype method still executable after overriding?.
As we see, the literal object {metadata, aaa, init} was initialized by the extend function as a 2nd import variable, so this object should be the object automatically automatically instantiated and represents the extended sub-class. Then inside the object, it inherited the init method from its parent class and overrode it. first call the parent init method to trigger some necessary things, then setup the data for this extended component.
My question is: the init method in this literal object is its own init method, because this refered to the literal object, right? if it's yes, why I can't see it under this proprieties in my debugger tool.
enter image description here
sap.ui.define([
"sap/ui/core/UIComponent",
"sap/ui/model/json/JSONModel",
"sap/ui/model/resource/ResourceModel",
], function (UIComponent, JSONModel, ResourceModel) {
"use strict";
return UIComponent.extend("sap.ui.demo.walkthrough.Component", {
metadata: {
"interfaces": [
"sap.ui.core.IAsyncContentCreation",
],
"rootView": {
"viewName": "sap.ui.demo.walkthrough.view.App",
"type": "XML",
/*"async": true, // implicitly set via the sap.ui.core.IAsyncContentCreation interface*/
"id": "app",
},
},
aaa: function () {
console.log("aaa");
},
init: function () {
// call the init function of the parent
UIComponent.prototype.init.apply(this, arguments);
// set data model
var oData = {
recipient: {
name: "World",
},
};
var oModel = new JSONModel(oData);
this.setModel(oModel);
// set i18n model
var i18nModel = new ResourceModel({
bundleName: "sap.ui.demo.walkthrough.i18n.i18n",
});
this.setModel(i18nModel, "i18n");
},
});
});
Hope this other answer clears some things up:
__proto__ is the actual object that is used in the lookup chain to resolve methods, etc. prototype is the object that is used to build __proto__ when you create an object with new
This means when you see methods in the prototype section it doesn't mean that the original parent class is overwritten. Your new "class" sap.ui.demo.walkthrough.Component also has/is a prototype and this (which is an instance of your new "class") has it's own __proto__. This new prototype/proto constains methods form the parent prototype and can also define new methods.
And from the documentation of sap.ui.base.Object.extend:
Creates a subclass of class sap.ui.base.Object with name sClassName and enriches it with the information contained in oClassInfo.
oClassInfo might contain three kinds of information:
[...]
any-other-name: any other property in the oClassInfo is copied into the prototype object of the newly created class. Callers can thereby add methods or properties to all instances of the class. [...]
So when you do this.__proto you will see the build plan for the current instance.
Do this.__proto__.__proto__ to get the build plan from the parent class. Do this.__proto__.__proto__.__proto__ to get the grand parent class (and so on).
The protos of the parent class are not affected by your child class. But your child classes include everything from the parent class. When you "override" a method you are are adding a method to your new prototype. The parent method still exists in the UIComponent prototype and can therefore be called by explicitly stating UIComponent.prototype.init.apply.
Related
This question is about Javascript, however the first programming language I learned was Java so I relate to that the most easily.
In Java, objects have access to static members, as demonstrated in the article Understanding Class Members by Oracle:
public class Bicycle {
// INSTANCE fields
private int cadence;
private int gear;
private int speed;
// CONSTRUCTOR
constructor Bicycle(c,g,s) { /*...*/ }
//...
// STATIC fields
private static int numberOfBicycles = 0;
}
You can also refer to static fields with an object reference like
myBike.numberOfBicycles
In addition to objects having access to static fields, subclasses do as well, as mentioned in Inheritance.
public class MountainBike extends Bicycle {
public int seatHeight;
constructor MountainBike(c,g,s,h) { /*...*/ }
private static int mountainBikePrice = 324;
}
MountainBike now has 4 instance fields: cadence, gear, speed, and seatHeight; and 2 static fields: numberOfBicycles, and mountainBikePrice.
I would like to emulate this behavior in my Javascript program, however, since JS is prototype-oriented and not object-oriented, objects and subclasses cannot have access to their "class" members.
// constructor
function Bicycle(c,g,s) {
this.cadence = c
this.gear = g
this.speed = s
}
// instance methods
Bicycle.prototype.go = function () { /*...*/ }
Bicycle.prototype.stop = function () { /*...*/ }
// STATIC field
Bicycle.numberOfBicycles = 0 // <-- I want THIS field inherited
When I create a new Bicycle object via var myBike = new Bicycle(), I have access to the instance methods myBike.go(), etc., however I cannot get the static field via myBike.numberOfBicycles as I could in Java. The only way to get this is by calling Bicycle.numberOfBicycles.
Additionally, when I create a new subclass, it won't have access to its parent’s fields.
// subclass constructor
function MountainBike(c,g,s,h) {
Bicycle.call(this,c,g,s)
this.seatHeight = h
}
// vvvvvvvv extension mechanism... ignore this vvvvvvvvvv
MountainBike.prototype = Object.create(Bicycle.prototype)
MountainBike.prototype.constructor = MountainBike
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// STATIC field
MountainBike.mountainBikePrice = 324
If I call MountainBike.numberOfBicycles I get undefined (which would not happen in Java—it would return the parent’s value).
Other than assigning Bicycle.prototype.numberOfBicycles = 0, there must be an easier way for objects to inherit properties of their class, and subclasses to inherit properties of their parent.
Is there a way to access the object’s class, such as myBike.class? If so, then I could call myBike.class.numberOfBicycles.
Is there a way to access a subclass’s parent class, something like MountainBike.super?
Actually, the property you are looking for is constructor (as in myBike.constructor.numberOfBicycles works as intended).
The constructor of an object is (kind of) like the class of an instance in Java. It's probably best to take this with a grain of salt because the concepts are not exactly interchangeable.
Edit:
So I see you are basically trying to create some full implementation of "classes" in JavaScript using prototype and constructor hacks (which is fine really). Backbone has an interesting approach to this in their extend function. Relevant source is the following (see bottom of question for license of this code):
var extend = function(protoProps, staticProps) {
var parent = this;
var child;
// Inherits constructor if not specified in child props.
if (protoProps && _.has(protoProps, 'constructor')) {
child = protoProps.constructor;
} else {
child = function(){ return parent.apply(this, arguments); };
}
// copy all properties from parent and from static properties
// into child
_.extend(child, parent, staticProps);
// figure out child prototype (kind of like you had)
child.prototype = Object.create(parent.prototype);
_.extendOwn(child, protoProps);
child.prototype.constructor = child;
// Save super for later access (answers question 2)
child.__super__ = parent.prototype;
return child;
};
Annotations are mine. I edited the code to expose some similarities with what the OP had posted (i.e used Object.create instead of _.create). The way to use it is to set in the parent Parent.extend = extend; and then create the child class with Child = Parent.extend({...new properties and methods...},...).
I decided to go ahead and post the entire function because it might contain solutions to problems you haven't asked about yet (such as inheritance of constructors). Specifically for question 2, the Backbone devs made a decision to explicitly save the parent class in the child with __super__, so you could do that as well in your inheritance code (after you call the Object.create).
It also might just make more sense to use ES2015 Classes and inheritance with extends (here is some good docs on it). The support for it is not great, so you would need to use a preprocessor such as Babel. Here is an example of these classes:
class Parent {...}
class Child extends Parent {...}
License of Backbone code fragment posted:
(c) 2010-2016 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors.
Backbone may be freely distributed under the MIT license.
For all details and documentation:
http://backbonejs.org
Say I have a custom component as my-panel
And in the backing JS (my-panel.js)
this.get('targetObject')
Also I reference/use this component from another custom component say my-sections.hbs as
{{my-panel}}
My question is when I do
this.get('targetObject') //Called inside my-panel.js
How/Where does it lookup the property targetObject in ? What does the chain look like?
It looks it up within the scope of the backing custom component js (probably).
The chain/scope really starts from the this object, so depending what this is in that scope, that's really where it is looking it up on. If you had a reference to a different object, or were within a callback function my statement may not be true.
App.MyCompComponent = Ember.Component.extend({
foo: 'bar',
actions: {
doit: function(){
// `this` is the scope of an instance of this component.
alert(this.get('foo'));
}
}
});
http://emberjs.jsbin.com/totibidohe/edit?html,js,output
You can think of it outside the scope of being in the template as well, and just create an instance and reference that instance and it would be the same.
var MyCompComponent = Ember.Component.extend({
foo: 'bar',
actions: {
doit: function(){
alert(this.get('foo'));
}
}
});
var j = MyCompComponent.create();
alert(j.get('foo'));
http://emberjs.jsbin.com/xekohoguto/1/edit?html,js,output
I have a very simple extension in Ext JS from Ext.form.Panel:
Ext.define('path.to.SomeClass', {
extend : 'Ext.form.panel',
xtype : 'some-class'
config : {
hasDog : true
},
constructor : function (config) {
if (this.config.hasDog) {
// do something dog related
} else {
// do something not dog related
}
}
});
I then have a "container" for this custom componet:
Ext.define('path.to.OtherClass', {
extend : 'Ext.window.Window',
// ....
items : [{
xtype : 'some-class',
hasDog : false
}]
});
However, for some reason unknown to me, the if...else evaluation in SomeClass is always picking up the default configuration for hasDog. Am I not correctly configuring some-class in OtherClass's items config?
To add a bit more context, OtherClass is called via some using code:
var window = Ext.create('path.to.OtherClass');
window.show();
From what I can see, the above is pretty standard stuff - at least in thought.
The reason why you always get the default configuration is because you're accessing this.config, which is the declaration of your configuration, instead of the actual configuration from the constructor argument. So either use config or - once you've called the parent class constructor - this.
constructor : function (config) {
// before parent class constructor or this.initConfig was called:
console.log(config.hasDog);
// call parent class constructor
this.callParent(arguments);
// after parent class constructor or this.initConfig was called:
console.log(this.hasDog);
}
Also have a look at the documentation:
Note: You need to make sure Ext.Base.initConfig is called from your constructor if you are defining your own class or singleton, unless you are extending a Component. Otherwise the generated getter and setter methods will not be initialized.
In your case, since your extending a component, calling the parent constructor should suffice (as shown in the example above).
I've spent a lot of time debugging strage error in my component. That component have disabled/enabled buttons, but I haven't seen the effect. After a while, I've noticed, the buttons are changed in the last component instance I've created. The declaration looks so:
constructor: function(options) {
for(var i in options){
this[i] = options[i];
}
},
domNode: null,
grid: null,
data: [],
buttons: {},
In the debug, I've seen, that when I create second instance of my object:
new CustomComponent({domNode: dojo.byId('secondid')})
the buttons are already set - their instance is shared by all instances!
In end effect, I've created a static variable in my component. This is not what I wanted! What is wrong in that declaration? How should I made 'buttons' instance separate for each component instance?
I suppose that CustomComponent is a widget? Then you're doing some stuff wrong. The thing you do in your constructor (I suppose that's to populate your widget properties?) is not even necessary since that's already there by default when you use dijit/_WidgetBase.
Same with your property domNode, it's also there already by default if you use dijit/_WidgetBase.
My guess is that by overriding the constructor to act like this, you're actually doing some steps that the WidgetBase should do and thus messing up the private scope of the properties.
An example widget:
var CustomComponent = declare("my/CustomComponent", [WidgetBase], {
grid: null,
data: [],
buttons: {}
});
This code does exactly the same as your widget and is a lot shorter.
An example JSFiddle that has instance scoped properties (as you can see in the console log).
I've inspected the problem more exact. The problem is, that the declare block is executed only once, therefore creating object prototype, which values are copied to instances.
So when I do buttons: {}, I create Object, which is then copied to all children. In end effect, all children have the same buttons instance - I've created a quasi-static field.
All bugs where gone when I've created the object in the constructor:
constructor: function(options) {
for(var i in options){
this[i] = options[i];
}
this.buttons = {}
this.data = []
},
Now every instance of my component have own buttons object.
In fact, my problem was exactly described here:
dojo.declare("my.classes.bar", my.classes.foo, {
someData: [1, 2, 3, 4], // doesn't do what I want: ends up being
I am having a hard time overriding a constructor from a singleton class in ExtJS 4.1. I have an override defined, but by the time my override statement gets processed, the constructor has already executed.
Ext.define('singleton', {
singleton: true,
constructor: function() {
alert('replace me');
}
});
Overriding the constructor of a singleton doesn't make sense, a singleton is a class that is very early on in the execution of Extjs turned into an instance of itself. This means that you are trying to override an instance of a class and not the class itself.
I can recommend that whatever you want done to the singleton be done in a separate method in the class definition that you can call very early in Ext.onReady() or your apps instantiation.
You can't override a singleton class, that is true, but you can override the singleton instance:
Ext.define('singleton', {
singleton: true,
constructor: function() {
// does whatever
}
});
Ext.define('singletonOverride', {
override: 'singleton',
// adding new property
foo: 'bar',
// adding new method
baz: function() {},
initSingletonOverride: function() {
// do whatever is needed
// to augment the singleton
// behavior the way you want
}
},
function() {
// `this` is the singleton instance
this.initSingletonOverride();
});
Keep in mind that all this fluff comes down to the JavaScript objects. A class is an Object, and a class instance is another Object. You can override either of them, or both; the class system is here to help you not to lose track of what you're doing.