I'm trying to extend the functionality of some methods of the 2dcontext object, however I can't get it to work the way I want: I want to override a method, but I want to call the original method from the overridden method like this:
//First get the original context
var ctx = canvas.getContext("2d");
//Create a class which uses ctx as it's prototype
var ExtendedContext = function (){};
ExtendedContext.prototype = ctx;
//And extend a method
ExtendedContext.prototype.fillRect = function(x, y, width, height) {
//Do some stuff
this.prototype.fillRect(x, y, width, height); //Doesn't work
//Do some more stuff
};
How can I call the original fillRect method from inside my own method?
You can store the reference of the original function just like that:
var oldFillRect = ctx.fillRect;
and then call it like
ExtendedContext.prototype.fillRect = function() {
//Do some stuff
oldFillRect.apply(this, arguments);
//Do some more stuff
};
This technique is sometimes called 'duck punching' or a 'function hook'. In this particular instance, you should also be able to use the Object.getPrototypeOf method to get the original function reference. This would look like
ExtendedContext.prototype.fillRect = function() {
//Do some stuff
Object.getPrototypeOf(ExtendedContext.prototype).fillRect.apply(this, arguments);
//Do some more stuff
};
So you don't even need to store a reference.
No need to save the old names in a separate object, use closures :
ExtendedContext.prototype.fillRect = (function () {
var oldf = ExtendedContext.prototype.fillRect;
return function () {
//Do some stuff
oldf.apply (this, arguments);
//Do some more stuff
};
}) ();
If you have a bunch to do this might help :
function extend (fnc) {
var mthd = (fnc.toString ().match (/^function\s+(\w+)\s*\(/) || ['', ''])[1];
if (mthd in ExtendedContext.prototype)
throw ('ExtendContext method ' + mthd + 'does not exist');
ExtendedContext.prototype['_' + mthd] = ExtendedContext.prototype[mthd];
ExtendedContext.prototype[mthd] = fnc;
}
Then you can call extend as follows
extend (function fillrect () {
// Do some stuff
this._fillrect.apply (this, arguments);
// Do some more stuff
});
To refer to the old method use its name prefixed with '_'
I'm a few months late, but I'm using a fairly simple design to accomplish this functionality.The structure of our JavaScript runs off of a global object to keep our code secured from global vars.
For each page/usercontrol we are modifying our global object to hold a new object, but some code needs different functionality in different places, requiring extension methods. We don't want to duplicate code and redefine the whole object for the extended instance, and we don't want the code to care how it is being extended.
Instead of punching a duck until it does what you want it to, why not create a generic extension method? Using our case, here is an example:
// Using a Global JavaScript object:
GlobalNameSpace.ExtensionFunction = function(oParam1, oParam2, oParam3)
{
/// <summary>All parameters are optional</summary>
return; // For instances when it is not being overwritten, simply return
}
//In the Code to be extended:
GlobalNameSpace.Control.ControlFunction(oSender, oArgs)
{
///<summary>Control's function</summary>
// Function-y stuff..
GlobalNameSpace.ExtensionFunction(oSender, oArgs);
}
//and finally in the code to extend the functionality
GlobalNameSpace.Page.Init
{
///<summary>Initializes the page</summary>
// redefine the extension function:
GlobalNameSpace.ExtensionFunction = function(oSender, oArgs)
{
// Call the extension function, or just code the extension here
GlobalNameSpace.Page.Function(oSender, oArgs);
}
}
The short coming of this method is if you want to do this for multiple objects at a time, at which point it may be a better idea to move an extension method into the code you are specifically wanting to extend. Doing this will make that extension code less generic, but that can be decided according to your needs.
Related
Edit: the code below was made up on the spot to show how I was going about what I was doing. It definietely won't run, it is missing a lot of things.
Here is a working example in codepen: https://codepen.io/goducks/pen/XvgpYW
much shorter example: https://codepen.io/goducks/pen/ymXMyB
When creating a function that is using call or apply, the this value stays null when using getPerson. however, when I use apply or call with getPerson it returns the correct person.
Please critique, I am really starting to learn more and more. I am in the middle of a project section so it might be hard to change all the code, but my next project could implement this better.
call and apply are setting to the window and not the object.
I will provide code that is much simpler with the same concept of what I am talking about.
function createPerson(){
this.manager = null;
this.teamManager = null;
this.setTeamManager = function(val){
this.teamManager = val;
}
this.setManager = function(val){
console.log('setting manager to',val);
this.teamManager = val;
}
this.getTeamManager = function(){
console.log('setting team manager to',val);
return this.teamManager ;
}
this.getManager = function(){
return this.manager;
}
this.appendSelect = function(elem){
var that = this;
createOtherSelects(that,elem);
}
//some functions that create selects with managers etc
//now assume there are other selects that will filter down the teams,
//so we might have a function that creates on change events
function createOtherSelects(that){
//code that creates locations, depending on location chosen will
//filter the managers
$('#location').on('change',function(){
//do some stuff
//... then call create management
createManagement(that,elem);
});
}
function createManagement(that,elem){
var currentLocation = that.location; //works
var area = that.area;//works ... assume these are set above
//code that returns a filter and unique set of managers back
that.teamManager = [...new Set(
data.map(person=>{
if(person.area==area &&
person.currentLocation==currentLocation
){
return person;
}
})
)].filter(d=>{if(d){return d}});
if(elem.length>0){
var selectNames = ['selectManager','selectTeamManager'];
var fcns = [that.setManager,that.setTeamManager];
for(var i = 0; i < selectNames.length;i++){
//do stuff
if(certainCriteriaMet){
// filter items
if(filteredManager == 1){
fcns[i].call(null,currentManager);//
}
}
}
}
}
}
var xx = new createPerson()
In console I see setting manager and setting team manager to with the correct values.
however when I call xx in console, I see everything else set except for
xx.teamManager and xx.manager
instead it is applying to the window, so if I type teamManager in the console, it will return with the correct person.
If I straight up say
that.setManager('Steve')
or even it works just fine.
xx.setManager('steve')
the this value in setManager is somehow changing from the current instance of the object to this window. I don't know why, and I would like to learn how to use apply and call using that for future reference.
I think the issue is with your following code
fcns[i].call(null,currentManager)
If you are not supplying "this" to call, it will be replaced with global object in non-strict mode.
fcns[i].call(that,currentManager)
See mdn article here
From your codepen example, you need to change that line
fcnset[0].apply(that,[randomName]);
The first argument of the apply method is the context, if you are not giving it the context of your method it's using the global context be default. That's why you end up mutating the window object, and not the one you want !
I'm trying to override a native method called "localStorage" for functions INSIDE an object.
Here's a gist of what I'm trying to do:
function SomeObject(){
this.localStorage = "aaa"; //block access to localStorage for functions INSIDE this object.
... (some more code here)
_testRun(){
window.testA = localStorage; //chose to store the instance on a window (global-like) object
}
this.testRun = function(){ _testRun(); };
this.testRun2 = function(){ window.testB = localStorage;};v
}
var a = new SomeObject();
a.testRun();
a.testRun2();
(after this, when I look up window.testA and window.testB, they both point to the Native localStorage, not the custom one inside the SomeObject.)
BTW, I don't want to override a native function for the whole document.
(i.e. might use native localStorage OUTSIDE the object)
Any suggestions/solutions on how I can do this? thanks!
Try to add window.localStorage and this.localStorage instead of just localStorage
function SomeObject(){
this.localStorage = "aaa"; //block access to localStorage for functions INSIDE this object.
... (some more code here)
_testRun(){
window.testA = window.localStorage; //chose to store the instance on a window (global-like) object
}
this.testRun = function(){ _testRun(); };
this.testRun2 = function(){ window.testB = this.localStorage;};
}
I'm using a pattern like the one shown below to create a javascript library that has private and public methods. The idea is that the page that includes this library would call MYLIB.login() and provide two functions for when the user clicks OK, or Cancel.
var MYLIB = function() {
// private data and functions
var showForm = function() {
// create and show the form
};
var onOK = function() {
// hide the form, do some internal stuff, then…
okFunction();
};
var onCancel = function() {
// hide the form, do some internal stuff, then...
cancelFunction();
};
var okFunction = null;
var cancelFunction = null;
// public functions
return {
login : function(okf, cancelf) {
okFunction = okf;
calcelFunction = cancelf;
showForm();
},
};
}();
My question is about getting the buttons in the form to call the internal functions onOK and onCancel, which are private. The buttons look like this:
<button onclick="onOK();">OK</button>
<button onclick="onCancel();">Cancel</button>
I can get it to work if I make the functions public, but then I may as well make everything public. How can I do what I want? Coming from a C++/Java background, trying to be a good OO guy. Thanks.
There are a lot of ways to go about it, but from what I see the onOK and onCancel have to be public because you need to access them from outside the class. You could vreate a public function to handel these actions like:
this.setStatus = function(status){
// check if the conection is on before calling the functions and stuff like that
if(status) onCancel();
else onOK();
}
it actually depends on where you want to go with this in the log run. Javascript provides a lot of OOP stuff, but is very different than C++/Java.
Tip: if you want to use private functions I would encourage you to use priviledgiat functions to work with the private methods (http://cuzztuts.blogspot.ro/search/label/oop) , also keep in mind that it's important the order in witch you declare your vars.
I want to create an object that can parse a certain filetype. I've looked at some of the files in the File API and I want my object to work about the same. So basically, what I want is this:
A function, called CustomFileParser. I want to be able to use it as the following:
var customFileParser = new CustomFileParser();
customFileParser.parsed = paresed;
customFileParser.progress = progress;
customFileParser.parse(file);
function parsed(event){
//The file is loaded, you can do stuff with it here.
}
function progess(event){
//The file load has progressed, you can do stuff with it here.
}
So I was thinking on how to define this object, but I'm not sure how to define these events and how I should do this.
function customFileParser(){
this.parse = function(){
//Do stuff here and trigger event when it's done...
}
}
However, I'm not sure how to define these events, and how I can do this. Anyone can give me a hand?
Javscript is prototype-based OOP language, not class-based like most other popular languages. Therefore, the OOP constructs are a bit different from what you might be used to. You should ignore most websites that try to implement class-based inheritance in JS, since that's not how the language is meant to be used.
The reason people are doing it because they are used to the class-based system and are usually not even aware that are alternatives to that, so instead of trying to learn the correct way, they try to implement the way that they are more familiar with, which usually results in loads and loads of hacks or external libraries that are essentially unnecessary.
Just use the prototype.
function CustomFileParser(onParsed, onProgress) {
// constructor
this.onParsed = onParsed;
this.onProgress = onProgress;
};
CustomFileParser.prototype.parse = function(file) {
// parse the file here
var event = { foo: 'bar' };
this.onProgress(event);
// finish parsing
this.onParsed(event);
};
And you can use it like so
function parsed(event) {
alert(event);
}
function progress(event) {
alert(event);
}
var customFileParser = new CustomFileParser(parsed, progress);
var file = ''; // pseudo-file
customFileParser.parse(file);
From what it sounds to me i think you need your program to look like this
function customFileParser( onparse , progress){
this.onparse = onparse;
this.progressStatus = 0;
this.progress = progress;
this.parser = function (chunk)
}
this.parse = function(){
// Do stuff of parsing
// Determine how much data is it
// Now make a function that parses a bit of data in every run
// Keep on calling the function till the data is getting parsed
// THat function should also increase the percentage it think this can be done via setTimeout.
// After every run of the semi parser function call the progress via something like
this.parser();
if(progressStatus <100){
this.progress(this.progressStatus);
}else{
this.parsed();
}
}
}
and u can create instance of that object like
var dark = new customFileParser( function () { // this tells what to
do what parsed is complete } , function (status) { // this tells what
to do with the progress status } ) ;
using the method i suggested. you can actually define different methods for all the instances of the object you have !
*update: sorry for the vagueness. I have updated the question
I am using the pusher library, where i create channels and bind it to a function (http://pusherapp.com/docs/quickstart)
var myChannel = pusher.subscribe('MY_CHANNEL');
myChannel.bind('thing-create', function(thing) {
//executes
});
In this case, i have 2 objects that subscribe to different channels, but execute the same set of function.
var myChannel = pusher.subscribe('MY_CHANNEL');
var myChannel2 = pusher.subscribe('MY_CHANNEL2');
//wondering if there is syntax for something like this
(myChannel+myChannel2).bind('thing-create', function(thing) {
//executes
});
I can certainly just repeat it, but I know you can do it in jQuery, so i wonder if there is a way to do this for js objects. Maybe jQuery just iterates the the elements
//in jquery, you would do this
$(".xxx",".xxx").bind(...)
var f = function() {...};
a1.bind("xx", f);
a2.bind("xx", f);