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****
Related
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.
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.
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/
This is more of an exploratory question, seeing how the core JavaScript stuff works. I realize the convention is to not override any core JavaScript classes, but I just can't seem to wrap my head around this one.
You can create what acts like "class methods" in JavaScript by adding to the core Function prototype like this:
Function.prototype.class_method = function() {
console.log("class method called")
}
var User;
User = (function() {
function User() {}
return User;
})();
User.class_method(); // "class method called"
My question is, is there a way to add "instance methods" in a similar way? Something crazy like this, but what's below doesn't work (or make any sense):
alias = Function.prototype.constructor;
Function.prototype.constructor = function() {
child = this;
child.prototype.instance_method = function() {
console.log("instance method called");
}
alias.apply(child);
}
var user = new User();
user.instance_method(); // method doesn't exist
It's almost like you'd need to override the Function class' constructor method and access the prototype from there. Is this possible?
It does work if you add to the Object.prototype like this:
Object.prototype.instance_method = function() {
console.log("instance method");
}
var user = new User();
user.instance_method(); // "instance method called"
But that doesn't seem right either, mainly because seeing the output in the node.js console from console.log({}); change is confusing:
console.log({});
// {};
Object.prototype.instance_method = function() {
console.log("instance method");
}
console.log({});
// {"instance_method": [Function]}
If you are using node.js, you should be able to use Object.defineProperty [MDN] and make the new property non-enumerable:
Object.defineProperty(Object.prototype, 'instance_Method', {
value: function() {
console.log("instance method");
},
enumerable: false // it's already the default
});
This was introduced in ECMAScript5, so only newer browsers will support it.
It's important to understand when the prototype comes into play. It's simply an object that is a property of a function. It only has meaning when you use the new keyword. Example:
var Widget = function(val) {
this.value = val;
};
Widget.prototype.getValue = function() {
return this.value;
};
var widget1 = new Widget('test');
widget1.getValue(); // test
var widget2 = new Widget('test2');
widget2.getValue(); // test2
When new is used, the js interpreter will create a hidden _proto_ property on the instance. This proto link is simply a reference to the prototype object of the constructor function, e.g., Widget at the time the constructor was called.
When you override the Function constructor, you are literally adding something that will be on the _proto_ property of every function created after you modified Function.prototype.
If you make the statement child.prototype... = ... in your base 'class' constructor function, then that prototype will not have meaning until something 'instantiates' child, e.g., var child = new child();.
A great Resource.
To answer your question about 'instance methods', you simply need to do something like the following:
var Widget = function() {
this.method = function() {
return 'instance method';
};
};
Widget.prototype.method = function() {
return 'class method';
};
var widget1 = new Widget();
widget1.method(); // instance method
delete widget1.method;
widget1.method(); // class method
This is due to javascript's implementation of Prototypical Inheritance. The proto link I spoke of before is key here. When widget1 was first created, inside the constructor function Widget, method was attached specifically to widget1. This method will not be available to other instances. However, method on the prototype is shared across all instances of Widget.
At runtime, when the js interpreter sees widget1.method();, it first sees if widget1 has method as a property directly on it (objects in js are just hashmaps in essence, in which the keys are called 'properties'). It finds the instance method as a property in this case. However, once you delete the instance method, it will attempt to follow the _proto_ link, which is just an object reference to Widget.prototype (at the time the constructor was called). Widget.prototype.method is defined; therefore, the interpreter will execute that. If no method function is found when continuing to follow _proto_ links, it'll be a run-time error.
What's wrong with this—how come the variable foo isn't defined from within onModified() of a Document object?
function Document() {
var foo = "dfsadf";
this.onModified = function() {
alert(foo);
};
}
// Does not alert; "foo" doesn't resolve
new Document().onModified();
I'd like to have public methods on Document that reference variables that are somehow private to Document.
Your Document function is clashing with the Document constructor from the DOM.
document instanceof Document; // true
As with any host-object its behavior completely depends on the host environment, and they often can give you unexpected results.
As far I've tested, on Firefox you are not able to replace its value, therefore I would recommend you to either, rename your function, or, declare it on other scope.