I'm trying to create an object in JavaScript and I'm following Mozilla's tutorial . the tutorial works just fine, but when I apply that technique to my code it doesn't work. (I'm doing something wrong but I don't see it). I coded all my methods and I don't get any errors, I initialize my object and I don't get any errors either, I even call my methods and I don't get errors, but the return value is a string with my code instead of the value that I'm expecting
function JavaScriptObj(id, datatype) {
function initialize(id, datatype) {
if (typeof id === 'number' && id > 1) {
this.theID = id;
} else {
console.error("ERROR: JavaScriptObj.initialize" + id + "is NOT a valid argument");
}
if (typeof datatype === 'string') {
this.data_type = datatype;
} else {
console.error("ERROR: JavaScriptObj.initialize" + datatype + "is NOT a valid argument");
}
}
}
JavaScriptObj.prototype.getSectionName = function(){
var SectionName = "section-" + this.theID;
return SectionName;
};
var person2 = new JavaScriptObj(2, "texteditor");
alert(person2.getSectionName);
this is my jsfiddle
thanks in advance! :-)
Remove the initialize nested function:
function JavaScriptObj(id, datatype) {
if (typeof id === 'number' && id > 1) {
this.theID = id;
} else {
console.error("ERROR: JavaScriptObj: " + id + "is NOT a valid argument");
}
if (typeof datatype === 'string') {
this.data_type = datatype;
} else {
console.error("ERROR: JavaScriptObj: " + datatype + "is NOT a valid argument");
}
}
JavaScriptObj.prototype.getSectionName = function(){
var SectionName = "section-" + this.theID;
return SectionName;
};
var person2 = new JavaScriptObj(2, "texteditor");
alert(person2.getSectionName()); // need to call it too
It looks like you're not actually executing/calling your method. In order to call your method, you need to append parenthesis to the call:
alert(person2.getSectionName());
Small aside -- using console.log() instead of alert() tends to save you a few keystrokes and makes development a bit faster. Also, alert() is a blocking call that stops all other code execution on the page. While that won't make a difference when you're first starting out, it could potentially be a pain point down the road as your javascript ninja skills increase. :)
Related
Going through a course on learning Javascript. The problem asks me to return my name as string. Here is an solution guide they've given me but I'm confused on what I'm missing. Here is what I have so far, but could use help on a step by step on how to solve it.
Guide :
describe("Solution", () => {
it("should return a string as a name", () => {
const name = getUserName();
if (typeof name === "string") {
console.log(`Hello, ${name}!`);
}
expect(name).to.be.a("string");
});
});
**my solution so far: **
function getUserName("Jawwad") {
let name = getUserName()
if (typeof name === String) {
console.log("Hello " + name);
}
return name;
}
I'm expecting it to return my name as string
A few problems with your logic:
You can't call a function inside of the same function.
It doesn't make sense to have a function receive as an argument the name it returns, for example: it would make sense to receive a name as argument and then do something with it like printing.
Here is an example derived from your code:
//innitial name
let theName = "Jonh"
// function purpose is to print the name
function printName(_theName) {
if (typeof _theName === 'string') { //string has to be in quotations
console.log("Hello " + _theName);
}
}
//calling function outside of itself
printName(theName);
For some reason, objects that have been returned from the server end of a Google Apps Script project have any member functions replaced by null. Here's some sample code demonstrating this:
server.gs
function A() {
this.a = 'a string';
this.toString = function() { return this.a; }
this.innerObj = { b : "B", toString : function(){ return 'inner object'; } }
}
function getA() { return new A(); }
clientJS.html; /* or console, if you prefer... */
google.script.run.withSuccessHandler(console.log).getA();
Object, when printed raw, looks something like this:
{ "a": "a string", "toString": null, "innerObj": { "b": "B", "toString": null } }
Live demo of the problem
what can I do about this?!
This is by design, as noted in the documentation
Legal parameters and return values are JavaScript primitives like a Number, Boolean, String, or null, as well as JavaScript objects and arrays that are composed of primitives, objects and arrays. [...] Requests fail if you attempt to pass a Date, Function, DOM element besides a form, or other prohibited type, including prohibited types inside objects or arrays.
As a workaround, you can stringify an object with its methods:
JSON.stringify(obj, function(key, value) {
if (typeof value === 'function') {
return value.toString();
} else {
return value;
}
});
and then reconstruct the functions from strings on the receiving side.
My answer extends Desire's answer. I was able to get this to work, by stringifying the member functions, but for reconstruction, instead of use eval(), I used these:
function shouldBeFunction(str)
{
str = str.toString().trim();
// str should *not* be function iff it doesn't start with 'function'
if (str.indexOf('function') !== 0) return false;
// str should *not* be function iff it doesn't have a '(' and a ')'
if ((str.indexOf('(') === -1) || (str.indexOf(')') === -1)) return false;
// str should *not* be function iff it doesn't have a '{' and a '}'
if ((str.indexOf('{') === -1) || (str.indexOf('}') === -1)) return false;
return true;
}
var myObjectWithFunctions = JSON.parse(objectWithStringsAsFunctions,
function (key, value) {
var DEBUG = false;
if ((typeof(value) === 'string') && (shouldBeFunction(value))) {
if (DEBUG) {
console.log('function string detected on property named : ' + key);
console.log('function text: " ' + value + '"');
}
// get arguments list, if there is one to get
var argsList = value.substring(value.indexOf('(') + 1, value.indexOf(')')).trim();
if (DEBUG) console.log('argsList == ' + argsList);
// get function body
var functionBody = value.substring(value.indexOf('{') + 1, value.lastIndexOf('}')).trim();
if (DEBUG) console.log('functionBody == ' + functionBody);
if (argsList)
return new Function(argsList, functionBody);
return new Function(functionBody);
}
return value;
}
);
The reason being that I don't know if eval() is evil, or a sign of bad programming practice.
UPDATE: I learned that eval() may be OK if the strings came from the server and are being turned back into functions on the client-side
Let's say I have an object:
var shapes = {
addTriangle: function(color){
console.log("added " + color + " triangle");
},
addSquare: function(color){
console.log("added " + color + " square");
}
}
This object will have methods that will be changed and updated frequently, but all the methods will always be running sequentially. Is there a way to automatically get and run all the methods, to ease maintainability?
Something like:
function runShapes(color){
shapeNames = Object.getOwnPropertyNames(shapes);
for(i = 0; i < shapeNames.length; i++){
shapeNames[i].apply(color);
}
}
This gives `shapeNames[i].apply is not a function'. Bonus points for telling me how to do this with functional programming, instead of a for loop :)
Codepen:
http://codepen.io/anon/pen/mJxVyE
you have to check that the property you're calling is actually a function:
if( typeof shapes[shapeNames[i]] === "function") {
// your code here
}
About the 'functional programming' style, you can use foreach, which calls the function you pass as parameter on every element of an array. So your code should look like this:
function runShapes(color){
shapeNames = Object.getOwnPropertyNames(shapes);
shapeNames.forEach(function(prop) {
if(typeof shapes[prop] === "function") {
shapes[prop](color);
}
});
}
First off your mistake... You are iterating through the property names and not the properties.
shapeNames[i].apply(color);
should be:
shapes[shapeNames[i]](color);
A more functional version might look like this:
function runShapes(color){
Object.keys(shapes).forEach(function(each){
shapes[each](color);
});
}
So I would like to pass an object "Hello" in JavaScript file hello.js to another JavaScript file chat.js.
Hello contains members such as Strophe.Connections, and others.
I read that I could use LocalStorage, i.e. LocalStorage.SetItem("Hello", Hello) and then retrieve the object using LocalStorage.getItem("Hello").
This did not work for me, so I searched a bit more and found JSON stringify, e.g. someone at Storing Objects in HTML5 localStorage and also at How to send javascript object from one page to another page? suggested the use of stringify.
So I tried this as well, and here is the relevant code of Hello.js:
var Hello = {
connection: null,
start_time: null,
log: function (msg) {
$('#log').append("<p>" + msg + "</p>");
},
send_ping: function (to) {
var ping = $iq({
to: to,
type: "get",
id: "ping1"}).c("ping", {xmlns: "urn:xmpp:ping"});
Hello.log("Sending ping to " + to + ".");
console.log("Sending ping to " + to + ".");
Hello.start_time = (new Date()).getTime();
Hello.connection.send(ping);
},
handle_pong: function (iq) {
var elapsed = (new Date()).getTime() - Hello.start_time;
Hello.log("Received pong from server in " + elapsed + "ms.");
console.log('Received pong from server in " + elapsed + "ms.');
localStorage.setItem("hello", JSON.stringify(Hello));
return false;
}
};
Here is chat.js
var Hello = {
conn: null
};
$(document).ready(function(e) {
Hello = JSON.parse(localStorage.getItem("hello"));
$('#input3').bind('click',
Hello.connection.send("someStanza"))});
On line Hello = JSON.parse(localStorage.getItem("hello")); I try to restore the object.
And 2 line beow I call Hello.connection.send. However, here it becomes clear that I was not able to pass and restore the Hello object correctly. In Google Chrome JavaScript debugger, for example, I get an error at that line stating "Uncaught TypeError: undefined is not a function", which I would say means basically that the Chrome JS engine was not able to find any connection.send member in the Hello Object.
This leaves two options. Either what I am trying to do (passing an object that is not a primitive data type) is not possible in the way I am doing it. Or, that it is possible in the way I am doing it, and there is simply some other error/problem I am not seeing.
If it is possible, what am I doing wrong?
If it isn't possible to pass a non-primitive data type in this way, how else can I do it?
Hope I made myself clear enough for understanding,
Thanks, br,
Chris
One workaround is to toString the function and then store it in localStorage. Then, when you retrieve the object from localStorage, you eval the function. Here's a quick demo that demonstrates (it's very stupid and can be optimized/enhanced based on your needs...):
http://jsfiddle.net/nmef7utv/
function pack(obj){
var packedObj = {};
for (key in obj) {
// If obj property is a function, toString() it.
if (obj[key] && obj[key].constructor === Function) {
packedObj[key] = obj[key].toString();
} else {
packedObj[key] = obj[key];
}
}
return JSON.stringify(packedObj);
}
function unpack(obj) {
// Temporary variable to hold eval'd func, if needed
var temp;
// Parse obj to loop
obj = JSON.parse(obj);
for (key in obj) {
if (typeof obj[key] === "string") {
eval('temp =' + obj[key]);
// Assign eval'd function
obj[key] = temp;
} else {
obj[key] = obj[key];
}
}
return obj;
}
In the fiddle, I have:
// Packed Item
localStorage.setItem('test', pack(Hello));
// Unpack
testObj = unpack(localStorage.getItem('test'));
testObj.handle_pong();
Hope this is of help!
How to improve the following if-else structure in JavaScript?
if(isIE())
if(termin != "")
TargetElement.onclick = function() {merkzettelRemove(this, id, termin)};
else
TargetElement.onclick = function() {merkzettelRemove(this, id)};
else
if(termin != "")
TargetElement.setAttribute("onclick","merkzettelRemove(this, " + id + ",
'" + termin + "')");
else
TargetElement.setAttribute("onclick","merkzettelRemove(this, " + id + ")");
// get a cross-browser function for adding events
var on = (function(){
if (window.addEventListener) {
return function(target, type, listener){
target.addEventListener(type, listener, false);
};
}
else {
return function(object, sEvent, fpNotify){
object.attachEvent("on" + sEvent, fpNotify);
};
}
}());
// add the event listener
on(TargetElement, "click", function(){
// if termin is empty we pass undefined, this is the same as not passing it at all
merkzettelRemove(this, id, (termin) ? termin : undefined);
});
First things first, put some more braces in. It'll make it clearer what's going on, as well as saving untold heartache when you come to edit this in future.
Yes, you can get away without the braces if they wrap a single statement. When you come along three months from now and add something else into one of those blocks, your code will break unless you remember to wrap the whole block in braces. Get into the habit of doing it from the beginning.
First, use { } everywhere, it will make your loops more readable.
Second: I think this does the same
if(isIE()) {
TargetElement.onclick = function() {
merkzettelRemove(this, id, termin || null);
};
} else {
TargetElement.setAttribute("onclick",
"merkzettelRemove(this, " + id + ",'" + termin || null + "')");
}
Third, but you could try using unobtrusive javascript to add handlers to TargetElement
Firstly, lose the browser-sniffing. onclick= function... works everywhere; setAttribute is not only broken in IE, but also ugly. JavaScript-in-a-string is a big code smell; avoid. setAttribute on HTML attributes has IE problems and is less readable than simple DOM Level 1 HTML properties anyway; avoid.
Secondly, it would be ideal to make merkzettelRemove accept an out-of-band value (null, undefined, or even '' itself) as well as an omitted argument. It is possible it already allows undefined, depending on what mechanism it is using to support optional arguments. If so you could say simply:
TargetElement.onclick= function() {
merkzettelRemove(this, id, termin || undefined);
};
If you must completely omit the argument and you don't want to use an if...else, there's another way around although IMO the clarity is worse:
TargetElement.onclick= function() {
merkzettelRemove.apply(null, termin==''? [this, id] : [this, id, termin]);
};
I think using a Javascript framework is the best solution, but you can try something like this:
var src="merkzettelRemove(this, " + id + (termin && (",'" + termin + "'")) + ")";
if (isIE()) {
TargetElement.onclick = new Function(src);
} else {
TargetElement.setAttribute("onclick", src);
}
I know this is not an answer for your question, but if you don't have any reason for not doing so you should definitely use a library that cares about compatibility issues for you, such as jQuery. Then you could write something like this:
var el = $(TargetElement);
if (termin != "")
el.click(function() {merkzettelRemove(this, id, termin)});
else
el.click(function() {merkzettelRemove(this, id)});
how about using short circuiting operators. So the following code
if(A)
{
if(B) {
C; }
else{
D;}
else{
if(E)
F;
else
G;
}
}
Will become
A && ((B&&C) || D || ((E&&F) || G));