330 likes | 477 Views
JavaScript. The Good and The Bad. Caveat. We are not studying JavaScript so that you can write better scripts for your web pages (although if you’ve done some JavaScript in the past, the info here may help you understand more about what’s really happening)
E N D
JavaScript The Good and The Bad
Caveat • We are not studying JavaScript so that you can write better scripts for your web pages (although if you’ve done some JavaScript in the past, the info here may help you understand more about what’s really happening) • We will not cover the DOM or many techniques used in web pages • We will take a look at some of the more interesting aspects of JavaScript from a programming language design perspective
Data Types and Objects • Primitive types are numbers, strings, booleans, null and undefined • Everything else is an object • An object is simply a mutable, keyed collection • There are no classes! varmy_cat = { name: "Geronimo", // no quote if legal name "coat-color": "orange" // coat_color legal }; document.writeln(my_cat.name); document.writeln(my_cat["coat-color"]); document.writeln(my_cat["color"]); //document.writeln(my_cat.coat-color);
More on objects & prototypes • Can add a property by assigning a value my_cat.age = 3; document.writeln(my_cat.age); • Objects are passed by reference • Object keys are all strings • In addition to the key/value properties, each object has a pointer to the object’s prototype document.writeln(Object.getPrototypeOf(my_cat)); • If not set explicitly, this will be [object Object] • Prototype link is used only in retrieval
Prototypes – simple example • Wordy version (new syntax) • Object.create(proto[, propertiesObject]) varcat = Object.create(null); Object.defineProperty(cat, 'sound', {value: "meow", writeable: true }); // can also set enumerable and configurable varstray_cat = Object.create(cat); Object.defineProperty(stray_cat, 'name', {value: "Sigfried", writeable: true}); stray_cat['coat-color'] = "tuxedo"; document.writeln(stray_cat['name']); document.writeln(stray_cat['coat-color']); document.writeln(stray_cat['sound']);
Prototypes, continued common to override toString The process of using the chain of prototypes to retrieve properties is known as delegation. If the property is not on the chain, the result is undefined. Example from: http://yehudakatz.com/2011/08/12/understanding-prototypes-in-javascript/
Augmenting the basic types Number.prototype.integer = function(){ return Math[this < 0 ? 'ceil':'floor'](this); } document.writeln((-10 / 3).integer()); document.writeln((10 / 3).integer()); • How does this compare to Ruby? See: http://stackoverflow.com/questions/6868883/augmenting-types-in-javascript
Augmenting Object o1 = {x: 2}; Object.prototype.doSomething = function() { document.writeln("Just do it!"); } o1.doSomething(); How do you accomplish this in other languages? Think about: features to add behavior, vs features to organize behavior
Namespace • JavaScript issue: there’s a tendency to use global variables to contain all the data for an app. Clearly this can lead to reliability and maintainability issues. • A namespace is a container which allows developers to bundle up all functionality under a unique, application-specific name. In JavaScript a namespace is just another object containing methods, properties and objects. • Some guidelines (beyond our scope, really): • Place the namespace declaration in its own file • That file can be included in all app files (e.g., XUL) • Link has some guidelines for naming convention • For this course, interesting aspect is how JS handles namespace compared to other languages. https://developer.mozilla.org/en-US/Add-ons/Overlay_Extensions/XUL_School/JavaScript_Object_Management
Functions • Functions are objects • Include two additional hidden properties: context and code Object Object.prototype hidden { name: value name2: value2 fnName: function} Object hidden { name: value name2: value2 } context code Function.prototype
First-class objects • Can be stored in variables, objects and arrays • Can be passed as arguments to functions • Can be returned from functions • Can have methods • Special property: they can be invoked var add = function(a, b) { return a + b; } document.writeln(add(5,6));
Function invocation • Invocation operator is () • Parameters inside (), separated by commas • May have more values than parameters (just ignored) • May have more parameters than values (set to undefined) • No type checking • Two additional parameters, this and arguments • Four different invocation patterns: • Method invocation • Function invocation • Constructor invocation • Apply invocation • Differ in how this is initialized from JavaScript: The Good Parts
Method invocation • Function can be stored as property of an object • this will be bound to the calling object varmyObject = { value: 0, increment: function(inc) { this.value += typeofinc === 'number' ? inc : 1; } }; myObject.increment(); document.writeln(myObject.value); myObject.increment(3); document.writeln(myObject.value);
Function invocation • this is bound to global object • causes issues with inner functions (covered later)
Constructor invocation • Functions intended to be used with new prefix are called constructors • The new prefix causes an object to be created with a hidden link to the function’s prototype method, and this is bound to the new object • This syntax is typically used with classes… but JavaScript has prototypes. Just caused confusion (not recommended, and JavaScript 1.8.5 has Object.create syntax) • NOTE: Why cover this, if it’s not recommended? Because it’s good to think about mistakes that have been made in language design. Also, lots of old examples online… in many languages.
Constructor example var Quo = function(string) { this.status = string; // implicit return value is this }; // Must have prototype, otherwise not shared with // new object. Quo.prototype.get_status = function() { return this.status; }; // Quo is a function object // Putting new in front of it causes a new // object to be created – weird! varmyQuo = new Quo("confused"); document.writeln(myQuo.get_status());
Apply invocation function function sum() { varresult = 0; // note the use of the arguments array for (vari = 0; i < arguments.length; i++) { result += arguments[i]; } return result; } varargs = [1,2,3]; // first argument to apply is the context // in other words, this // second is an array sum.apply(null, args); // will return 6 From: http://stackoverflow.com/questions/1976015/how-to-implement-apply-pattern-in-javascript
Apply invocation with context varobj = { data:'Hello World' } vardisplayData = function() { document.writeln(this.data); } displayData(); //undefined, no global data displayData.apply(obj); //Hello World // call is similar to apply, but it takes // an argument list rather than an array displayData.call(obj); // empty arg list From: http://doctrina.org/Javascript-Function-Invocation-Patterns.html
Another call/apply example var x, o1, o2, r1, r2, r3; x = 4; o1 = {x: 2}; o2 = {x: 7}; f = function(m, n) {return m * n * this.x;}; r1 = f(3, 1); r2 = f.call(o1,3, 1); r3 = f.apply(o2,[3, 1]); document.writeln(r1); document.writeln(r2); document.writeln(r3); From: http://javascript.about.com/od/byexample/a/objectoriented-call-apply-example.htm
Usage brainstorm • How/when might we use apply and/or call? // Say a greeting to any object with a name say_hello = function() { return "Hello " + this.name; } // Add random health to any object with health inc_health = function() { this.health = Math.floor((Math.random() * 100) + 1); } // Assumes o1 already exists as an object o1.name = "Cyndi"; o1.health = 5; document.writeln(say_hello.call(o1)); inc_health.call(o1); document.writeln(o1.health); Quick Discussion: How does this compare to Ruby?
Scope • Most C-inspired languages have block scope • JavaScript has blocks… but not block scope (function scope) var foo = function() { var a=3, b=5; var bar = function() { varb=7, c=11; a += b + c; document.writeln("a: "+a+" b: "+b+" c: " + c); } bar(); document.writeln("a: " + a + " b: " + b); } foo(); Info: Crockford + http://stackoverflow.com/questions/17311693/why-does-javascript-not-have-block-scope
Scope example C# doesn’t compile JavaScript var foo = function() { // this is recommended: // vara; if (true) { // but this works var a = 5; } alert(a); } foo(); public void TestScope() { if (true) { inti = 5; } i.ShouldEqual(5); // does not compile } From: http://lostechies.com/jimmybogard/2008/09/26/javascript-block-scoping/
Inner functions • Inner functions get access to the parameters and variables of the functions they are defined within (exceptthis and arguments)
Inner functions example var add = function(a, b) { return a + b; } varanObject = { value: 4, }; // this not set properly anObject.doubleIt = function() { varhelper = function() { this.value= add(this.value, this.value); }; helper(); }; document.writeln("before doubleIt"); document.writeln(anObject.value); anObject.doubleIt(); document.writeln("after doubleIt"); document.writeln(anObject.value); // common workaround anObject.double = function() { var that = this; var helper = function() { that.value = add(that.value, that.value); }; helper(); }; document.writeln("before double"); document.writeln(anObject.value); anObject.double(); document.writeln("after double"); document.writeln(anObject.value);
Inner function - workaround anObject.double = function() { var that = this; var helper = function() { that.value = add(that.value, that.value); }; helper(); };
Sidebar: first-class functions // avoid repeated code on previous slide anObject.someFn= function(func) { document.writeln("before fn value"); document.writeln(anObject.value); func.call(this); document.writeln("after fn value"); document.writeln(anObject.value); }; // passing the function as an argument anObject.someFn(anObject.doubleIt); anObject.someFn(anObject.double);
Closures* *brief intro, you’ll explore in homework var quo2 = function(status) { // returning an object with get_status method return { // even after quo2 ends, will have access to // copy of parameter, via the context get_status: function() { return status; } }; }; varmyQuo = quo2("amazed"); document.writeln(myQuo.get_status()); myQuo.status = "dazed"; document.writeln(myQuo.get_status()); Examples from JavaScript: The Good Parts
Another closure example var fade = function (node) { var level = 1; varstep = function() { varhex = level.toString(16); node.style.backgroundColor = '#FFFF' + hex + hex; if (level < 15) { level += 1; setTimeout(step, 100); } }; // will call step after 100 milliseconds (1/10th sec) // fade already returned, but its variables (e.g., level) // live on inside this closure. setTimeout(step, 100); }; fade(document.body);
Callbacks • Another use for functions, dealing with asynchronous events • Naïve way to make a server request (freeze til get response): request = prepare_the_request(); response = send_request_synchronously(request); display(response); • A better approach request = prepare_the_request(); send_request_asynchronously(request, function(response) { display(response); }); Again, passing a function as an argument
Memoization • Remembering the results of previous operations, so as to avoid unnecessary work call_count = 0; varfibonacci = function(n) { call_count += 1; return n < 2 ? n : fibonacci(n-1) + fibonacci(n-2); }; document.writeln("\nNAIVE FIBONACCI"); for (vari=0; i<=10; i += 1) { document.writeln('// ' + i + ':' + fibonacci(i)); } document.writeln("count of calls: " + call_count); Cool example from JavaScript: The Good Parts
Memoized Fibonacci varfib_count = 0; var fibonacci2 = function() { var memo = [0,1]; var fib = function(n) { fib_count += 1; var result = memo[n]; if (typeof result != 'number') { result = fib(n-1) + fib(n-2); memo[n] = result; } return result; }; return fib; }(); for (vari=0; i<=10; i += 1) { document.writeln('// ' + i + ':' + fibonacci2(i)); } document.writeln("count of calls: " + fib_count); • fib_count is 29 • 11 calls in for loop • only 18 to calculate values
General purpose memoizer varmemoizer = function (memo, fundamental) { var shell = function(n) { varresult = memo[n]; if (typeof result != 'number') { result = fundamental(shell, n); memo[n] = result; } return result; }; return shell; }; var fibonacci3 = memoizer([0,1], function(shell, n) { return shell(n-1) + shell(n-2); }); document.writeln("Fibonacci 10: " + fibonacci3(10)); var factorial = memoizer([1,1], function(shell, n) { return n * shell(n-1); }); document.writeln("Factorial 5: " + factorial(5));
Object-based vs Object-oriented • http://en.wikipedia.org/wiki/Object-based_language • http://stackoverflow.com/questions/15430004/core-difference-between-object-oriented-and-object-based-language • http://cse3342.blogspot.com/2006/03/object-based-vs-object-oriented-and.html