Using Object.create from inside class with this keyword - javascript

i have this simple class:
function Page(u,o) {
this.o = o;
this.gen(u);
}
Page.prototype = {
gen:function(u) {
if(u ==='index.php') new test(this.o);
}
}
new Page('index.php',{data:"just for test"});
Is there a way to use Object.create(this); instead of new test(this.o); so that the test class can have access to this.o? and not spawn new instances of test every time? As you see i don't use var x = new ... because i don't need it.
test class just use the this.o to append some data in a div element, nothing is returned.
Thanks

Instead of using a class, just make test a normal function (if it is not already) and drop the "new". For that matter, since it looks like you don't do anything with the Page class either, make that a straight function as well:
function page(u, o) {
if (u === 'index.php') {
test(o);
}
}

Related

How to make a jQuery like select and action system(JavaScript)?

This is not for use in my project, Only for learning purposes.
In jQuery,
When we call $('h1'). it simply returns all the h1 elements from the document. Again when we make some action on an element like $('h1').hide(), it simply hides all the elements(cool ah?)
I want to learn this similar functionality, for example:
function app(elm){
const x = (typeof elm !== 'object') ? document.querySelectorAll(elm) : elm
return {
hide : function(){
x.forEach( target =>{
target.style.display = 'none';
});
}
}
}
This is a simple code here. So, If I call it like app('h1').hide(); it will hide all the h1 elements from the document. But if I call it like app('h1') it returns the object what I return that's normal.
In here I need all h1 elements from the document like jQuery. I mean It should work like this,
$('h1') === app('h1') //JQuery is equal to myCFunction (problem)
$('h1').hide === app('h1').hide() //jQuery is equal to myCFunction (solved)
[NOTE] Here is an article that is similar to my question but it's not my question answer.
Article Link
You can return x instead of a custom object, but before returning inject the hide function into x object's prototype like x.prototype.hide = function(){/*...*/}.
I think $("h1") does not return selected elements. It stores the selected elements. Instead we can have new function(getElement) to get select elements.Hope this code helps.
var App = function() {
var x ;
this.app = function (elem) {
x = document.querySelectorAll(elem);
return this;
}
this.hide = function(){
x.forEach(target => {
target.style.display = 'none';
});
return;
}
this.getElement = function(){
return x;
}
}
var $ = new App();
$.app("h1").hide();
console.log($.app("h1").getElement());
I've got a mostly working solution, but you still have to fix one small but annoying problem (see caveat 3). It's mostly done so I'll put it here anyway.
I think this is what you are looking for:
function app(selector) {
const retArr = document.querySelectorAll(selector); // The array to return
// Add proxies for all prototype methods of all elements
for (let e of retArr) {
let methods = getProtoMethods(e);
for (let mKey in methods) {
// Skip if the proxy method already exists in retArr
if (retArr[mKey] !== undefined) continue;
// Otherwise set proxy method
Object.defineProperty(retArr, mKey, {
value: function(...args) {
// Loop through all elements in selection
retArr.forEach(el => {
// Call method if it exists
if (el[mKey] !== undefined) el[mKey](...args);
});
}
});
}
}
return retArr;
// Gets all prototype methods for one object
function getProtoMethods(obj) {
let methods = {};
// Loop through all prototype properties of obj and add all functions
for (let pKey of Object.getOwnPropertyNames(Object.getPrototypeOf(obj))) {
// Skip properties that aren't functions and constructor
if (pKey !== "constructor" && typeof obj[pKey] === "function") {
methods[pKey] = obj[pKey];
}
}
return methods;
}
}
The idea is to put all the selected objects in an array, then define additional methods on the array. It should have all the method names of the selected objects, but those methods are actually proxies of those original methods. When one of these proxy methods is called, it calls the original method on all (see caveat 1) the selected objects in the array. But otherwise the returned object can just be used as a normal array (or more accurately, NodeList in this case).
However it's worth mentioning that there are several caveats with this particular implementation.
The list of proxy methods created is the union of the methods of all selected objects, not intersection. Suppose you selected two elements - A and B. A has method doA() and B has method doB(). Then the array returned by app() will have both doA() and doB() proxy methods. However when you call doA() for example, only A.doA() will be called because obviously B does not have a doA() method.
If the selected objects do not have the same definition for the same method name, the proxy method will use their individual definitions. This is usually desired behaviour in polymorphism but still it's something to bear in mind.
This implementation does not traverse the prototype chain, which is actually a major problem. It only looks at the prototypes of the selected elements, but not the prototypes of prototypes. Therefore this implementation does not work well with any inheritance. I did try to get this to work by making getProtoMethods() recursive, and it does work with normal JS objects, but doing that with DOM elements throws weird errors (TypeError: Illegal Invocation) (see here). If you can somehow fix this problem then this would be a fully working solution.
This is the problematic recursive code:
// Recursively gets all nested prototype methods for one object
function getProtoMethods(obj) {
let methods = {};
// Loop through all prototype properties of obj and add all functions
for (let pKey of Object.getOwnPropertyNames(Object.getPrototypeOf(obj))) {
// Skip properties that aren't functions and constructor
// obj[pKey] throws error when obj is already a prototype object
if (pKey !== "constructor" && typeof obj[pKey] === "function") {
methods[pKey] = obj[pKey];
}
}
// If obj's prototype has its own prototype then recurse.
if (Object.getPrototypeOf(Object.getPrototypeOf(obj)) == null) {
return methods;
} else {
return {...methods, ...getProtoMethods(Object.getPrototypeOf(obj))};
}
}
Sorry I cannot solve your problem 100%, but hopefully this at least somewhat helpful.

Javascript prototype function override when x

In my case, I'm using the Phaser framework.
So in this example I'm extending the Group class of phaser. Every 'actor' class (Sprite, Group, ...) calls upon the update() prototype every few miliseconds.
My idea was to extend this function only when the application runs on a desktop (so not on a phone).
for example:
var MousePointer = function (game, parent, name) {
Phaser.Group.call(this, game, parent, name);
this.init();
};
MousePointer.prototype = Object.create(Phaser.Group.prototype);
MousePointer.prototype.constructor = MousePointer;
MousePointer.prototype.init = function () {
// ... init
};
MousePointer.prototype.update = function () {
// Do something when on desktop
};
I can't possibly use an if clausule in the update() function to check whether the player is on dekstop/tablet/phone. So is there a way to actually override the prototype on initialisation?
for example (pseudocode):
if(onPhone)
MousePointer.prototype.update = parent.prototype.update;
else
MousePointer.prototype.update = this.update;
Well, you've kind of already written the answer for yourself, haven't you? This code (not inside the init method).
if(onPhone) {
MousePointer.prototype.update = function(){//Phone implementation};
} else {
MousePointer.prototype.update = function(){//Other implementation};
}
I advise against starting off with the "regular" function and then potentially overriding it, since you're just declaring it for nothing.
I think a better way to do this would be to write two different classes that shares the same parent, and then write different update() implementations for them. Then you can just do something like:
if(phone) {
var obj = new PhoneMousePointerObject();
} else {
var obj = new DesktopMousePointerObject();
}
// ... later
obj.update()

How to assign a function to a object method in javascript?

I'd like to 'proxy' (not sure if that's the term at all) a function inside a function object for easy calling.
Given the following code
function Soldier() {
this.el = $("<div></div>").addClass('soldier');
this.pos = this.el.position; // $(".soldier").position(), or so I thought
}
In the console:
s = new Soldier();
$("#gamemap").append(s.el); // Add the soldier to the game field
s.pos === s.el.position // this returns true
s.el.position() // Returns Object {top: 0, left: 0}
s.pos() // Returns 'undefined'
What am I doing wrong in this scenario and is there an easy way to achieve my goal (s.pos() to return the result of s.el.position()) ?
I thought about s.pos = function() { return s.el.position(); } but looks a bit ugly and not apropriate. Also I'd like to add more similar functions and the library will become quite big to even load.
When you're calling s.pos(), its this context is lost.
You can simulate this behavior using call():
s.pos.call(s); // same as s.pos()
s.pos.call(s.el); // same as s.el.position()
This code is actually ok:
s.pos = function() { return s.el.position(); }
An alternative is using bind():
s.pos = s.el.position.bind(el);
You can use the prototype, that way the functions will not be created separately for every object:
Soldier.prototype.pos = function(){ return this.el.position(); }
I'd recommend to use the prototype:
Soldier.prototype.pos = function() { return this.el.position(); };
Not ugly at all, and quite performant actually.
If you want to directly assign it in the constructor, you'll need to notice that the this context of a s.pos() invocation would be wrong. You therefore would need to bind it:
…
this.pos = this.el.position.bind(this.el);
It's because the context of execution for position method has changed. If you bind the method to work inside the element context it will work.
JS Fiddle
function Soldier() {
this.el = $("<div></div>").addClass('soldier');
this.pos = this.el.position.bind(this.el);
}
var s = new Soldier();
$("#gamemap").append(s.el);
console.log(s.pos());

How to get the name of a jQuery UI widget?

I need a way to read the name of a jQuery UI widget. I have subclassed the dialog widget into two subclasses, myDialog1 and myDialog2. I have created a destroyDialog function to destroy whichever dialog is active. There should be a way to determine the name of the widget instance.
What I want to do is something like this:
var destroyDialog = function() {
activeDialog[activeDialog.widgetName]("destroy");
}
But I don't see a way to get the widget name. For now I'm using ugly nested try-catch statements.
var destroyDialog = function() {
try {
activeDialog.dialog("destroy");
}
catch (e) {
try {
activeDialog.myDialog1("destroy");
}
catch (e) {
activeDialog.myDialog2("destroy");
}
}
}
You can get the widget name (and use it) by using
activeDialog.data("widgetName");
... as tdmartin refers to. So therefore:
activeDialog[activeDialog.data("widgetName")]("destroy");
But to get around this problem personally, I have written a plugin that will allow you to call a widget method without knowing what type the widget is. This will allow you to do:
activeDialog.callWidgetMethod('destroy');
It relies on you using jQuery UI 1.11+. If you are using <1.11, you can take out the "Skip this widget if it does not have the method" check, but the downside of that is that you will get an error if you attempt to call a method that a widget does not have.
Plugin code:
jQuery.fn.callWidgetMethod = function () {
var args = arguments;
var result = null;
if(!this || !this.length)
return this;
this.each(function (i,o) {
var compClass = $(this).data("widgetName");
var func = $(this)[compClass];
// Skip this element if it does not appear to be an initialised jQuery widget
if(!compClass || !func)
return true;
// Skip this widget if it does not have the method (the name of which will be in args[0])
// This relies on the 'instance' method provided in jQuery UI 1.11
if(args.length>1 && !$(this)[compClass]("instance")[args[0]])
return true;
result = func.apply($(this),args);
});
if(this.length>1)
return this;
else
return result;
};
If you standardize your namespace you could use a regex to match the name of the variable where your widget instance is stored (the name of the widget), returned by the $().data() method.
for (i in $(<your element>).data() ) {
if (i.match(/dialog/)) {
$(<your element>).data(i).destroy();
}
}

How do I create methods for an HTML element?

I'm trying to create a simple, small and basic javascript framework just for learning purposes.
But the thing is that i'm allready stuck at the very basics.
I'm trying to do something like this:
$('testdiv').testFunction();
And the code i've written for that:
var elementID;
var smallFramework = {
$:function(id) {
this.elementID = id;
},
testFunction:function() {
alert(this.elementID);
}
};
window.$ = smallFramework.$;
But in return I get:
$('testdiv) is undefined
Can anyone help me with this small and hopefully easy question?
To get the behavior you're expecting, you need the $ function to return an object with a method named testFunction.
Try:
var smallFramework = // an object for namespacing
{
$:function(id) // the core function - returns an object wrapping the id
{
return { // return an object literal
elementID: id, // holding the id passed in
testFunction: function() // and a simple method
{
alert(this.elementID);
}
};
}
};
Of course, there are many other ways to achieve the behavior you desire.
If you're trying to add methods to an HTML element you could do something along these lines.
$ = function( elementId ) {
var element = document.getElementById( elementId );
element.testFunction = function(){
alert( this.id );
return this; // for chaining
}
return element;
}
$('test').testFunction();
Try
smallFramework.$('testdiv');
instead. According to the code you posted, that's where your $ function ended up.
Or alternatively, it looks like you're trying to replicate something like jQuery. You might want to try something like this.
var $ = smallFramework = (function () {
var f =
{
find:function(id) {
f.elementID = id;
return f; //every function should return f, for chaining to work
},
testFunction:function() {
alert(f.elementID);
return f;
}
}
return f.find //the find function will be assigned to $.
//and also assigned to smallFramework.
//the find function returns f, so you get access to testFunction via chaining
// like $("blah").testFunction()
})() //note this function gets called immediately.
this code may look confusing to someone new to JavaScript because it depends heavily on the concept of closures. I suggest that if this doesn't make sense, spend some time at Douglas Crockford's JavaScript website. This is important because the code above will bite if you happen to use this in the find function because this won't be bound to f, as you may expect it to be when you use it from $ or smallFramework.

Categories

Resources