javascript the good and the bad. caveat we are not studying javascript so that you can write better...

37
JavaScript The Good and The Bad

Upload: abel-bennett

Post on 17-Dec-2015

225 views

Category:

Documents


1 download

TRANSCRIPT

JavaScriptThe 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

• Info from this lecture will be included on the next exam

Language Design • An object “encapsulates” data and methods• Assume you want to create a new language that contains

objects• You want to keep it simple• How can you create something that’s quick and easy to

implement?

Data Types and Objects• Primitive types are numbers, strings, boolean, null and undefined• Everything else is an object• An object is simply a mutable, keyed collection• There are no classes!

var my_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• Can add a property by assigning a valuemy_cat.age = 3;document.writeln(my_cat.age);

• Objects are passed by reference• Object keys are all strings

Prototypes• In addition to the key/value properties, each object has a pointer

to the object’s prototypedocument.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])

var cat = Object.create(null);Object.defineProperty(cat, 'sound',

{value: "meow", writeable: true });

// can also set enumerable and configurablevar stray_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

Example from: http://yehudakatz.com/2011/08/12/understanding-prototypes-in-javascript/

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.

Augmenting the basic types

Number.prototype.integer = function(){ return Math[this < 0 ? 'ceil':'floor'](this); }

document.writeln(10/3);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();o2 = {y: 3};o2.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 – Powerful Stuff!• Functions are objects• Include two additional hidden properties: context and code

Object.prototype { name: value name2: value2 fnName: function}

hidden

Function.prototype

Object

{ name: value name2: value2 }contextcode

Objecthidden

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));document.writeln(add(5,6,7));

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 – similar to instance method• 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

var myObject = { value: 0, increment: function(inc) {

this.value += typeof inc === '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 examplevar 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!var myQuo = new Quo("confused");document.writeln(myQuo.get_status());

Apply invocation functionfunction sum() { var result = 0; // note the use of the arguments array for (var i = 0; i < arguments.length; i++) { result += arguments[i]; } return result; } var args = [1,2,3];// first argument to apply is the context// in other words, this// second is an arraysum.apply(null, args); // will return 6

From: http://stackoverflow.com/questions/1976015/how-to-implement-apply-pattern-in-javascript

Apply invocation with context

var obj = { data:'Hello World'

} var displayData = 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 arraydisplayData.call(obj); // empty arg list

From: http://doctrina.org/Javascript-Function-Invocation-Patterns.html

Another call/apply examplevar 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 namesay_hello = function() { return "Hello " + this.name; }// Add random health to any object with healthinc_health = function() { this.health = Math.floor((Math.random() * 100) + 1); }// Assumes o1 already exists as an objecto1.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() { var b=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

public void TestScope() { if (true) { int i = 5; } i.ShouldEqual(5); // does not compile }

JavaScript

var foo = function() { // this is recommended: // var a; if (true) { // but this works var a = 5; } alert(a); } foo();

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 • except this and arguments• Issues!!

Inner functions examplevar add = function(a, b) { return a + b;}var anObject = { value: 4,};// this not set properlyanObject.doubleIt = function() { var helper = 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 workaroundanObject.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 - workaroundanObject.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 slideanObject.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 argumentanObject.someFn(anObject.doubleIt);anObject.someFn(anObject.double);

Closures*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;}

};};var myQuo = quo2("amazed");document.writeln(myQuo.get_status());myQuo.status = "dazed";document.writeln(myQuo.get_status());

Examples from JavaScript: The Good Parts

*brief intro, you’ll explore in homework

Another closure examplevar fade = function (node) { var level = 1; var step = function() { var hex = 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);

Final closure example

var addScore = (function(points) {var score = 0;return function (points) {

return score+=points; }})()

document.writeln(addScore(5));document.writeln(addScore(7));

• Idea: if points stored in global var, anything could change. This way, score is protected in a closure.

Relatedvar scoreChanger = (function(points){ var score = 0; var operation = {

add: function(points) {return score += points;},sub: function(points) {return score -= points;},mult: function(points) {return score *= points;},div: function(points) {return score /= points;}}return operation;})()

scoreChanger.add(1);document.writeln(scoreChanger.add(2));document.writeln(scoreChanger.mult(5));

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 approachrequest = 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 workcall_count = 0;var fibonacci = function(n) {

call_count += 1;return n < 2 ? n : fibonacci(n-1) +

fibonacci(n-2);};document.writeln("\nNAIVE FIBONACCI");for (var i=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 Fibonaccivar fib_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 (var i=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 memoizervar memoizer = function (memo, fundamental) { var shell = function(n) { var result = 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));