I'm trying to create an Object containing other Objects and functions, in a prototype, the relevant part is the UI prototype;
var fChat = function() {
this.debug = true;
};
fChat.prototype = {
constructor: fChat,
Log: function(str){
if(this.debug){
console.log(str);
}
},
UI: {
Login: {
Show: function(){
this.Log("UI.Login.Show()");
}
}
}
};
var fChatInstance = new fChat();
fChatInstance.UI.Login.Show();
When i call fChatInstance.UI.Login.Show() It give me an error:
Uncaught TypeError: this.Log is not a function
Is that because by using this is on another scope?
Usually i do var self = this;at the start of a prototype, but i don't know how I can do that by using an Object prototype.
Yes. The problem is the javascript dynamic binding of this, to fix it you can set "this" to the object by using bind function. Change the fchat function refactor it like this:
var fChat = function() {
this.debug = true;
this.UI.Login.Show = this.UI.Login.Show.bind(this);
this.Log = this.Log.bind(this);
};
Related
I am looking to achieve something along the following.
HTMLSpanElement.prototype.testNS = {
_this: this,
testFunc: function() {
console.log(this.innerHTML) //undefined as expected as this is the testFunc object
},
testFunc2: function() {
console.log(this._this) //Window object
}
}
My goal is to add some helper functions directly to a span element in this case.
So, if I had the following:
<span>test</span>
I could find the span and call this code to return "test"
spanElement.testNS.testFunc()
I know that a function retains scope of it's parent when I do it like so...
HTMLSpanElement.prototype.testFunc = function() {
console.log(this.innerHTML)
}
But I am attempting to organize the code a bit and make it more obvious where the functions are coming from when I add them, and I can't seem to find a way to retain scope, when I do a normal JSON object grab the this scope into _this: this it just returns the global scope of "window".
Disclaimer: You shouldn't be trying to modify the prototypes on built-in types, especially host objects. It's a bad idea.
The reason your approach isn't working for you is that the functions are being called with the testNS object as the this.
You can get this to work if you define testNS as a property with a getter function, using Object.defineProperty. The reason this works is that the get function runs in the context of the object on which the property is being accessed (which would be the span):
Object.defineProperty(HTMLSpanElement.prototype, 'testNS', {
get: function() {
var _this = this;
return {
testFunc: function() {
console.log(_this.innerHTML)
},
testFunc2: function() {
console.log(_this)
}
}
}
});
var span = document.getElementById('mySpan');
span.testNS.testFunc();
span.testNS.testFunc2();
<span id="mySpan">Wah-hoo!</span>
A more "vanilla" approach is to just have testNS be a plain function and call it like one. This works because testNS is called in the context of the object on which it is being called (again, the span):
HTMLSpanElement.prototype.testNS = function() {
var _this = this;
return {
testFunc: function() {
console.log(_this.innerHTML)
},
testFunc2: function() {
console.log(_this)
}
}
}
var span = document.getElementById('mySpan');
span.testNS().testFunc();
span.testNS().testFunc2();
<span id="mySpan">Wah-hoo!</span>
When you call a function as foo.bar() then this inside bar refers to foo. Hence if you call the function as spanElement.testNS.testFunc(), this refers to spanElement.testNS.
_this: this, cannot work because this cannot refer to a <span> element.
To get access to spanElement from testFunc you could implement testNS as a getter:
Object.defineProperty(HTMLSpanElement.prototype, 'testNS', {
get: function() {
var element = this;
return {
testFunc: function() {
console.log(element.innerHTML);
},
};
},
});
document.querySelector('span').testNS.testFunc();
<span>foo</span>
Because it's a strange requirement I wrote a an equivalent strange solution :-)
Basically the createElement has been overriden in order to add a namespace object literal and then define a new function testFunc on top of the namespace using the instance of the element binded to the function
!function(){
var defaultNamespace = "testNS";
var createElement = document.createElement;
document.createElement = function(tag, namespace) {
var element = createElement.apply(document, arguments);
element[namespace || defaultNamespace] = {
testFunc : function() {
console.log(this.innerHTML);
}.bind(element)
};
return element;
}
}();
var span = document.createElement("span");
I have this class definition:
$.note = function() {}
$.note.prototype = {
init: function(note) {
this.note = note;
this.ctrl = document.getElementById(note);
},
// I have these getter functions because I was getting errors using
// myObject.note or myObject.ctrl
getNote: function() {
return this.note;
},
getCtrl: function() {
return this.ctrl;
}
}
I created a new object with this class like this:
var note = new $.note('C');
Which I can access in my console like this:
But when I try and access note.getNote(), I get undefined as the response:
Am I going about accessing these properties incorrectly? I've tried using just note.note or note.ctrl, and I get the same thing...
Nothing's going to call that "init" function if you don't.
$.note = function(note) { this.init(note); }
Some frameworks provide an object system that uses constructor helper functions like that, but plain JavaScript doesn't.
Try this:
$.note = function(note) { this.note = note;}
or you should call init function:
var note = new $.note();
note.init('C');
I have some functions defined inside an object:
var functions = {
__construct: function(){
console.log(this);
this.prototype.__construct(); // <problem
}
};
I merge this object with another one that is a function (after I create a new instance of the function):
var plugin = function(){};
plugin.prototype.__construct = function(){
console.log('parent');
};
var i = new plugin();
i = $.extend({}, i, functions);
But after that when I try to call the __construct function:
i.__construct();
I get this error:
Uncaught TypeError: Cannot call method '__construct' of undefined
It's because this line:
this.prototype.__construct();
I tried to see if I can call the parent constructor function from the child constructor, but it appears that the object doesn't have a prototype? wtf?
Not sure what you're trying to do here but i does have a __construct function when using the code you provided:
var functions = {
__construct: function(){
console.log(this);
this.prototype.__construct(); // <problem
}
};
var plugin = function(){};
plugin.prototype.__construct = function(){
console.log('parent');
};
var i = new plugin();
i = $.extend({}, i, functions);
console.log(i.__construct===functions.__construct);//true
console.log(i.__construct===plugin.prototype.__construct);//false
Not sure what you think this.prototype is going to be, maybe this answer will clear up what prototype is used for (shared members) and what this means (instance specific). You can shadow shared members that are defined in the prototype further down the prototype chain or in an instance but that's not what you're doing here.
If you dynamically want to extend an instance created with a constructor function you can do something like this:
var functions = {
__construct: function(){
this.constructor.prototype.__construct.call(this);
}
};
var Plugin = function(){};
Plugin.prototype.__construct = function(){
console.log('parent');
};
var i = new Plugin();
i = $.extend(i,functions);
i.__construct();
var fn = function() {
this.method1 = function() {
return this.public;
};
this.method2 = function() {
return {
init: function() { return this.public; }
}
};
fn.prototype.public = "method prototype";
};
create object fn
var object = new fn();
object.method1() // "method prototype"
object.method2().init(); // undefined
this.public Prototype in method2().init() function run return undefined ?
Is there an alternative to Prototype?
thank you.
The issue is related to the different scope in which this is bound on init function of method2(), so try this:
this.method2 = function() {
var self = this;
return {
init: function() { return self.public; }
}
};
so
object.method2().init(); // return "method prototype"
There is a lot wrong with this.
But the direct answer to your specific question is that calling init returns undefined because its reference to this is to the inner object that you've created and not the instance that you think it refers to.
I suggest you stop trying to solve this particular problem and learn the basics of prototypal inheritance in JavaScript
This this in the init function is the object returned by object.method2(), which has no public property on it.
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());