Javascript object properties access functions in parent constructor? - javascript

So I'm using this pretty standard jquery plugin pattern whereby you can grab an api after applying the jquery function to a specific instance.
This API is essentially a javascript object with a bunch of methods and data.
So I wanted to essentially create some private internal methods for the object only to manipulate data etc, which just doesn't need to be available as part of the API.
So I tried this:
// API returned with new $.TranslationUI(options, container)
$.TranslationUI = function (options, container) {
// private function?
function monkey(){
console.log("blah blah blah");
}
// extend the default settings with the options object passed
this.settings = $.extend({},$.TranslationUI.defaultSettings,options);
// set a reference for the container dom element
this.container = container;
// call the init function
this.init();
};
The problem I'm running into is that init can't call that function "monkey". I'm not understanding the explanation behind why it can't. Is it because init is a prototype method?($.TranslationUI's prototype is extended with a bunch of methods including init elsewhere in the code)
$.extend($.TranslationUI, {
prototype: {
init : function(){
// doesn't work
monkey();
// editing flag
this.editing = false;
// init event delegates here for
// languagepicker
$(this.settings.languageSelector, this.container).bind("click", {self: this}, this.selectLanguage);
}
}
});
Any explanations would be helpful. Would love other thoughts on creating private methods with this model too.
These particular functions don't HAVE to be in prototype, and I don't NEED private methods protected from being used externally, but I want to know how should I have that requirement in the future.
// Edited based on Matthew's comment
So I tried moving the prototype definition based on Matthew's comment. This seems to work now, but still not sure if this is the correct way to be doing this. Thoughts? Obviously it would be cleaner if I move the prototype object into a separate area
$.TranslationUI = function (options, container) {
function monkey(){
console.log("blah blah blah");
}
// extend the default settings with the options object passed
this.settings = $.extend({},$.TranslationUI.defaultSettings,options);
// set a reference for the container dom element
this.container = container;
$.extend($.TranslationUI.prototype,
{
init : function(){
monkey();
// editing flag
this.editing = false;
// init event delegates here for
// languagepicker
$(this.settings.languageSelector, this.container).bind("click", {self: this}, this.selectLanguage);
}
}
);
// call the init function
this.init();
};
So while this works, the crappy part is that I'm re-initing prototype every time that constructor runs. I'm sure that's not efficient. But not sure how else to have the prototype methods have access to private functions/variables of a certain instance.

The error is because monkey is not defined in the scope you're calling $.extend from.

Alright. So found an answer on stackoverflow, confirmed by Crockford's site.
javascript - accessing private member variables from prototype-defined functions
Essentially, you can't really get access to private functions from the prototype methods. You can via 'privileged' functions, which in turn call private variables and functions, but then you are basically creating a crapload of getters and setters, which might just be doubled in your prototype "public" methods.
So its kind of a lot of work, especially if your stuff doesn't TRULY need to be private.

Have a look at my answer and some of the others here:
call function inside a nested jquery plugin

Related

$.fn.myPlugin vs $.something.myPlugin in regard to private and public methods

So I can't seem to find astraight answer on this, only vague examples of multiple variations where similar plugin/method declarations are used. I know that by saying
$.fn.myPlugin
I am defining a publicly available plugin method that can be executed on any valid jQuery object where the fn denotes the prototype. My question is then, by defining a method, either inside of my main plugin like so
$.fn.myPlugin.methodName
or outside of my plugin like so
$.something.methodName //where 'something' is in place of 'fn'
does this affect it being a public private/method? and if each is a different type of declaration, what is the difference.
The backstory of why I would like to know, to give some context to the situation, is that I want to define one main plugin that can be called and have run normally, however if the end user wants to redefine some method I have allowed to be public then they can do that. If I have any methods that I don't want the user to redefine but instead provide callbacks so they can hook into it, then I want to make that method private.
Anything set on $.whatever will be public, and therefore able to be modified by other developers.
If you want private methods, you should create a closure.
(function() {
function init(jqObj) { ... } // do magic here
$.fn.myPlugin = function() { init(this); } // avoid exposing core method
$.fn.myPlugin.publicMethod = function() { ... }
function privateMethod() { ... }
})();

OOP Javascript: Should Private Functions Be Added to Class Prototype?

Background
I've been working with OOP style Javascript for the past few months, starting with just dissecting open source libraries. It seems like they mostly follow the same pattern, except that I've seen two ways of handling private functions, and I'm wondering which is the best (best as in best practice, or better for reasons I might not know about).
Example Code
Below is a very stripped down skeleton of the pattern I'm using. If you'll note, there are two different forms of private functions.
The first is attached to the prototype like public functions, but is prefixed with an _.
The second is just a function who's scope is only accessible by the class.
(function(window) {
window.FooBarClass = (function() {
var Class = function( params ) {
this._init( params );
}
/***************************************/
/************* INITIALIZE **************/
/***************************************/
Class.prototype._init = function( params ) {
// DO SETUP STUFF
};
/***************************************/
/********** PUBLIC FUNCTIONS ***********/
/***************************************/
Class.prototype.doThings = function() {
// DO STUFF
};
/***************************************/
/****** PRIVATE FUNCTIONS 1ST WAY ******/
/***************************************/
Class.prototype._createSection = function( params ) {
// DO STUFF
};
/***************************************/
/****** PRIVATE FUNCTIONS 2ND WAY ******/
/***************************************/
function correctTwoDigitYear( variable ) {
// DO STUFF
}
return Class;
}());
}(window));
Question
Which of these is preferable, and why?
JS doesn't actually have private methods, though as you've seen you can limit access to functions and variables by closing over their scope.
In the end, if it's on the prototype, it's not private--regardless of naming convention (leading underscores, etc). So if you really want to limit access to something, do NOT put it on the prototype.
The second pattern, putting functions in the local scope, is preferable because it's actually private. It's not really OOP though.
The first pattern, putting functions in underscored properties on the prototype, is preferable because they are actual methods that get their this passed implicitly like you expect of a method. By being instance methods, they are available to any module that needs them, instead of being restricted by scope, which can be beneficial in larger projects. And of course methods are important in OOP as they offer dynamic dispatch if you want to use polymorphism.
Also prototype methods are (were?) a bit better optimised, so they were chosen if you need to squeeze out the last bit of performance, though in practice you won't see much difference to a plain function call.
any method that is attached to the prototype property of a function is accessible by the object or by the child class. SO,
class.prototype.__init
cannot be considered as a private method.
It can be accesible by the object or it can be modified by extending the class
ex:
var someClass = (function () {
var Class = function( params ) {
this._init( params );
}
Class.prototype._init = function( params ) {
console.log("Hello World");
};
return Class;
} ());
var extendSomeClass = someClass;
extendSomeClass.prototype._init = function () {
console.log("Hey there");
}
var obj = new extendSomeClass(); // this wil print "Hey there"
In the example that you have posted, the _init is acting as a constructor, hence the property associated with it is made public (though the naming convention suggests a private member). But to follow a private access, a closure scope should be the best practice.
example:
var fooClass = (function () {
var Class = function () {
if(this._init) {
this._init.apply(this, arguments);
}
}
Class.prototype.hello = function () {
log("hello world");
}
function log(args) {
console.log(args);
}
}());
In the example above the function log is a private method and cannot be modified/overridden outside the scope.

Prototype method not being called

I'm creating a custom object on click and then trying to access it's prototype's methods.
$(function(){
$('.addtask').on("click", function(){
var new_task = new Task();
$('.deletetask').on("click", function(){
new_task.deleteTask();
});
function Task(){
this.html="<li>Add Description<span class='deletetask'></span></li>";
}
Task.prototype = {
constructor: Task,
deleteTask: function(){
this.remove()
},
}
});
});
Now when i click on class deleteTask it says:
TypeError: new_task.deleteTask is not a function
new_task.deleteTask();
How can I access the prototype method deleteTask() ??
You're setting the prototype of Task after you've created your new Task. You're not changing the prototype of your already created instances by doing so, as the prototype is referenced at the instance creation.
Move the Task.prototype = { assignation before the creation of your new Task.
If you really want to add a function to all instances after they're created, change the prototype of the constructor instead of replacing it :
Task.prototype.deleteTask = function(){...
You should move the definition of Task and its prototype methods before the initial .on call. This will incidentally fix the problem identified by #dystroy whereby you're trying to set the prototype of Task after creating an instance of it.
As written the Task function is recreated every time the click handler is invoked, and therefore the Task objects created on each click will be instances of different classes, which makes using the prototype moot. Prototype methods are an effective way of sharing methods amongst multiple instances, and achieving inheritance, but you're using neither.
Alternatively, leave the Task function where it is but abandon using prototype methods, e.g.:
function Task() {
this.html = "<li>Add Description<span class='deletetask'></span></li>";
this.removeTask = function() {
this.remove();
}
}
(NB: this.remove is also undefined at this point?)

jQuery plugin patterns: something more object-oriented?

I'm working on a jQuery plugin, following the pattern detailed in the Authoring guide. Basically:
(function($) {
// Private
var doSomething = function($element, settings) { ... }
var doSomethingElse = function($element, settings) { ... }
// Public
var methods = {
init: function(options) { ... },
show: function() { ... },
hide: function() { ... },
update: function(content) { ... }
};
$.fn.myPlugin = function(method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || ! method) {
return methods.init.apply(this, arguments);
} else {
$.error('Method ' + method + ' does not exist on jQuery.myPlugin');
}
};
})(jQuery);
Here's my dislike: I have to pass the same "instance" variables to all of the private functions. I'm still working on becoming a JS pro — so pardon my incorrect term usage — but if I were doing this same thing in Ruby or PHP, I'd create a class with all of these public and private members and methods, and each instance of the class would correspond to an $element. Then I could do something like this (in JS):
var firstElement = new MyPlugin($element, settings);
firstElement.doSomething();
Rather than passing $element and settings to doSomething(), it already has access to those via this.$element and this.settings.
Get where I'm going with this? I'm looking for a more object-oriented approach, I guess. Now, I totally understand that JS doesn't have classes like Ruby or PHP. But between constructor functions, the module pattern, and regular object notation (like methods above), I'm not sure which is the best option for a jQuery plugin.
Can someone help point me in the right direction? Maybe some examples of existing jQuery plugins that do this well? Thanks!
The jQuery UI Widget Factory might be a good solution. It's useful for creating any kind of stateful jQuery plugins and can be used entirely separate from the rest of the jQuery UI suit.
Some useful links:
http://bililite.com/blog/understanding-jquery-ui-widgets-a-tutorial/
http://wiki.jqueryui.com/w/page/12138135/Widget-factory
http://ajpiano.com/widgetfactory/ (presentation)
If you want a more bare bone solution I'd go with either a regular Constructor + prototype setup to do things "properly" or use the Revealing Module Pattern to create a function that takes the element and any options as arguments and returns the public methods.
An example using the Revealing Module Pattern:
function myPlugin (element, options) {
var privateVar;
function privateFunc () {}
function publicMethod () {}
return {
publicMethodName: publicMethod
};
}
This pattern is a bit more tidy than a traditional prototypal set up, but does not take advantage of the prototype chain.
Edit: To clarify, when using any of these patterns you are supposed to create a new instance for each element/use.
It isn't necessarily a good idea to store any kind of stateful information in the plugin itself since it would be shared by all instances. One option is to store that data elsewhere, outside of the plugin.
The Plugins/Authoring page has a Data section which describes how to store information for use by your plugin on a per-element basis using the data() function.
Using data helps you keep track of variables and state across method
calls from your plugin. Namespacing your data into one object literal
makes it easy to access all of your plugin's properties from one
central location, as well as reducing the data namespace which allows
for easy removal if need be.
The example provided on the page uses the plugin pattern described in your post, but allows "instance" variables to be stored with the element they're associated with.
One key thing to remember when doing this is:
Always namespace your methods, events and data.
Edit:
It should be noted too, that in your example some of your functions expect $element as a parameter, but this isn't necessary since this will refer to the right thing when those functions are called through the plugin (because apply() is being called and setting the context to the correct this).

Javascript Object Inheritence

I'm creating a control for Google maps v2. While creating my control I've found a design challenge and want to find an appropriate solution. Here's the goods.
A custom Google control inherits from GControl;
myControl.prototype = new GControl();
Next I need to overload the initializer so here it is.
myControl.prototype.initilize = function (map) {
//do some work and return stuff
};
Now within my custom controls initlize function I create a couple elements which, using the GEvent class, I subscribe to various events. To make my callback functions managable, I included them into the controls prototype.
myControl.prototype.onEvent = function(e){
//do some work;
//modify the members of the current myControl instance
};
Within my callback function "onEvent" I want to modify members within my control. What is the best way to access my control from the function? The keyword "this" cannot be used because that is a reference to the element that was clicked, in my case a div. And I can't access the members through the prototype because I need a specific instance of the object. The only viable solution I've considered is to create my control globally in one of my scripts. Is this the best method?
The easiest thing that I can think, it to define your onEvent method within your constructor, there you will have quick access to the current object instance, and you will not have to modify your public API:
function MyControl () {
var instance = this; // store a reference to 'this'
this.onEvent = function (e) {
// use the instance variable
instance.otherMethod();
};
}
Note that in this approach, the onEvent property will exist physically in your object instances (obj.hasOwnProperty('onEvent') = true).
Edit: You can simply use the GEvent.bind function:
GEvent.bind(map, "click", myObj, myObj.onEvent);
The above bind method will enforce the context, so the this keyword inside myObj.onEvent will point to the myObj object instance when the event is triggered, it will work with your code without problems.
I'm not familiar with how you subscribe to events using GEvent, so I'll make that part up. Do this:
myControl.prototype.onEvent = function(e, instance) {
// do some work
// modify members of 'instance'
};
function wrap(handler, instance) {
return function(e) {
handler(e, instance);
}
}
GEvent.Register('Event', wrap(instance.onEvent, instance));

Categories

Resources