Access object in array which is a property of another object - Javascript - javascript

It's been a long time since I learned OOP and I'm new to JS, so things might look weird for more advanced users - sorry :)
function page(title) {
this.subPages = [];
this.title = title;
}
page.prototype.addToSubPages = function(subPage) {
this.subPages.push(subPage);
}
page.prototype.getSubPages = function() {
return this.subPages;
}
Now I create 2 objects:
startPage = new page("start");
somePage = new page("foo");
...and try to add somePage into the array in startPage:
startPage.addToSubPages(somePage);
Now this doesn't seem to work, although it should be correct, if I'm not mistaken.
console.log(startPage.getSubPages());
This shows me that something is in the array, but the object appears to be empty. What am I doing wrong?
Also: How would I access a certain element in that array? Like this: startPage.getSubPages()[0].getFoo();?
Edit: Holy Mackerel, Batman! This is more like my actual code: http://jsfiddle.net/pZHKS/2/
You're all right, the code I posted actually works. As soon as inheritance comes into play, it doesn't work anymore. Why, though? It should work exactly like the code above, right?

function page(title) {
this.title = title;
}
function subPage() {
this.contentPages = [];
}
subPage.prototype = new page;
There are two problems.
your not calling page in subPage.
your using Child.prototype = new Parent; that's wrong, use Object.create instead
So the fixed code would be.
function page(title) {
this.title = title;
}
function subPage(title) {
page.call(this, title);
this.contentPages = [];
}
subPage.prototype = Object.create(page.prototype);

Related

adding multiple properties to object in a loop

I think my question is easy but I dont understand why my solution doesnt work :(
I'm trying to add to a new object some long properties using foreach loop but I'm getting the whole time error.
Could someone please help me?
let i = 0;
const obj = {};
for (const item of read) {
console.log(item.name);
console.log(item.imageURL);
obj['hits']['hits'] ='whyDosntWork';
console.log(item.name);
if (item.imageURL) {
obj['hits']['hits'][i]['_source.ActiveChannelReleases'][0]['ImageExports'][0]['Resolutions'][0]['Url'] =getServerURL()+item.imageURL;
} else {
obj['hits']['hits'][i]['_source.ActiveChannelReleases'][0]['ImageExports'][0]['Resolutions'][0]['Url'] ='https://cdn3.iconfinder.com/data/icons/kitchen-glyph-black/2048/4834_-_Cookbook-512.png';
}
console.log(item.imageURL);
i++;
}
I have an response and I want to mock it with my data
I wish to have for example an object that I can fill with data:
class ResponseController {
constructor() {
this.response = {
'hits': {
'hits': [{'_source.ActiveChannelReleases': [{'ImageExports': ['Resolutions']}],
}],
},
};
}
}
module.exports = ResponseController;
Will it work if I write
obj = new ResponseController();
and then I can easily add variables from the looo?
First this is a madness :).
Why is not working?
const obj = {};
you cannot do this obj['hits']['hits'] ='whyDosntWork'; due to obj['hist'] does not exists.
You need to do:
obj['hits'] = {}
and then obj['hits']['hits'] ='whyDosntWork';
And the same for the rest...
I cannot understand what do you want to do here:
obj['hits']['hits'][i]['_source.ActiveChannelReleases'][0]['ImageExports'][0]['Resolutions'][0]['Url']
But follow what I said before, you need to create each step the value you want. I can assume that you want an array in ´hits`...
The issue is that you're defining obj to be an object, and then are trying to add stuff into obj.hits without defining that as an object
const obj = {};
obj['hits'] = {}
obj['hits']['hits'] ='whyDosntWork';

Assigning callback events from an array of strings (PIXI.js)

all. I have kind of a doozy of a problem, that could be solved really simply, if I just wanted to duplicate the code. I mean, really, it's a small part of a project that I'm doing just to see if I can, more than anything else, but it is bothering me since I've thought it up.
The Project
For fun, I've decided to take someone's ActionScript 3, text-based game engine and convert it to TypeScript and ultimately JavaScript using PixiJS.
The thing is, there are still 20213 errors to be fixed running tsc, so I could just leave this to a later date. But I was working on the Button class, which they defined as a subclass of MovieClip. That's fine; I just responded by reading up on PIXI buttons, and they seem fairly straightforward. Just, in the button's constructor, add something akin to the following lines:
export class Button extends PIXI.Sprite {
private _callback : Function;
private _height : number;
private _width : number;
public get callback() : Function { return this._callback; }
public set callback(fn : Function) {this._callback = fn; }
public get height() : number { return this._height; }
public set height(h : number) {this._height = h; }
public get width() : number {return this._width; }
public set width(w : number) {this._width = w; }
public constructor(width = 180, height = 90, callback: Function = null){
super(new PIXI.Texture(new PIXI.BaseTexture(GLOBAL.BTN_BACK, PIXI.SCALE_MODES.NEAREST)));
this.callback = callback;
this.width = width;
this.height = height;
this.buttonMode = true;
this.interactive = true;
this.anchor.set(0.5);
this.on('mousedown', this.callback)
.on('touchstart', this.callback);
}
}
That's a bit of a simplified version, and the version I did on Codepen uses a Container and a private _sprite field instead (as well as a ColorMatrixFilter that doesn't work too well on the black icons I picked out, but that's not really important for this question), but that's roughly the gist of how it's done.
The Problem
The problem is that, in the codepen, I'd like to do the following:
// assign `this.callback` to each of the following events:
let that = this;
['click','mousedown','touchstart'].map(evt => that.on(evt, that.callback});
with a simple call being passed in their constructors elsewhere:
for (let n = 0; n < 5; ++n){
btnArray.push(new Button(16, 16, () => console.info('You pushed button %d', n)));
}
but I'm not getting anything from them, even in the Chrome Console. I even logged that ColorMatrixFilter I mentioned earlier, to see if it was console.info that was wrong. Nope. So now, I'm confused on that. I was hoping to be able to just make a GLOBAL (a legacy static object from the AS source) key to iterate through for the events, but it looks like that's not happening.
The Questions
Is what I'm trying to do feasible, if odd? Is it blocked by a security feature (for which I'd be grateful)? If not, what am I doing wrong?
Should I even worry about setting all these different event handlers, or is just listening to click enough?
When an arrow function like your event map is executed the this context is not set, so any code that references this is going to get the current value, including any functions your map calls.
Replace your event map with the following:
['click','mousedown','touchstart'].map(function(evt) { that.on(evt, that.callback} } );
A demonstration:
function Named(x) {
this.name = x;
}
var foo = new Named("foo");
var bar = new Named("bar");
var showFunc = function show() {
// this is context dependant
console.log(this.name);
}
var showArrow;
// this is the window
showArrow = () => console.log(this.name);
var fooShowArrow;
(function() {
// this is foo
that = this;
fooShowArrow = () => console.log(that.name);
}).apply(foo);
var example = function(func) {
// For the demo, at this point, this will always be bar
func.apply(this, [ "arbitrary value" ]);
}
// explicitly set the current "this" to bar for the execution of these functions
example.apply(bar, [showFunc]); // works
example.apply(bar, [showArrow]); // fails, this is still the window
example.apply(bar, [fooShowArrow]); // fails, this is still foo

Get calling arguments for getter in javascript

Given a javascript object like this:
var myThing = {};
Object.defineProperty(myThing, 'gen', {
'get' : function() {
// access caller name here, so I can return cool/neat stuff
}
});
I want to be able to get children of myThing.gen, but know what is being asked for in the getter.
for example:
var coolThing = myThing.gen.oh.cool;
var neatThing = myThing.gen.oh.neat;
I want the "oh.cool" or "oh.neat" part in getter, so I can make decisions based on this, and return something specific to it. I am ok with solution not working in IE, or old browsers, as it is primarily for node.
The actual purpose of this is so that I can request myThing.gen.oh.neat and have the myThing.gen getter resolve to require('./oh/neat.js') and return it.
Since require cache's, this is an efficient way to dynamically load modular functionality, and have a tidy interface (rather than just dynamically building the require where needed) without having to know the structure ahead of time.
If there is no introspection-of-name function that can get this for me, I could just do something less elegant, like this:
myThing.gen = function(name){
return require('./' + name.replace('.', '/') + '.js');
}
and do this:
neatThing = myThing.gen('oh.neat');
I don't like this syntax as much, though. I looked at chai's dynamic expect(var).to.not.be.empty stuff, but couldn't figure out how to do it completely dynamically. Maybe there is not a way.
without actually solving the problem of dynamically discovering the caller, I can do this:
var myThing = {};
Object.defineProperty(myThing, 'gen', {
'get' : function() {
return {
'oh':{
'cool': require('./oh/cool.js'),
'neat': require('./oh/neat.js')
}
};
}
});
Is there a way to do this dynamically?
You can't see what the property gen will be used for in the future, so you would need to return an object with properties that react to what the object is used for when it actually happens:
var myThing = {};
Object.defineProperty(myThing, 'gen', {
'get' : function() {
var no = {};
Object.defineProperty(no, 'cool', {
get: function(){ alert('cool'); }
});
Object.defineProperty(no, 'neat', {
get: function(){ alert('neat'); }
});
return { oh: no };
}
});
Demo: http://jsfiddle.net/UjpGZ/1/

Passing references in javascript

This is my first SO post. I'm eternally grateful for the information this community has and shares. Thanks.
I'm coming from Flash and I'm not even sure what the right question to ask is. All I can do is lay out my code example and then explain what I am trying to do. I do not fully grasp the terms that I am trying to illustrate here so I feel it is best to omit them.
The code below is incomplete as it only includes the parts that I feel are relevant to my question. Please refer to the comments in my code to see my issue.
EDIT: Full source file here: [link removed] The console.log outputs the issue in question.
<script type="text/javascript">
var a_chests = [];
var chestID = 0;
//I'm creating a plugin to be able to make multiple instances
(function ($) {
$.fn.chestPlugin = function (option) {
//This function creates a master sprite object which many of my sprites will use
//I've simplified the features to get to the heart of my question
var DHTMLSprite = function (params) {
var ident = params.ident,
var that = {
getID: function(){
return ident;
}
};
return that;
};
//ChestSprite inherits DHTMLSprites properties and then adds a few of its own
var chestSprite = function(params) {
var ident = params.ident,
that = DHTMLSprite(params);
that.reveal=function(){
console.log(ident);
};
return that;
};
//Here I create multiple instances of the chests
var treasure = function ( $drawTarget,chests) {
for (i=0;i<chests;i++){
var cs = chestSprite({
ident: "chest"+chestID
})
console.log(cs.reveal())
//This logs "chest0", "chest1", "chest2" as the for loop executes
//This behavior is correct and/or expected!
a_chests[chestID]={id:i,ob:cs};
//I add a reference to the new chestSprite for later
chestID++;
//increment the chestID;
}
console.log(a_chests[1].ob.reveal());
//This always logs "chest2" (the last chest that is created), even though
//the logs in the for loop were correct. It seems it is referencing the
//DHTML object (since the DHTMLSprite function returns that;) and since
//there is no reference to which chest I need, it passes the last one.
//Is there any way I can pass a reference to DHTMLSprite in order to retain
//the reference to the three individual chests that are created?
//Is there another solution altogether? Thanks!!!
};
//The rest of the code.
return this.each(function () {
var $drawTarget = $(this);
treasure($drawTarget,3);
});
};
})(jQuery);
</script>
You forgot to declare `that' as a local variable, so it's being overwritten on each iteration.
var chestSprite = function(params) {
var that;
var animInterval;
...
When you write:
a_chests[chestID]={id:i,ob:cs};
You are assigning the cs object itself, not an instance of this object. If later you modify cs, this will also modify what you stored in the ob property.
I guess what you need is a closure:
for (i=0;i<chests;i++){
(function(){
var cs = chestSprite({ident: "chest"+chestID});
a_chests[chestID]={id:i,ob:cs};
})();
}
This way, each loop creates a different cs object.

Stacking up methods in Javascript

I have an object I created with this snip-it that looks like this:
...
var steps = new Array();
this.createStep = function(){steps.push(new step()); return steps[steps.length-1];};
this.getSteps = function(){return steps;}; //returns Array
this.removeStep = function(pos){steps.splice(parseInt(pos), 1);}; // integer possition zero base
this.insertStep = function(pos){steps.splice(parseInt(pos),0, new step());};
And this works fine:
...
var newStep = wfObj.createStep();
newStep.setTitle('Step-'+i);
newStep.setStatus('New');
but this does not
var newStep = wfObj.createStep().setTitle('Step-'+i).setStatus('New');
Could someone please tell me how to fix this or even what to call it when you chain methods like this?
This is called a fluent interface. The way to make it work is to have every function return this.
As Ned said, this is sometimes called fluent interface. It's also sometimes called method chaining, as you have heard.
You probably have some code somewhere that looks like this:
this.setTitle = function(newTitle) {
title = newTitle;
};
Change that to this:
this.setTitle = function(newTitle) {
title = newTitle;
return this;
};
Do the same for setStatus.

Categories