Related
I am working on a javascript based character sheet system for role playing games. Within the specific type of RPG I am trying to implement there exists something of an inheritance tree, so that there are the basic RPG-system rules, there are more specific rules for a given setting within that RPG-system, and then there might be differences between specific versions of that setting.
For a given user interaction the code should execute some actions for the overall RPG-system, and some other code for the specific setting and version that is being used.
Currently I am handling this by doing something in the form of:
CharacterSheet.prototype.handleSomeUserInteraction() {
this.handleSomeUserInteractionForSystem();
this.handleSomeUserInteractionForSetting();
this.handleSomeUserInteractionForVersion();
}
// Empty implementations here to avoid errors, this will be overwritten with a
// function that does the actual handling in a separate file
CharacterSheet.prototype.handleSomeUserInteractionForSystem() {}
CharacterSheet.prototype.handleSomeUserInteractionForSetting() {}
CharacterSheet.prototype.handleSomeUserInteractionForVersion() {}
The above is of course inelegant and not efficient - some interactions don't require handling at all three levels, but I still have to call an empty callback. (It should be noted that, of course, I don't know in advance which of these callbacks are needed - it depends on the particular system/setting/version.)
Ideally I would like to achieve the following:
It should be lightweight - in rare circumstances these callbacks need to be called maybe hundred times per second.
Only required/non-empty callbacks should be called.
The interface to registering a callback should be intuitive and clean, so that other developers who are otherwise unfamiliar with the rest of the code can use it easily develop their own RPG system/setting/version code.
I.e. it should be obvious which parts of the code can be extended (e.g. where a callback can be registered) and how.
Related to the above, there should be as little juggling with e.g. 'this' arguments as possible.
How can I best achieve the above objectives?
NOTE: Since inheritance is a bit wonky for me in JavaScript I have not gone that path, I have also read that JS inheritance may decrease performance (though I suspect that at my required four levels of inheritance it would not be a problem).
NOTE: I am looking for a clean vanilla JS solution, I am not a big fan of all these frameworks that bloat the codebase needlessly.
This is what I have come up with so far - inspired by the event driven solution suggested by #Nope.
CharacterSheet.prototype.onUserInteraction = [];
CharacterSheet.prototype.handleSomeUserInteraction()
{
var _this = this;
this.prototype.onUserInteraction.forEach(callback => {callback.call(_this);});
}
// Implementers then first create an implementation of the callback:
CharacterSheet.prototype.handleOnUserInteracation = function()
{
// Do something here
}
// And then register the callback this way:
CharacterSheet.prototype.onUserInteraction.push(CharacterSheet.prototype.handleOnUserInteracation);
This is not as clean and intuitive as I would ideally like, but it ensures that the code written in the callback can be treated as if it was written directly for the CharacterSheet prototype, and I do not have to mess with true JS inheritance.
Feedback for this approach would be appreciated, and alternative solutions will be upvoted if you explain the advantages/disadvantages. If it's good enough I will gladly mark it as accepted also.
Please note that the above code was written from memory, so it might contain some minor mistakes.
The nature of JavaScript allows for its native objects to be completely re-written. I want to know if there is any real danger in doing so!
Here are some examples of native JavaScript objects
Object
Function
Number
String
Boolean
Math
RegExp
Array
Lets assume that I want to model these to follow a similar pattern that you might find in Java (and some other OOP languages), so that Object defines a set of basic functions, and each other object inherits it (this would have to be explicitly defined by the user, unlike Java, where everything naturally derives from object)
Example:
Object = null;
function Object() {
Object.prototype.equals = function(other) {
return this === other;
}
Object.prototype.toString = function() {
return "Object";
}
Object.equals = function(objA, objB) {
return objA === objB;
}
}
Boolean = null;
function Boolean() {
}
extend(Boolean, Object); // Assume extend is an inheritance mechanism
Foo = null;
function Foo() {
Foo.prototype.bar = function() {
return "Foo.bar";
}
}
extend(Foo, Object);
In this scenario, Object and Boolean now have new implementations. In this respect, what is likely to happen? Am I likely to break things further down the line?
Edit:
I read somewhere that frameworks such as MooTools and Prototype have a similar approach to this, is this correct?
Monkey patching builtin classes like that is a controversial topic. I personally don't like doing that for 2 reaons:
Builtin classes are a global scope. This means that if two different modules try to add methods with the same name to the global classes then they will conflict, leading to subtle bugs. Even more subtly, if a future version of a browsers decides to implement a method with the same name you are also in trouble.
Adding things to the prototypes of common classes can break code that uses for-in loops without a hasOwnProperty check (people new to JS often do that to objects and arrays, since for-in kind of looks like a foreach loop). If you aren't 100% sure that the code you use is using for-in loops safely then monkeypatching Object.prototype could lead to problems.
That said, there is one situation where I find monkeypatching builtins acceptable and that is adding features from new browsers on older browsers (like, for example, the forEach method for arrays). In this case you avoid conflicts with future browser versions and aren't likely to catch anyone by surprise. But even then, I would still recommend using a shim from a third party instead of coding it on your own, since there are often many tricky corner cases that are hard to get right.
There's some level of preference here, but my personal take is that this sort of thing has the potential to become a giant intractable mess.
For example, you start with two projects, A and B, that each decide to implement all sorts of awesome useful fluent methods on String.
Project A has decided that String needs an isEmpty function that returns true if a string is zero-length or is only whitespace.
Project B has decided that String needs an isEmpty function that returns true if a string is zero-length, and an isEmptyOrWhitespace function that returns true if a string is zero-length or is only whitespace.
Now you have a project that wants to use some code from Project A and some code from Project B. Both of them make extensive use of their custom isEmpty functions. Do you have any chance of successfully joining the two? Probably not. You are in a cluster arrangement, so to speak.
Note that this is all very different than extension methods in C#, where you at least have to import the containing static class's namespace to get the extension method, there's no runtime conflict, and could reasonably consume from A and B in the same project as long as you didn't import their extensions namespace (hoping that they had the foresight to put their extension classes in a separate namespace for exactly this reason).
The worst case in JS that I know of along these lines is undefined. You can define it.
You're allowed to do things like undefined = 'blah';.... at which point, you can no longer rely on if(x === undefined). Which could easily break something elsewhere in your code (or, of course, in a third party lib you may be using).
That's completely bonkers, but definitely shows the definitely dangers of arbitrarily overwriting built-in objects.
See also: http://wtfjs.com/2010/02/15/undefined-is-mutable
For a slightly more sane example, take the Sahi browser testing tool. This tool allows you to write automated scripts for the browser to test your site. (similar to Selenium). One problem with doing that is if your site uses alert() or confirm(), the script would stop running while it waits for user input. Sahi gets around this by overwriting these functions with its own stub functions.
I avoid overriding the default behavior of the inherent objects. It's biten me a few times, while others I was fine. A library you can look at for an example is Sugar.js. Its a great library that some folks love, but I generally avoid it simply because it extends the behavior of existing JavScript objects, such as what you are doing.
I think however that you will find that this is purely opinion and style.
I recently watched Douglas Crockford's JavaScript presentations, where he raves about JavaScript prototype inheritance as if it is the best thing since sliced white bread. Considering Crockford's reputation, it may very well be.
Can someone please tell me what is the downside of JavaScript prototype inheritance? (compared to class inheritance in C# or Java, for example)
In my experience, a significant disadvantage is that you can't mimic Java's "private" member variables by encapsulating a variable within a closure, but still have it accessible to methods subsequently added to the prototype.
i.e.:
function MyObject() {
var foo = 1;
this.bar = 2;
}
MyObject.prototype.getFoo = function() {
// can't access "foo" here!
}
MyObject.prototype.getBar = function() {
return this.bar; // OK!
}
This confuses OO programmers who are taught to make member variables private.
Things I miss when sub-classing an existing object in Javascript vs. inheriting from a class in C++:
No standard (built-into-the-language) way of writing it that looks the same no matter which developer wrote it.
Writing your code doesn't naturally produce an interface definition the way the class header file does in C++.
There's no standard way to do protected and private member variables or methods. There are some conventions for some things, but again different developers do it differently.
There's no compiler step to tell you when you've made foolish typing mistakes in your definition.
There's no type-safety when you want it.
Don't get me wrong, there are a zillion advantages to the way javascript prototype inheritance works vs C++, but these are some of the places where I find javascript works less smoothly.
4 and 5 are not strictly related to prototype inheritance, but they come into play when you have a significant sized project with many modules, many classes and lots of files and you wish to refactor some classes. In C++, you can change the classes, change as many callers as you can find and then let the compiler find all the remaining references for you that need fixing. If you've added parameters, changed types, changed method names, moved methods,etc... the compiler will show you were you need to fix things.
In Javascript, there is no easy way to discover all possible pieces of code that need to be changed without literally executing every possible code path to see if you've missed something or made some typo. While this is a general disadvantage of javascript, I've found it particularly comes into play when refactoring existing classes in a significant-sized project. I've come near the end of a release cycle in a significant-sized JS project and decided that I should NOT do any refactoring to fix a problem (even though that was the better solution) because the risk of not finding all possible ramifications of that change was much higher in JS than C++.
So, consequently, I find it's riskier to make some types of OO-related changes in a JS project.
I think the main danger is that multiple parties can override one another's prototype methods, leading to unexpected behavior.
This is particularly dangerous because so many programmers get excited about prototype "inheritance" (I'd call it extension) and therefore start using it all over the place, adding methods left and right that may have ambiguous or subjective behavior. Ultimately, if left unchecked, this kind of "prototype method proliferation" can lead to very difficult-to-maintain code.
A popular example would be the trim method. It might be implemented something like this by one party:
String.prototype.trim = function() {
// remove all ' ' characters from left & right
}
Then another party might create a new definition, with a completely different signature, taking an argument which specifies the character to trim. Suddenly all the code that passes nothing to trim has no effect.
Or another party reimplements the method to strip ' ' characters and other forms of white space (e.g., tabs, line breaks). This might go unnoticed for some time but lead to odd behavior down the road.
Depending on the project, these may be considered remote dangers. But they can happen, and from my understanding this is why libraries such as Underscore.js opt to keep all their methods within namespaces rather than add prototype methods.
(Update: Obviously, this is a judgment call. Other libraries--namely, the aptly-named Prototype--do go the prototype route. I'm not trying to say one way is right or wrong, only that this is the argument I've heard against using prototype methods too liberally.)
I miss being able to separate interface from implementation. In languages with an inheritance system that includes concepts like abstract or interface, you could e.g. declare your interface in your domain layer but put the implementation in your infrastructure layer. (Cf. onion architecture.) JavaScript's inheritance system has no way to do something like this.
I'd like to know if my intuitive answer matches up with what the experts think.
What concerns me is that if I have a function in C# (for the sake of discussion) that takes a parameter, any developer who writes code that calls my function immediately knows from the function signature what sort of parameters it takes and what type of value it returns.
With JavaScript "duck-typing", someone could inherit one of my objects and change its member functions and values (Yes, I know that functions are values in JavaScript) in almost any way imaginable so that the object they pass in to my function bears no resemblance to the object I expect my function to be passed.
I feel like there is no good way to make it obvious how a function is supposed to be called.
My background is in C and I've picked up PHP, mySQL, HTML, CSS without too much issue.
But I'm finding Javascript/jQuery surprisingly difficult to get right.
Very frustrating.
Why?
It seems to violate a number of traditional programming principles (e.g. variable scope)
Undefined variables seem to appear out of nowhere and already have values associated with them. For example (from the jQuery docs):
$("a").click(function(event) {
event.preventDefault();
$('<div/>')
.append('default ' + event.type + ' prevented')
.appendTo('#log');
});
What exactly is "event"? Do I have to use this variable name? Should I just assume that this object is magically instantiated with the right stuff and I can use any of the methods list at the JQuery API?
There seems to be bunch of random rules (e.g. return false to stop a default action, but sometimes this doesn't work?)
Non-deterministic behavior when debugging. (e.g. I refresh the browser, try something and get result X for JS variables I'm watching in Firebug. I refresh again and I get result Y?)
Very messy looking code that is hard to follow. What happens when? I'm using Firebug and Chrome Developer Tools, but I'm not getting enough visibility.
It seems like everyday there's some random JS "rule" that comes up that I've never seen before in any of my JS books or tutorials.
What do I need to do to make Javascript/jQuery more deterministic, controlled, and logical to me?
Are there any resources that explain Javascript's quirks/gotchas?
Thanks!
1) It seems to violate a number of traditional programming principles (e.g. variable scope)
You need to declare variables using var, else it will go into the global scope.
2) Undefined variables seem to appear out of nowhere and already have values associated with them (how did this happen?)
This is possibly related to 1) and/or 4).
3) There seems to be bunch of random rules (e.g. return false to stop a default action, but sometimes this doesn't work?)
You need to let the handler return false as well. E.g. form onsubmit="return functionname()". You also need to return from the "main" function, not only from a closure (a function inside a function), referring to your previous question. It would only return into the "main" function and continue on.
4) Non-deterministic behavior when debugging. (e.g. I refresh the browser, try something and get result X for JS variables I'm watching in Firebug. I refresh again and I get result Y?)
Probably the code was executed before the HTML DOM was finished populating. You need to hook on window.onload or $(document).ready() whenever you want to execute stuff during page load.
5) Very messy looking code that is hard to follow. What happens when? I'm using Firebug and Chrome Developer Tools, but I'm not getting enough visibility.
I bet that you're talking about jQuery source? It's just a large library. You should after all not worry about this when debugging. Rather worry about your own code. However, make sure that you're looking at the unminified version of jQuery's source code.
See also:
JavaScript: the bad parts
What should every JavaScript programmer know
Douglas Crockford's "Javascript: The Good Parts" was an invaluable resource. Javascript plays a lot more like Lua, Lisp, or Python than C, it just happens to LOOK like C.
Link provided to Amazon; I snagged mine from O'Reilly.
To be honest, I think you have a good understanding. Some of my hangups were similar. The way that I have been moving on is "well, if that's the way it is, then that's the way it is". Just accept the idiosyncrasies and plow forward. PHP does some of the same things (variables can show up out of nowhere, etc...). Just code the way you want to code and if it works, then great!
Then after you get to that point start breaking out the profiler and see if there's anything that you can optimize.
Here are a couple of things:
If you understand CSS, then jQuery selectors should be easy. As far as the code goes, that's straightforward too if you can deal with chaining and JSON. EDIT: also, the jQuery documentation on everything is EXCELLENT! And There is no shortage of jQuery experts here on SO to help us noobs (and hopefully we can return the favor for newer noobs).
There is a scope to work with. (Basically) anything written outside of a function or object is in global scope. If you are inside of an object or function and use var then that sets the variable's scope
Javascript isn't like a C-based language (C++ or PHP even). It uses prototypes to deal with class/object relationships rather than a subclassing scheme.
The #1 thing that threw me for a loop is that any JS that appears anywhere on the page or that was included in <script> tags is fair game. If you have a global variable in one script, you can use that same variable in a completely different script and it will work. That may be what you mean about variables showing up out of nowhere. Also, there are some DOM based variables that can just "show up" too.
Anyways, I think that if you just plow ahead, you'll get some "AHA" moments. I'm a relative noob to programming, but I continually grow as long as I don't hang up on something that doesn't have too much of an impact on actually making the code run.
It's a language based on prototypal inheritance and is influenced by functional programming languages and the paradigm so it isn't completely just OO/Procedural like other languages. Variables are implied globals unless declared with var.
Please include an example?
return false exits out of the function as with any language's return statement. preventDefault() would be the DOM method to cancel the default behaviour of a link
Javascript is used primarily on the client side. Since there are many user agents, each of them have a different implementation of the DOM, which is very inconsistent, moreso than JS itself. Again, please include a real example to get a definitive answer.
You'll find messy looking code in any language, and maybe your lack of understanding perceives the code as messy, when in fact it isn't so bad. Or maybe you're looking at some minified/obfuscated code.
I recommend http://eloquentjavascript.net/ for learning aspects of Javascript.
Things you'll learn from the link above
lambdas
closures
Prototypal inheritance
Event based programming
Debugging
DOM
"JavaScript: The Good Parts" by Douglas Crockford is a good start
In your case, the appendices ("the bad parts" and "the awful parts") might be the most interesting :)
Crockford's "Javascript: The Good Parts" gives some common JS patterns that help with variable privatization and scoping. This is for javascript in general. For jQuery I just use the API. Also the Yui Theatre videos on javascript are quite good
Javascript can be a little tricky and some of it's functional aspects confuses people. If you actually learn and understand the language you'll find it really useful, most people just randomly start using it and then just hate.
Read javascript the good parts by crockford, it's really helpful: http://javascript.crockford.com/
Also make sure you understand closure. It's a fundamental that people don't get but often use.
In terms of variable scope, there are local and global variables. One of the gotchyas of variable scope can be seen in this example:
var thisIsAGlobalVariable
function anon () {
var thisIsALocalVariable
thisIsAGlobalVariable = 5; //if you don't use the var prefix inside a fn, it becomes global
}
You are finding it difficult because:
javascript has another kind of syntax.
javascript is dificult to debug
javascript has no autocompletion like c# etc) ?or does it
javascript has illogical rules (they become logical once you are known with them)
everything can be done in 1000 ways, and when you search for a solution, you will find 2000 answers :) where c#, php mostly have a good practice function u "should/could" use
However, I started using js/jquery a half year ago, with the same reasoning as you do, and I stuck to it, and now I use it daily to enhance my webapps.
I just love it (especcially jquery). It is a life saver, I know what and where to look, I can do about anything with it.
Everything seems logical.
If I can give you one advice: javascript/jquery is a sour apple, but just hang in there, bit trough and you won't regret it.
also, a loooot of people use it and are always willing to lend a hand if needed (I know I do)
Javascript is tricky. You don't have a compiler watching your back. To compensate, unit testing becomes more important. I've been doing my unit testing with jQuery/QUnit, but I recently started using Jasmine (http://github.com/pivotal/jasmine) and I recommend it 200%. Its a great testing framework.
If you're not familiar with testing, or testing with javascript, I'd highly recommend finding unit tests for other OSS javascript projects (hopefully for code you could use) and seeing how they test it.
With unit tests, you'll make the same mistakes, but catch them much sooner and with less grief. And if your tests are good, the mistakes won't come back after you fix tham.
I don't know how much UI design you have done in C, but the event variable only shows up when it is sent by the caller and the handler needs to, well, handle the object. If you do reading on event object, the confusion in q #2 should go away.
There is no event handling in PHP, so I think you have not came across this issue in the past. JavaScript is a programming language with its own purpose, so it was designed to work for that specific purpose.
Maybe you have to link your code to an HTML onclick="event()" button to fire off as the event.
Javascript usage has gotten remarkably more sophisticated and powerful in the past five years. One aspect of this sort of functional programming I struggle with, esp with Javascript’s peculiarities, is how to make clear either through comments or code just what is happening. Often this sort of code takes a while to decipher, even if you understand the prototypal, first-class functional Javascript way. Any thoughts or techniques for making perfectly clear what your code does and how in Javascript?
I've asked this question elsewhere, but haven't gotten much response.
The use of a common library is one thing, but I think a lot of "getting" JavaScript is simply spending time writing it. Things that may seem esoteric will slowly become mundane. This is true of just about any language or framework.
Many of the idioms used in JavaScript, such as anonymous functions, literal syntax, etc. only seem strange when you are first exposed to them. I think the same rules for writing understandable C#/Java/C++/VB/etc. code apply to JavaScript - use variable names that have semantic meaning, write comments that help someone understand intent and acknowledge assumptions, be explicit, etc.
Now, if you are really asking "how can I make JavaScript understandable to someone that is not familiar with it?" you have another issue - JavaScript is JavaScript and developers just have to do the hard work of learning it before they can be proficient in it and become "at one" with it.
For example, this function may seem very strange to those that are not familiar with JavaScript, but for me (and I am certain many others) it isn't that hard to figure out:
// comments are not included *on purpose* for illustrating
// my point about the need for language knowledge
function copy(obj) {
return new (function(o) {
for(var property in o) {
if(o[property].constructor == Array) {
this[property] = [];
for(var i = 0; i < o[property].length; i++) {
this[property][i] = new arguments.callee(o[property][i]);
}
} else if(o[property].constructor == Object) {
this[property] = new arguments.callee(o[property]);
} else {
this[property] = o[property];
}
}
})(obj);
}
The fact that this function has a name helps the casual reader know what it does, but to really understand what it is doing, one has to have an understanding of why this function might be necessary, how JavaScript object properties can be referenced, what data types JavaScript supports, how constructor functions work, how anonymous functions work, etc. Only a decently deep knowledge of those things are going to help (or a library that has literally everything but the kitchen sink).
UPDATE: To those that say comments in the above sample would lend help to the developer attempting to understand it - obviously. Comments are useful and I think that is a given. The above sample code was meant to illustrate multiple JavaScript-specific idiosyncrasies that are only going to be understood by someone with deep enough knowledge about the language.
As I said in the comments on someone else's answer, my code, which I can completely understand based on my JavaScript knowledge, shouldn't need to be so commented that it becomes a substitute for language knowledge. I shouldn't have to explain, for example, where an anonymous function is or that I am using one in the above code as an anonymous constructor function and that it will alter the perceived standard behavior (to C# and Java devs) of the this keyword, all things that are going to confuse lesser JavaScript developers.
One good way to do this is to use the principles of Unobtrusive JavaScript to separate concerns. The use of jQuery as suggested by Sebastian used this principle.
Unobtrusive JavaScript
My opinion is twofold:
Learn the language. JavaScript closures and {key: {function}} are not that hard to understand. In fact, it's a pretty common dialect.
Learn the framework. jQuery, prototype, etc. are all great frameworks that tries to follow a principle of regularity and homogeneity along all the API. If you grasp this way of doing things in your own JavaScript code, things become clear.
For example, I recently needed a method for applying custom behavior to some inputs. Instead of going functional style, I extended all the <input> elements with my method (I'm using prototype), which goes in line with the rest of the prototype stuff. If one keeps these kinds of principles in mind when developing, most of the code becomes pretty straightforward.
My biggest complaint for JavaScript is to know how to organize my .js files, but that's another story.
I think it is one of the aims of those frameworks like prototype or jQuery to hide most of the JavaScript stuff. Since it is not easy to understand it, they are designed to be as easy as can be.
Therefore if you use such frameworks JavaScript gets cleaner. What I do is to write many many comments, use a lot of whitespace and lines and so on. But those {{function(){... stories remain.
Don't forget that when you write (or come across) a particularly esoteric bit of code, you should probably consider explaining what it's doing (and why it's doing it) with a quick comment, which will help anyone else who has to read the code, but who might not be literate enough in the language to understand what's actually happening.
#Jason Bunting
I've got to say I disagree that "only a decently deep knowledge" of the ins and outs of javascript will help one's understanding of your copy() example. Comments along the lines of "deep copy" or "recursive call to copy object properties" and so forth can quickly elucidate for the programmer who isn't green behind the ears but doesn't know all the particulars of Javascript's peculiarities.
I know there are a number of important concepts to pick up before you can really know Javascript, but that doesn't mean code can't be clearer at a glance.
Format your code. If you have no idea how to use it - rely on IDE ( NetBeans or WebStorm has it inbuilt ) or JS Beautyfier ( if you are still coding in Notepad, for some reason. )
Use general principles for clean code. Use brackets, stick to one code convention, use comprehensible naming scheme, comment sparingly, avoid global variables, deep nesting, long functions and esoteric code.
Make sure your IDE can recognize your code. Often this means you have to scrap module pattern aka power constructor var module = ( function () { ... } )(); in favor of classic constructor Module = function () { ... }; Module.prototype.method1 = function () { ... };
Declare vars in the beginning of function, to avoid well known problems with variable hoisting.
Replace this in constructors with better name. Which somehow shows actual object purpose. function Car() { _car = this; _car.accelerate = function () { ... }; }
Write cross-browser code for modern browsers, and, if you need support for old crap like IE8 and below - use jQuery or other framework. Avoid doing browser detection anywhere after your initializer function, if possible.
Use JS Doc, when you can - i.e. if your IDE or repo-browser supports it.
Write for debug, when your code is really complex. i.e.: do not function() { return { huge chunks of code, do not abuse chaining, do not inline things that might break.
Do not use eval, neither standalone, nor implicit eval in setTimeout or addEventListener.