Is this possible? I'm trying to overwrite a javascript method after my page has been loaded. The code in question looks similar to this:
myObject = Backbone.ViewManager.BaseView.extend({
myMethod: function() {
alert("in old method definition");
},
initialize: function() {
var a = this;
Our.Events.on("alertEvent", function(){
a.myMethod();
}
}
);
(The Backbone.ViewManager bit is just a way to create an object in javascript using a framework and not important here.)
Note the event callback defined above in the initialize method. We initialize the objects as soon as they're created.
After my page has loaded I tried to redefine myMethod to alert a different message. But when the alertEvent fires the original message appears in the alert.
I assume this is because of a closure, that redefining the method on myObject after its been initialized won't affect the definition pointed to by a? If that's the case, is there something I can do to change the definition used by a, or am I out of luck?
You should be able to define myMethod on the object you create which will override the prototype myMethod. What you have written should be able to work because you arn't referencing myMethod directly, but through the a object.
The following works for me
function ObjCstr(){}
ObjCstr.prototype.myMethod = function(){ alert("Old Message"); };
ObjCstr.prototype.callMyMethod = function(){
var a = this;
return function(){ a.myMethod(); };
};
var test = new ObjCstr();
var fakeListener = test.callMyMethod();
fakeListener(); // "OldMessage"
test.myMethod = function(){ alert("Overridden!"); };
fakeListener(); // "Overridden!"
Related
I've got 3 codes :
var control = new Control();
function Control() {
this.doSomethingElse = function() {...}
this.doSomething = function () {
control.doSomethingElse();
}
}
Or
var control = new Control();
function Control() {
var self = this;
this.doSomethingElse = function() {...}
this.doSomething = function () {
self.doSomethingElse();
}
}
Or
var control = Control();
function Control() {
var self = this;
this.doSomethingElse = function() {...}
this.doSomething = function () {
self.doSomethingElse();
}
return self;
}
Important : The function is a controller, and just declared once. Then I'm using "control" everywhere in my code...
I was wondering if the control.doSomethingElse() was slow ?
In the end, what is the right thing to do and/or the fastest code in those exemple ?
Thanks !
The first is wrong - an object should never internally use the variable name by which it is known outside. Other code could change that variable to point to something else, breaking this code.
The third is also wrong - when calling Control() without new the assignments to this.foo inside will end up getting attached to the global object (except in strict mode, where there's no implicit this on bare function calls, so the assignment to this.doSomethingElse tries to attach to undefined, causing a runtime error).
That only leaves the second as appropriate, but ultimately it's a question of correctness, not performance.
Do not define methods in constructor - that means defining them every time an instance is created. Use Control.prototype.foo = function() {} instead. Also you do not need to return this if you're using new operator - that's the whole point of new operator.
The recommended approach is this:
function MyClass(param1) {
// Here we're changing the specific instance of an object
this.property1 = param1;
}
// Prototype will be shared with all instances of the object
// any modifications to prototype WILL be shared by all instances
MyClass.prototype.printProperty1 = function() {
console.log(this.property1);
}
var instance = new MyClass("Hello world!");
instance.printProperty1(); // Prints hello world
To understand this code, you need to understand javascript's prototype-based inheritance model. When you create instance of MyClass, you get a new object that inherits any properties present in MyClass.prototype. Read more about it.
Also I wonder:
The function is a controller, and just declared once.
If you're not using this multiple times, you don't need to create something like class. You can do this instead:
var control = {doSomething:function() { ... }};
I assume you are used to Java, where everything must be a class, whether it makes sense or not. Javascript is different, you can also make single objects or functions as you need.
Original Question
Can't figure out why I can't call the second function from within that first function. I am using jQuery-turbolinks. (Also, if you happen to know of a better way to only run page-specific javascript in rails, let me know. Currently this is my best implementation where I check if the body has a certain class, and if it does then I run the init function within this javascript object).
app/assets/javascripts/blogs.js
$(document).ready(function(){
var blogsjs = {
myBlog: this,
init: function(){
alert("hello from blogs");
$("input").on('click', function(){
$(this).hide('slow', function(){
myBlog.another();
});
});
},
another: function(){
alert("I was called!")
}
};
if($('body').hasClass("blogs") == true){
blogsjs.init();
}
});
Solution After Feedback
Simply Just used object.method() syntax from within a method to call another method within that same object:
$(document).ready(function(){
var blogsjs = {
init: function(){
alert("hello from blogs");
$("input").on('click', function(){
$(this).hide('slow', function(){
blogsjs.another();
});
});
},
another: function(){
alert("I was called!");
blogsjs.yetanother();
},
yetanother: function(){
alert("yet another called");
}
};
blogsjs.init();
});
I don't like how messy this code looks, but the encapsulation benefits from an Object-oriented design, I think, is solid: Each resource's javascript only has access to the methods inside its javascript object.
I don't know what you're trying to accomplish with this part of your declaration:
var blogsjs = {
myBlog: this
}
but, this will NOT be set to blogsjs. It will be whatever it was in the above function. In Javascript, this is only set on a function call. It is NOT set in a Javascript literal declaration so you cannot statically declare a property that refers to the object itself. Javascript just does not support that.
You can add properties after the object is constructed that contain references to the object if desired.
If you want myBlog to be initialized to point to blogsjs, then you will have to do that after the object is defined:
var blogsjs = {
init: function() {...},
another: function() {...}
};
blogsjs.myBlog = blogsjs;
In addition, this line of code won't work:
myBlog.another();
because myBlog is a property of an object, not a variable. It must be referenced with its parent object.
So you're probably getting an Cannot read property 'another' of undefined exception because you're specifying myBlog on the blogsjs object but do not reference it. Also myBlog will not be a reference to blogsjs but the scope jquery calls the document.ready function with.
You need to either create the reference inside your init method:
init: function(){
var myBlog = this;
alert("hello from blogs");
$("input").on('click', function(){
$(this).hide('slow', function(){
myBlog.another();
});
});
}
or simply use blogsjs from one scope above your init method.
Have a look at this question to learn about scoping.
I am just getting my feet wet where JavaScript Prototyping is involved, and I am having some trouble.
I need to create a _LEAVE object from a LEAVE prototype for a system I am working on based on a prototype object. The _LEAVE object has a function named Ready, which should fire when the document is ready. The system already has similar functionality in some of it's older code, and I am trying to keep it uniform.
Here is the code I am trying, but I keep getting an error:
var LEAVE = function () {
}
$(document).ready(function () {
_LEAVE.Ready();
});
var _LEAVE = function (params) {
this.Ready = function () {
alert ("Leave Ready");
};
}
_LEAVE.prototype = new LEAVE();
Error:
SCRIPT438: Object doesn't support property or method 'Ready'
leave.js, line 6 character 5
I'm not sure where I am going wrong, as this seems to be what is happening in other parts of the system. At least, something similar is happening, but I am struggling to wrap my mind around the old code...
Would appreciate any advice anyone could give me! :-)
I'm not sure if I've understood you correctly, but are you attempting to create an instance of a LEAVE object? If so, LEAVE needs to be a constructor function, and Ready should be a method on the prototype of that:
var LEAVE = function () {};
LEAVE.prototype.Ready = function () {
alert("Leave Ready");
};
Now, you can instantiate LEAVE by calling the constructor with the new operator:
var _LEAVE = new LEAVE(); // _LEAVE is an instance of LEAVE
$(document).ready(function () {
_LEAVE.Ready(); // Ready is a method of `LEAVE.prototype`
});
Methods declared as properties of the prototype object are shared by all instances. So all instances of LEAVE will have a .Ready method available to them, but they will share one copy of the function in memory (the copy that was assigned to the property of LEAVE.prototype).
What you have done here is just inherited the child _LEAVE function from parent LEAVE function. But if you want to call a method in the child class, you need to create an instance of it. So you need to create an instance of the _LEAVE class. just add this line :
var _LEAVE_OBJECT = new _LEAVE();
and use _LEAVE_OBJECT.Ready() instead of _LEAVE.Ready(); in $(document).ready.
Modified code :
var LEAVE = function () {
}
$(document).ready(function () {
_LEAVE_OBJECT.Ready();
});
var _LEAVE = function (params) {
this.Ready = function () {
alert ("Leave Ready");
};
}
_LEAVE.prototype = new LEAVE();
var _LEAVE_OBJECT = new _LEAVE();
If I have a function like this:
function a() {
console.log('a');
}
and then assign a static property like this:
a.static = 'foo';
But say I want to override the function with another function like this:
var old = a;
a = function() {
console.log('new');
old.call(this);
};
a.static // undefined
Since I assigned a new function to a, it’s static properties are lost. Is there a neat way to keep the static properties without looping and manually copying them?
Update:
Here’s a real world scenario: In Bootstrap jQuery plugins, the author assigns defaults to the property function like this:
$.fn.modal = function() {
// some code
};
$.fn.modal.defaults = { // some object };
So if I want to "extend" the prototype I would normally do:
var old = $.fn.modal;
$.fn.modal = function() {
// do my thing
old.apply(this, arguments);
}
But that would make
$.fn.modal.defaults === undefined
This will break the functionality, because the defaults are lost. I was wondering if there a sneaky way in javascript to change only the function without losing the static properties.
No, you cannot do this. Replacing the object (function) always takes any properties with it.
There are two solutions here, and both involve transferring the properties from the old object to the new one.
The first (recommended) approach is to copy the properties, which can be done conveniently with $.extend:
$.fn.plugin = $.extend(function() { ... }, $.fn.plugin);
The second option would be to dynamically set the prototype of the new function to be the old function. For example, in some browsers this would work:
var f = function() { ... };
f.__proto__ = $.fn.plugin;
$.fn.plugin = f;
However this is non-standard and might give rise to complications; don't do it.
I am using a jQuery Class plugin as so :
jQuery(document).ready(function($) {
window.SSK.calendar = new(Class.extend({
filter_by_filtered_names: function() {
console.log('foobar!');
},
init: function() {
if ( window.location.href.match(/name_filters/) ) {
SSK.calendar.filter_by_filtered_names();
};
}
}))
});
For some reason this returns on load :
SSK.calendar is undefined
Which tells me that the plugin class is not loading before its own call. Very strange indeed. Curious if anyone knew a remedy?
The behaviour seems to make perfect sense to me, even if I don't know how Class works:
Class.extend(...) creates a new constructor function (I assume). new executes the constructor which in turn calls init. The result is assigned to window.SSK.calendar. You see, init is called upon instantiation and this happens before the instance is assigned to window.SSK.calendar.
Here is a simplified example:
function MyClass() {
this.bar = 'baz';
console.log(foo.bar);
}
var foo = new MyClass();
This will fail since foo is still undefined at the moment the constructor is called. The instance is the return value of the function call, hence foo cannot contain a reference to the instance before the call.
You might be able to solve your problem by simply using this to reference the instance:
init: function() {
if ( window.location.href.match(/name_filters/) ) {
// better in this case: if(/name_filters/.test(window.location.href))
this.filter_by_filtered_names();
};
}
The documentation of the plugin should mention how you can reference the instance from inside a method.
Seems you use two onReady listeners: jQuery(document).ready(fn) and $(fn) are exactly equivalent. Effectively, you will append the inner function to the end of the function queue when the outer function executes. When trying to access SSK.calendar in any onDOMready function that is not registered after that, it won't be available.
Example:
$(function(){
console.log("A");
$(function(){
console.log("B");
});
console.log("C");
});
$(function(){
console.log("D");
});
will log:
A
C
D
B