ExtJS 4 Object.prototype fail - javascript

have a bit problem to use prototype while using framework ExtJS version 4.1.1.
At first I made my prototypings before I load ExtJS.
On "Array.prototype.xyz" and "String.prototype.xyz" all work fine.
Bot on "Object.prototype.xyz" there is a bad behavior in mixin inclusion of ExtJS.
Example my test code:
Object.prototype.doSomething = function() {
console.log('I do it!');
}
var a = {};
a.doSomething();
Error message from ExtJS:
Uncaught TypeError: Cannot read property '$childEls' of undefined
And break.
And:
- Yes. Without "Uncaught TypeError: Cannot read property '$childEls' of undefined" it work
fine.
- No. I use not oter mixins currently.
- Yes. I try to use only one dummy panel Component.
Question: Is there a simple solution to prototype on Object class-object?

The problem stems from one of the fundamental methods of the Ext JS library: Ext.merge
Proving this is very simple:
Object.prototype.doSomething = function(){ console.log("Does something"); };
var emptyObj = {};
console.log(emptyObj.hasOwnProperty("doSomething")); // Prints "false"
var mergeObj = Ext.merge({}, {a: "b"});
console.log(mergeObj.hasOwnProperty("doSomething")); // Prints "true"
Basically, every time Ext.merge (or Ext.apply) is called with an object literal your prototype method is "promoted" up the prototype chain. When you go to create a panel (or any component, really) the class mixin object is merged with its prototype's mixin object. Since a mixin is defined as an object literal in the class definition, your "doSomething" method is promoted.
Then in Ext.util.ElementContainer#getClassChildEls, the mixin object is iterated over assuming each property is an existing class and tries to access mixins[name].self.$childEls (where mixins[name] is your "doSomething" method). Your method doesn't have a self property so accessing $childEls throws the error.
If you need an object available on every object, write it as a static method like Object.doSomething or even Ext.Object.doSomething.

Related

Firefox add-on: function not available after juggling scopes

As of Firefox 36, Function.__exposedProps__ was made unavailable. Instead if one wanted to expose a chrome JS object to be used in content scripts, they have to use Components.utils.cloneInto with the target scope as browser.contentWindow.wrappedJSObject.
If one does not turn on the cloneFunctions flag, only those attributes are cloned that are not functions. Turning the flag does clone functions too, but not those functions that are defined via the Function.prototype path. For those functions one has to export them via Components.utils.exportTo with the target scope as your exposed object.
Coming to the issue I'm facing. (As I am unable to put it in words, I am adding a MWE).
Chrome end JS:
function Foo(){
this._nFunc = "something";
this._func = function(){/*do something*/};
}
Foo.prototype.Bar = function(){
this._func();
}
Foo.prototype.FooBar = function(){
this._nFunc = "somthing else";
}
var myFoo = new Foo();
var targetScope = browser.contentWindow.wrappedJSObject;
targetScope.myExposedObject = Components.utils.cloneInto(myFoo, targetScope, {cloneFunctions:true});
Components.utils.exportFunction(myFoo.Bar, targetScope.myExposedObject , {defineAs:"Bar"});
Components.utils.exportFunction(myFoo.FooBar, targetScope.myExposedObject , {defineAs:"FooBar"});
Content end JS:
window.myExposedObject.FooBar(); // works
window.myExposedObject._func(); // works
window.myExposedObject.Bar() // error this._func is undefined
Upon logging the this scope received by the function Bar(), we get _func:(void 0), while _nFunc is logged correctly.
Questions:
Is there something I'm missing, or is this a limitation in Firefox? If it is a limitation, please suggest possible ways to workaround the limitation.
Initially I thought that Bar() was somehow unable to access the scope of the calling object, and I tried to supply it the scope as parameters, i.e., Foo.prototype.Bar = function(scope){ scope._func();} and window.myExposedObject.Bar(window.myExposedObject);. Interestingly upon logging, the scope object also turned out to be (void 0). Why is that? I am sure that I am missing something here. What I expected was that the exposed object would map to the original object and upon sending the exposed object as parameters the chrome end JS would be able to get the original object.
While what you're trying to do might be possible with the right combination of cloneInto/exportFunction and waiving of xrays i would suggest you simply load the unprivileged part of your class hierarchy directly into the target context with the subscript loader and only hook the minimal amount of privileged functions into the prototype once it has been created.
This should reduce the attack surface and also avoid headaches with inheritance.
Additionally, these may prove useful:
https://developer.mozilla.org/en-US/docs/Components.utils.createObjectIn
https://developer.mozilla.org/en-US/docs/Components.utils.makeObjectPropsNormal

"Uncaught TypeError: undefined is not a function" Error message in Javascript when calling a method in an object

I have a problem when calling a function from a button in HTML that gives me the: "Uncaught TypeError: undefined is not a function" error. I don't think there's anything wrong here.. Or there is something that I haven't taken into account. Thanks in advance for answering!
I have a lot of JS files, this is because this is a school assignment and we're now learning the Model, View, Controller (MVC) method.
I have this button:
<button onClick="ControllerBKE.reageerOpKlik()">Ok!</button>
I then have this Javascript code that creates an object of ^ "ControllerBKE":
"use strict"
window.onload = reageerOpStart();
function reageerOpStart()
{
var controllerBKE = new ControllerBKE();
}
Here is the line of code that is in the "ControllerBKE" that should, but is not reacting to the button:
function ControllerBKE(){
this.reageerOpKlik = reageerOpKlik;
function reageerOpKlik(){
alert('hoi');
}
}
This is just a small portion of a big code. But I get the error message when I click on the button instead of getting an alert with 'hoi'.
reageerOpKlik is an instance method. You have to use it from an instance. The simplest solution (not the best) is to create a global controller instance. There are many ways you could get rid of that global variable, but it's beyond the scope of the question.
function reageerOpStart()
{
window.controllerBKE = new ControllerBKE();
}
<button onClick="window.controllerBKE.reageerOpKlik()">Ok!</button>
The problem is that your code
<button onClick="ControllerBKE.reageerOpKlik()">Ok!</button>
is trying to call reageerOpKlik on your prototype object ControllerBKE.
What you probably mean is
<button onClick="controllerBKE.reageerOpKlik()">Ok!</button>
where controllerBKE is an instance of your prototype.
However, you have another problem. The function:
function reageerOpStart()
{
var controllerBKE = new ControllerBKE();
}
Creates controllerBKE in the scope of the reageerOpStart function, meaning that it's not avaiable in the global scope, which is where your button click handler would expect it.
You might want to consider:
<button onClick="APP.controllerBKE.reageerOpKlik()">Ok!</button>
APP = {}
function reageerOpStart()
{
APP.controllerBKE = new ControllerBKE();
}
Or, better still:
<button id="myButton">Ok!</button>
function reageerOpStart()
{
var controllerBKE = new ControllerBKE();
document.getElementById("myButton").addEventListener("click", function() {
controllerBKE.reageerOpKlik();
});
}
What you have is referred to as a closure. Your function has a limited scope. That is, it can only be called inside of ControllerBKE() where it is defined, not from outside the function.
What you have effectively done though is expose that closure via a property on your instance of ControllerBKE. While this works, it would fit more with the prototypal structure of JavaScript to add it to ControllerBKE.prototype.
It's important to remember that JavaScript is Prototypal not Object Oriented. While this may act similar to object oriented encapsulation, the two have different concepts and uses.
Look at the following example:
HTML:
<button onclick="controllerBKE.reageerOpKlik()">Ok!</button>
JavaScript:
"use strict";
window.controllerBKE = new ControllerBKE();
function ControllerBKE () { }
ControllerBKE.prototype.reageerOpKlik = function () {
alert('hoi');
}
I've simplified some of your code and refactored it to support the prototype object that JavaScript provides us with.
The first line is adding the controllerBKE variable to the window object. This gives it a global scope across the page, allowing your onclick function to have access to it.
The next line is a simple function wrapper. This will create an instance of ControllerBKE of type object.
The function you're trying to call is now attached to the prototype of ControllerBKE. This means that any instances of ControllerBKE created with the new keyword will have access to this function.
Check out the full functionality in the fiddle below:
FIDDLE
References:
Object.prototype
Object Oriented JavaScript

Can't call methods on subclass of native window object in javascript

I am trying to subclass native window object, but when I do so none of the window methods are callable in child class.
Here is an example below:
<script type="application/javascript" >
function baseWindow () {
}
baseWindow.prototype = window;
var mywindow = new baseWindow();
window.alert('works'); // works of course
alert(window.document); // accessing property of course works
mywindow.alert('doesn\'t work'); // alert doesn't work in subclass error: TypeError: Illegal invocation
mywindow.__proto__.alert('works') // accessing it directly via __proto__ works
alert(mywindow.document); // accessing document property works
</script>
Can someone explain why that doesn't work and if there is workaround ?
Thanks
As you figured out already, some properties of window are inherited properly, while others are not. Those that are not are methods that expect the object they are invoked on to be window which is obviously not the case in your example. By "expect" i mean they throw an error if the expectation is not met.
What you can do to avoid it is override those particular functions, perhaps by using the original functions somehow (depending on what you want to do with them).
function MyWindow(){
this.alert = window.alert.bind(window); // override it to work!
}
MyWindow.prototype = window;
var mine = new MyWindow();
mine.alert(mine.location);
If you want many instances of Window and a single alert function shared between them and you don't want to alter window.alert, you need to add another object that inherits from window as prototype for Window:
function MyWindow() {
}
MyWindow.prototype = Object.create(window);
MyWindow.prototype.alert = window.alert.bind(window);
var mine = new MyWindow();
mine.alert(mine.location);
I am trying to subclass native window object,
You cant.
Why? because window is not a function, and you cant call Window constructor.
Why? because the DOM is built that way.
function baseWindow () {
}
baseWindow.prototype = window
it's not even proper prototypal inheritance.
if Window constructor was callable one could write
function BaseWindow () {
Window.call(this);
}
BaseWindow.prototype = Object.create(Window.prototype)
But you cant do that.
EDIT just to be clear,
window is an instance of Window, they are not the same.

How can I extend the document object?

I'm currently trying to get a better understanding of JavaScript and prototyping.
I wanted to add a function to the document but prototype is undefined on document.
This code:
document.prototype.writeLine = function(text){
this.write(text);
this.write("<br />");
};
Generates this error:
// In FireFox
TypeError: document.prototype is undefined
// In Chrome
Uncaught TypeError: Cannot set property 'writeLine' of undefined
How can I extend the document object to be able to call something similar to document.WriteLine('MyText') ?
Here is the Fiddle I'm working with.
I updated your fiddle. The problem you were having is that document object is an instance of the HTMLDocument object type. The instance itself doesn't have a prototype, however the HTMLDocument does.
Update: Here is a snippet which works in IE9 because under IE9 HTMLDocument is undefined.
if (typeof HTMLDocument !== 'undefined') {
HTMLDocument.prototype.writeLine = function(text){
this.write(text);
this.write("<br />");
};
} else {
Document.prototype.writeLine = function(text){
this.write(text);
this.write("<br />");
};
}
document.writeLine("Line 1");
document.writeLine("Line 2");
The problem is that document is of type object and not function. In JavaScript you use functions as constructors like this:
function MyClass() {
this.myProperty = "something";
}
You may create an instance of MyClass as follows:
var myInstance = new MyClass;
alert(myInstance.myProperty);
Every function also has a property called prototype which is an object. All the properties of the prototype are inherited my instances of the constructor function:
MyClass.prototype.displayProperty = function () {
alert(this.myProperty);
};
myInstance.displayProperty();
In your case since document is the instance of a constructor and not the constructor itself, there's no property called prototype on it.
For more information about inheritance in JavaScript read this answer.
is very easy, document and Document are both different, document is the document of the window and Document is the interface of the document (thats comone from DOM), if your like add a new prototype for you use in your document you need add this but into Document like this:
window.Document.prototype.Sayhi = "Hello World"
or Document.prototype.Sayhi = "Hello World"
and now you can call this from you document like
document.sayhi
thats happen because you need Set the prototype on Interfaces if you like for example add a new prototype in your Object window your need Set it at Window interface like:
Window.prototype.Saybye = "Bye Bro See You Later"
and you can call the prototype in you window.Saybye
remember, Window is an interface that contain window like Document and document****

Delegate Javascript Functions

As an siginificantly simplified scenario, say I have 2 Javascript objects defined as below:
var ClassA = Class.extend({
'say': function(message) {
console.log(message);
}
... // some more methods ...
});
var ClassB = Class.extend({
init: function(obj) {
this._target = obj;
}
});
I'd suppose that in Javascript there is some kind of mechanism could enable us to do the following trick:
var b = new ClassB( new ClassA() );
b.say("hello");
I'd like to find a way to detect if there is a method called upon ClassB, and the method is not defined in ClassB, then I can automatically forward the method call to be upon ClassA, which is a member variable in ClassB.
In a realworld scenario, ClassA is an object implemented as brwoser plugin and inserted into the webpage using <object> tag. It's method is implemented in C++ code so there is no way I can tell its methods from its prototype and insert it to ClassB's prototype beforehand.
I'd like to use the technical to create a native Javascript object, with a narraw-ed version of ClassA's interface. Is there a way I can do this?
I don't think there is a quick cross-browser solution to this.
If you only need Firefox, then use __noSuchMethod__
See here: is-there-such-a-thing-as-a-catch-all-key-for-a-javascript-object
and here: javascript-getter-for-all-properties
Otherwise, I would try something like this:
var b = new ClassB( new ClassA() );
// functionToCall is a string containing the function name
function callOnB(functionToCall) {
if(typeof b[functionToCall] === function) {
b[functionToCall]();
} else {
b._target[functionToCall](); // otherwise, try calling on A
}
}
This is using the Square Bracket Notation where
b.say('hello')
is the same as
b['say']('hello')
Of course, you should probably expand this to take arguments in:
function callOnB(functionToCall, listOfArguments) {...}
Thanks to jfrej's hint on noSunchMethod, I did some more research on it and it turns out what I need is quit fit with Harmony Proxies(here and here). And an example can be found at http://jsbin.com/ucupe4/edit#source
Another related post: http://dailyjs.com/2010/03/12/nosuchmethod/

Categories