JavaScript för Javautvecklare

Post on 08-May-2015

2.273 views 0 download

description

My Jfokus 2012 tutorial "JavaScript för Javautvecklare". Natural language parts are in Swedish.

Transcript of JavaScript för Javautvecklare

JavaScript för JavautvecklareJohn Wilander, Handelsbanken

@johnwilanderJfokus 2012

Typer

Primitiva:string, number, boolean, function, undefined

Resten är object

Primitiva:byte, short, int, long, float, double, boolean, char

Resten är objekt, s.k. referenstyper

Java

JavaScript

Typer

Primitiva:string, number, boolean, function, undefined

Resten är object

Primitiva:byte, short, int, long, float, double, boolean, char

Resten är objekt, s.k. referenstyper

Java

JavaScript

Typer

Primitiva:string, number, boolean, function, undefined

Resten är object

Primitiva:byte, short, int, long, float, double, boolean, char

Resten är objekt, s.k. referenstyper

Java

JavaScript

Strängar är inte generella objekt utan en egen typ

Typer

Primitiva:string, number, boolean, function, undefined

Resten är object

Primitiva:byte, short, int, long, float, double, boolean, char

Resten är objekt, s.k. referenstyper

Java

JavaScript

Det finns bara en typ för tal,

oavsett om det är heltal eller

decimaltal

Typer

Primitiva:string, number, boolean, function, undefined

Resten är object

Primitiva:byte, short, int, long, float, double, boolean, char

Resten är objekt, s.k. referenstyper

Java

JavaScript

Funktioner är fullödiga objekt

med en egen typ

Typer

Primitiva:string, number, boolean, function, undefined

Resten är object

Primitiva:byte, short, int, long, float, double, boolean, char

Resten är objekt, s.k. referenstyper

Java

JavaScript

JavaScripts version av

oinitialiserad är undefined

Låt oss testatypeof 10typeof 10.3typeof Math.LN2typeof Infinitytypeof NaN

typeof ”tutorial”typeof ””typeof (typeof 10)

JavaScript

Låt oss testatypeof 10typeof 10.3typeof Math.LN2typeof Infinitytypeof NaNtypeof Number(10)

typeof ”tutorial”typeof ””typeof (typeof 10)typeof String(”tutorial”)

JavaScript

Låt oss testatypeof undefinedtypeof tutorial

typeof {}typeof []typeof [4, 6, 2]typeof new Date()

JavaScript

Låt oss testatypeof undefinedtypeof tutorial

typeof {}typeof []typeof [4, 6, 2]typeof new Date()

typeof new Boolean(true)typeof new Number(10)typeof new String(”tutorial”)

JavaScript

Statisk vs dynamisk typning

Dynamisk typning:var name; Variabler har inga typername = ”John”; Värden har typername = 34; Variabler byter typ dynamiskt

Statisk typning:String name; Variabler har typername = ”John”; Värden har typername = 34; Variabler kan inte byta typ

Java

JavaScript

Dynamisk typning

var name = ”John”;

JavaScript

Värden har typerVariabler haringa typer

Stark vs svag typning

Stark vs svag typning

Stark !== braSvag !== dålig

Stark vs svag typning

var dynVar = 4; Mycket svag typningdynVar + ””; Implicit typkonverteringdynVar + null; Implicit typkonverteringdynVar + []; Implicit typkonverteringdynVar + [0]; Implicit typkonvertering

int intVar = 4; Ganska stark typningintVar + ””; Implicit typkonverteringintVar + 0.0; Implicit, breddande typkonv.intVar + (int)0.0; Explicit typkonverteringintVar + null; Inte tillåtet

Java

JavaScript

Typsäkert≈

inga typfel vid körning

Johns slutsats

Java är ett statiskt, starkt typat språk med hyfsat god typsäkerhet

JavaScript är ett dynamiskt, svagt typat språk med hyfsat god typsäkerhet

Objektorientering

Java: Klasser och objekt

Klass

Objekt

Objekt

Objekt

Klass Singleton-instans

KällkodExekverande

kod

JavaScript: Funktioner, objekt och prototyper

Prototyp

Funktion Funktion

KällkodExekverande

kod

Objekt

Objekt

Objekt

Prototyp

Funktion

Funktion

KällkodExekverande

kod

Objekt

Objekt

Objekt

Moduler

Anonyma closures

JavaScript: Funktioner, objekt och prototyper

Java: Utökning med arv

Subklass

Superklass

extends

Java: Subtypning med arv

Supertyp

Subtyp

extends

Javaklasser blir funktionsmässigt större

ochtypmässigt mindre

(mer specifika)via arv

I JavaScript så ärutökning

ochsubtypningskilda saker

JavaScript: Utökning via prototyp

JW = {}; log = console.log;

JW.Base = function(x, y) { this.x = x; this.y = y;};JW.Base.prototype.sum = function() { return this.x + this.y;}JW.instance = new JW.Base(1, 2);

log(JW.instance instanceof JW.Base);log(JW.instance.sum());

JavaScript

JavaScript: Utökning via prototyp

JW = {}; log = console.log;

JW.Base = function(x, y) { this.x = x; this.y = y;};JW.Base.prototype.sum = function() { return this.x + this.y;}JW.instance = new JW.Base(1, 2);

log(JW.instance instanceof JW.Base);log(JW.instance.sum());

JavaScript

JavaScript: Utökning via prototyp

JW = {}; log = console.log;

JW.Base = function(x, y) { this.x = x; this.y = y;};JW.Base.prototype.sum = function() { return this.x + this.y;}JW.instance = new JW.Base(1, 2);

log(JW.instance instanceof JW.Base);log(JW.instance.sum());

JavaScript

JavaScript: Utökning via prototyp

JW = {}; log = console.log;

JW.Base = function(x, y) { this.x = x; this.y = y;};JW.Base.prototype.sum = function() { return this.x + this.y;}JW.instance = new JW.Base(1, 2);

log(JW.instance instanceof JW.Base);log(JW.instance.sum());

JavaScript

JavaScript: Utökning via prototyp

JW = {}; log = console.log;

JW.Base = function(x, y) { this.x = x; this.y = y;};JW.Base.prototype.sum = function() { return this.x + this.y;}JW.instance = new JW.Base(1, 2);

log(JW.instance instanceof JW.Base);log(JW.instance.sum());

JavaScriptAnvändning av new innebär att:1) ett nytt objekt skapas med prototypen för JW.Base och att2) funktionen JW.Base() anropas med this pekandes på den nya instansen

JavaScript: Utökning via prototyp

JW = {}; log = console.log;

JW.Base = function(x, y) { this.x = x; this.y = y;};JW.Base.prototype.sum = function() { return this.x + this.y;}JW.instance = new JW.Base(1, 2);

log(JW.instance instanceof JW.Base);log(JW.instance.sum());

JavaScript

JavaScript: Utökning via prototyp

JW = {}; log = console.log;

JW.Base = function(x, y) { this.x = x; this.y = y;};JW.Base.prototype.sum = function() { return this.x + this.y;}JW.instance = new JW.Base(1, 2);

log(JW.instance instanceof JW.Base);log(JW.instance.sum());

JavaScript

JavaScript: Utökning via prototyp

JW = {}; log = console.log;

JW.Base = function(x, y) { this.x = x; this.y = y;};JW.instance = new JW.Base(1, 2);JW.Base.prototype.sum = function() { return this.x + this.y;}

log(JW.instance instanceof JW.Base);log(JW.instance.sum());

JavaScript

JavaScript: Utökning via prototyp

JW = {}; log = console.log;

JW.Base = function(x, y) { this.x = x; this.y = y;};JW.instance = new JW.Base(1, 2);JW.Base.prototype.sum = function() { return this.x + this.y;}

log(JW.instance instanceof JW.Base);log(JW.instance.sum());

JavaScript

Notera att vi inte har skapat någon ny subtyp.Vi har bara utökat den befintliga (proto-)typen.

JavaScript: SubtypningJW.Base = function(x, y) { this.x = x; this.y = y; };JW.Extension = function(x, y, z) { JW.Base.call(this, x, y); this.z = z;};JW.Extension.prototype = Object.create(JW.Base.prototype);JW.Extension.prototype.constructor = JW.Extension;JW.instance = new JW.Extension(1, 2, 3);log(JW.instance instanceof JW.Base);

JavaScript

JavaScript: SubtypningJW.Base = function(x, y) { this.x = x; this.y = y; };JW.Extension = function(x, y, z) { JW.Base.call(this, x, y); this.z = z;};JW.Extension.prototype = Object.create(JW.Base.prototype);JW.Extension.prototype.constructor = JW.Extension;JW.instance = new JW.Extension(1, 2, 3);log(JW.instance instanceof JW.Base);

JavaScript

Nu försöker vi ju bara programmera Javai JavaScript. Ni ser ju själva vilket elände det blir.Nåja, låt oss gå igenom det i alla fall.

JavaScript: SubtypningJW.Base = function(x, y) { this.x = x; this.y = y; };JW.Extension = function(x, y, z) { JW.Base.call(this, x, y); this.z = z;};JW.Extension.prototype = Object.create(JW.Base.prototype);JW.Extension.prototype.constructor = JW.Extension;JW.instance = new JW.Extension(1, 2, 3);log(JW.instance instanceof JW.Base);

JavaScript

JavaScript: SubtypningJW.Base = function(x, y) { this.x = x; this.y = y; };JW.Extension = function(x, y, z) { JW.Base.call(this, x, y); this.z = z;};JW.Extension.prototype = Object.create(JW.Base.prototype);JW.Extension.prototype.constructor = JW.Extension;JW.instance = new JW.Extension(1, 2, 3);log(JW.instance instanceof JW.Base);

JavaScript

JavaScript: SubtypningJW.Base = function(x, y) { this.x = x; this.y = y; };JW.Extension = function(x, y, z) { JW.Base.call(this, x, y); this.z = z;};JW.Extension.prototype = Object.create(JW.Base.prototype);JW.Extension.prototype.constructor = JW.Extension;JW.instance = new JW.Extension(1, 2, 3);log(JW.instance instanceof JW.Base);

JavaScript

JavaScript: SubtypningJW.Base = function(x, y) { this.x = x; this.y = y; };JW.Extension = function(x, y, z) { JW.Base.call(this, x, y); this.z = z;};JW.Extension.prototype = Object.create(JW.Base.prototype);JW.Extension.prototype.constructor = JW.Extension;JW.instance = new JW.Extension(1, 2, 3);log(JW.instance instanceof JW.Base);

JavaScript

JavaScript: SubtypningJW.Base = function(x, y) { this.x = x; this.y = y; };JW.Extension = function(x, y, z) { JW.Base.call(this, x, y); this.z = z;};JW.Extension.prototype = Object.create(JW.Base.prototype);JW.Extension.prototype.constructor = JW.Extension;JW.instance = new JW.Extension(1, 2, 3);log(JW.instance instanceof JW.Base);

JavaScript

Varför vill vi subtypa?Jo, för att vi tänker istatisk typning och

is-a-relationer

Statisk typning och subtypning

public void doPurchase(List<Item> items)

Java

Kan ta emot en lista av Itemseller subtyper till Item

Dynamisk typning

public void doPurchase(List<Item> items)

JavaScript

Dynamisk typning

public void doPurchase(List<Item> items)

JavaScript

Dynamisk typning

public void doPurchase(List<Item> items)

JavaScript

Dynamisk typning

public void doPurchase(List<Item> items)

JavaScript

Dynamisk typning

function doPurchase(items)

JavaScript

Kan ta emot vad som helst... eller ingenting

Dynamisk typning

function doPurchase(items)

JavaScript

Kan returnera vad som helst... eller ingenting

Vad är sant och falskt i JavaScript?

Truthiness is a ”truth” that a person claims to know intuitively ”from the gut” or because it ”feels right” without regard to evidence, logic, intellectual examination, or facts.

http://en.wikipedia.org/wiki/Truthiness

Låt oss testalog = console.log;if(!false) { log("false"); }if(!"false") { log("\"false\""); }if(!0) { log("0"); }if(!-1) { log("-1"); }if(!"") { log("\"\""); }if(!null) { log("null"); }if(!undefined) { log("undefined"); }if(![]) { log("[]"); }if(!{}) { log("{}"); }

JavaScript

Hur kan denna falskhet drabba oss?

Först ett par saker …

JW.obj = {drinks: "coffee"};JW.obj.drinks; => "coffee"JW.obj["drinks"]; => "coffee"

JavaScript

Objekts properties (≈medlemsvariabler) går att komma åt som array-referenser

Först ett par saker …

o = JW.obj;o.toString; // Finnso.hasOwnProperty(”drinks”) ?o.hasOwnProperty(”toString”) ?

JavaScript

Vad gör koden?JW = {};JW.apply=function(applyTo,applyWith){ for(var prop in applyWith) { if(applyWith.hasOwnProperty(prop) && !applyTo[prop]) { applyTo[prop] = applyWith[prop]; } }}JW.obj = {preselected: 3};JW.apply(JW.obj,{country: 'Sweden'});

JavaScript

Ser ni buggen?JW = {};JW.apply=function(applyTo,applyWith){ for(var prop in applyWith) { if(applyWith.hasOwnProperty(prop) && !applyTo[prop]) { applyTo[prop] = applyWith[prop]; } }}JW.obj = {preselected: 3};JW.apply(JW.obj,{country: 'Sweden'});

JavaScript

Ser ni buggen?JW = {};JW.apply=function(applyTo,applyWith){ for(var prop in applyWith) { if(applyWith.hasOwnProperty(prop) && !applyTo[prop]) { applyTo[prop] = applyWith[prop]; } }}JW.obj = {preselected: 0};JW.apply(JW.obj,{preselected: 5});

JavaScript

Hur startar man ett JavaScript-program?

JavaScript och webb<html><head> <script src=”js/main.js”></script> <script> var JW = {}; // Mer kod </script></head><body> <button onclick="document.getElementById('field2').value=document.getElementById('field1').value">Kopiera text</button> <div> Lite information </div> <script> JW.calcPrice = function(price, vat) { //… }; </script> <script src=”http://3rdparty.com/trackUsers.js”></script></body></html>

JavaScript och webb<html><head> <script src=”js/main.js”></script> <script> var JW = {}; // Mer kod </script></head><body> <button onclick="document.getElementById('field2').value=document.getElementById('field1').value">Kopiera text</button> <div> Lite information </div> <script> JW.calcPrice = function(price, vat) { //… }; </script> <script src=”http://3rdparty.com/trackUsers.js”></script></body></html>

Skript från fil i <head>

JavaScript och webb<html><head> <script src=”js/main.js”></script> <script> var JW = {}; // Mer kod </script></head><body> <button onclick="document.getElementById('field2').value=document.getElementById('field1').value">Kopiera text</button> <div> Lite information </div> <script> JW.calcPrice = function(price, vat) { //… }; </script> <script src=”http://3rdparty.com/trackUsers.js”></script></body></html>

Skript från fil i <head>

Inline:at skript i <head>

JavaScript och webb<html><head> <script src=”js/main.js”></script> <script> var JW = {}; // Mer kod </script></head><body> <button onclick="document.getElementById('field2').value=document.getElementById('field1').value">Kopiera text</button> <div> Lite information </div> <script> JW.calcPrice = function(price, vat) { //… }; </script> <script src=”http://3rdparty.com/trackUsers.js”></script></body></html>

Skript från fil i <head>

Skript, direkt i HTML

Inline:at skript i <head>

JavaScript och webb<html><head> <script src=”js/main.js”></script> <script> var JW = {}; // Mer kod </script></head><body> <button onclick="document.getElementById('field2').value=document.getElementById('field1').value">Kopiera text</button> <div> Lite information </div> <script> JW.calcPrice = function(price, vat) { //… }; </script> <script src=”http://3rdparty.com/trackUsers.js”></script></body></html>

Skript från fil i <head>

Skript, direkt i HTML

Inline:at skript i <head>

Inline:at skript i <body>

JavaScript och webb<html><head> <script src=”js/main.js”></script> <script> var JW = {}; // Mer kod </script></head><body> <button onclick="document.getElementById('field2').value=document.getElementById('field1').value">Kopiera text</button> <div> Lite information </div> <script> JW.calcPrice = function(price, vat) { //… }; </script> <script src=”http://3rdparty.com/trackUsers.js”></script></body></html>

Skript från fil i <head>

Skript, direkt i HTML

Inline:at skript i <head>

Skript från fil i <body>

Inline:at skript i <body>

Laddning och körning<html><head> <script src=”js/main.js”></script> <script> var JW = {}; // Mer kod </script></head><body> <button onclick="document.getElementById('field2').value=document.getElementById('field1').value">Kopiera text</button> <div> Lite information </div> <script> JW.calcPrice = function(price, vat) { //… }; </script> <script src=”http://3rdparty.com/trackUsers.js”></script></body></html>

Laddning och körning<html><head> <script src=”js/main.js”></script> <script> var JW = {}; // Mer kod </script></head><body> <button onclick="document.getElementById('field2').value=document.getElementById('field1').value">Kopiera text</button> <div> Lite information </div> <script> JW.calcPrice = function(price, vat) { //… }; </script> <script src=”http://3rdparty.com/trackUsers.js”></script></body></html>

Synkron laddning och körning

Laddning och körning<html><head> <script src=”js/main.js”></script> <script> var JW = {}; // Mer kod </script></head><body> <button onclick="document.getElementById('field2').value=document.getElementById('field1').value">Kopiera text</button> <div> Lite information </div> <script> JW.calcPrice = function(price, vat) { //… }; </script> <script src=”http://3rdparty.com/trackUsers.js”></script></body></html>

Körs synkront, dvs innan DOM:en har laddats

Synkron laddning och körning

Laddning och körning<html><head> <script src=”js/main.js”></script> <script> var JW = {}; // Mer kod </script></head><body> <button onclick="document.getElementById('field2').value=document.getElementById('field1').value">Kopiera text</button> <div> Lite information </div> <script> JW.calcPrice = function(price, vat) { //… }; </script> <script src=”http://3rdparty.com/trackUsers.js”></script></body></html>

Körs vid klick-eventet

Körs synkront, dvs innan DOM:en har laddats

Synkron laddning och körning

Laddning och körning<html><head> <script src=”js/main.js”></script> <script> var JW = {}; // Mer kod </script></head><body> <button onclick="document.getElementById('field2').value=document.getElementById('field1').value">Kopiera text</button> <div> Lite information </div> <script> JW.calcPrice = function(price, vat) { //… }; </script> <script src=”http://3rdparty.com/trackUsers.js”></script></body></html>

Körs när parsern kommit hit

Körs vid klick-eventet

Körs synkront, dvs innan DOM:en har laddats

Synkron laddning och körning

Laddning och körning<html><head> <script src=”js/main.js”></script> <script> var JW = {}; // Mer kod </script></head><body> <button onclick="document.getElementById('field2').value=document.getElementById('field1').value">Kopiera text</button> <div> Lite information </div> <script> JW.calcPrice = function(price, vat) { //… }; </script> <script src=”http://3rdparty.com/trackUsers.js”></script></body></html> Laddas och körs synkront

Körs när parsern kommit hit

Körs vid klick-eventet

Körs synkront, dvs innan DOM:en har laddats

Synkron laddning och körning

Asynkron laddning(function(){ var oldFirstScript = document.getElementsByTagName('script')[0]; newScript = document.createElement('script'),

newScript.async = true; newScript.src = 'js/script.js';

oldFirstScript.parentNode.insertBefore( newScript, oldFirstScript);}());

Synkron, blockerande laddning<script src=”js/script.js”></script>

Asynkron, icke-blockerande laddning<script defer src=”js/script.js”></script>

Asynkron, blockerande laddning<script async src=”js/script.js”></script>

HtmlLaddaKör

HtmlLaddaKör

HtmlLaddaKör

Synkron, blockerande laddning<script src=”js/script.js”></script>

Asynkron, icke-blockerande laddning<script defer src=”js/script.js”></script>

Asynkron, blockerande laddning<script async src=”js/script.js”></script>

HtmlLaddaKör

HtmlLaddaKör

HtmlLaddaKör

Synkron, blockerande laddning<script src=”js/script.js”></script>

Asynkron, icke-blockerande laddning<script defer src=”js/script.js”></script>

Asynkron, blockerande laddning<script async src=”js/script.js”></script>

HtmlLaddaKör

HtmlLaddaKör

HtmlLaddaKör

OK, men hur startar vi?

In the Beginning

(function(){}());

window.someGlobalFunction();

public static void main(String[] args)

Java

JavaScript

Självinvokerande funktioner

Anonym, självinvokerande med parameter(function myFunc(me) { // Kod}('John'));

Anonym, självinvokerande utan parameter(function myFunc() { // Kod}());

Självinvokerande funktioner

Anonym, namnlös och självinvokerande(function() { // Kod}());

Namngiven, självinvokerande, icke-anropsbarvar myFunc = function(me) { // Kod}('John');

Variabler

Scopevar GLOB = {};

GLOB.func = function(array) { var local = …; … for (var i=0; i<array.length; i++){ … var temp = array[i]; }};

JavaScript

Scopevar GLOB = {};

GLOB.func = function(array) { var local = …, i, temp; … for (i=0; i<array.length; i++){ … temp = array[i]; }};

JavaScript

Scopevar GLOB = {};

GLOB.func = function(array) { var local = …, i, temp; … for (i=0; i<array.length; i++){ … temp = array[i]; }};

JavaScript

Det finns bara två variabel-scope:Globalt eller lokalt funktions-scope.

Alla lokala variabler ”hissas” tilltoppen av funktionen (hoisting).

Statiska variabler?Medlemsvariabler?

JW.Personnummer = function (personnummerStr) { this.personnummer = personnummerStr; var checksum = personnummerStr.substring(11,12); };JW.Personnummer.prototype.regexp = /[0-9]+/;JW.Personnummer.OLDEST_DATE = new Date(1880, 0, 1);JW.obj = new JW.Personnummer("191212121212");JW.obj.personnummer;JW.obj.checksum;JW.obj.regexp;JW.Personnummer.OLDEST_DATE

JavaScript

JW.Personnummer = function (personnummerStr) { this.personnummer = personnummerStr; var checksum = personnummerStr.substring(11,12); };JW.Personnummer.prototype.regexp = /[0-9]+/;JW.Personnummer.OLDEST_DATE = new Date(1880, 0, 1);JW.obj = new JW.Personnummer("191212121212");JW.obj.personnummer;JW.obj.checksum;JW.obj.regexp;JW.Personnummer.OLDEST_DATE

JavaScript

JW.Personnummer = function (personnummerStr) { this.personnummer = personnummerStr; var checksum = personnummerStr.substring(11,12); };JW.Personnummer.prototype.regexp = /[0-9]+/;JW.Personnummer.OLDEST_DATE = new Date(1880, 0, 1);JW.obj = new JW.Personnummer("191212121212");JW.obj.personnummer;JW.obj.checksum;JW.obj.regexp;JW.Personnummer.OLDEST_DATE

JavaScript

JW.Personnummer = function (personnummerStr) { this.personnummer = personnummerStr; var checksum = personnummerStr.substring(11,12); };JW.Personnummer.prototype.regexp = /[0-9]+/;JW.Personnummer.OLDEST_DATE = new Date(1880, 0, 1);JW.obj = new JW.Personnummer("191212121212");JW.obj.personnummer;JW.obj.checksum;JW.obj.regexp;JW.Personnummer.OLDEST_DATE

JavaScript

JW.Personnummer = function (personnummerStr) { this.personnummer = personnummerStr; var checksum = personnummerStr.substring(11,12); };JW.Personnummer.prototype.regexp = /[0-9]+/;JW.Personnummer.OLDEST_DATE = new Date(1880, 0, 1);JW.obj = new JW.Personnummer("191212121212");JW.obj.personnummer;JW.obj.checksum;JW.obj.regexp;JW.Personnummer.OLDEST_DATE;

JavaScript

Closure (hölje)(function(array) { var i, temp; … for (i=0; i<array.length; i++){ … temp = array[i]; }}([1, 2, 3]));

JavaScript

Closure (hölje)(function(array) { var i, temp; … for (i=0; i<array.length; i++){ … temp = array[i]; }}([1, 2, 3]));

JavaScript

Ett closure håller sitt scope och sinkontext under hela sin livslängd.

En referens som kommit innanförhöljet (array i exemplet) lever alltsåkvar och håller sitt värde inom”höljet”.

Closure-exempelfor(i=0; i<pages.length; i++) { var page = pages[i]; pageElement.load(page.url, function() { launchForm(page.form); });}

JavaScript

Closure-exempelfor(i=0; i<pages.length; i++) { var page = pages[i]; pageElement.load(page.url, function() { launchForm(page.form); });}

JavaScript

Callback-funktion eftersompageElement.load()är asynkron

Closure-exempelfor(i=0; i<pages.length; i++) { var page = pages[i]; pageElement.load(page.url, function() { launchForm(page.form); });}

JavaScript

page hissas och får alltså ett nytt värde för varje iteration

Closure-exempelvar page;for(i=0; i<pages.length; i++) { page = pages[i]; pageElement.load(page.url, function() { launchForm(page.form); });}

JavaScript

page hissas och får alltså ett nytt värde för varje iteration

Closure-exempelvar page;for(i=0; i<pages.length; i++) { page = pages[i]; pageElement.load(page.url, function() { launchForm(page.form); });}

JavaScript

När väl callbacken anropas så kommer page referera till pages[pages.length-1] eller åtminstone ett annat element än det var tänkt.

Closure-exempelvar page;for(i=0; i<pages.length; i++) { page = pages[i]; pageElement.load(page.url, function() { launchForm(page.form); });}

JavaScript

Closure-exempelvar page;for(i=0; i<pages.length; i++) { page = pages[i]; (function(page) { pageElement.load(page.url, function() { launchForm(page.form); } }); })(page);}

JavaScript

Closure-exempelvar page;for(i=0; i<pages.length; i++) { page = pages[i]; (function(page) { pageElement.load(page.url, function() { launchForm(page.form); } }); })(page);}

JavaScript

Vi binder page till ett nytt höljeså att callbacken refererar rätt

Closure-exempelvar page;for(i=0; i<pages.length; i++) { page = pages[i]; (function(myPage) { pageElement.load(myPage.url, function() { launchForm(myPage.form); } }); })(page);}

JavaScript

Om man mår dåligt av såntså kan man döpa om höljetsvariabel :)

Crockford-moduleneller

Module Pattern

Med självinvokerande funktioner,scope och closures är vi redo för

det viktigaste designmönstret:

JW.cache = (function(){}());

JavaScript

JW.cache = (function(){ return {};}());

JavaScript

JW.cache = (function(){ return { getPatient: function(personnummer) {

} };}());

JavaScript

JW.cache = (function(){ var cache = {}; return { getPatient: function(personnummer) {

} };}());

JavaScript

JW.cache = (function(){ var cache = {}; return { getPatient: function(personnummer) { var res=cache[personnummer]; // Check if fresh // Return } };}());

JavaScript

JW.cache = (function(){ var cache = {}; return { getPatient: function(personnummer) { var res=cache[personnummer]; if(_isValid(res)) { return res.patient; } else { // Handle cache miss } } };}());

JavaScript

JW.cache = (function(){ var cache = {}, _isValid = function(res) {

} return { getPatient: function(personnummer) { var res=cache[personnummer]; if(_isValid(res)) { return res.patient; } else { // Handle cache miss } }

JavaScript

JW.cache = (function(){ var cache = {}, _isValid(res) { return !res ? false : (Date.now() - res.timestamp) <= 60000; // One minute } return { getPatient: function(personnummer) { var res=cache[personnummer]; if(_isValid(res)) { return res.patient; } else { // Handle cache miss

JavaScript

JW.cache = (function(){ var cache = {}, _isValid(res) { return !res ? false : (Date.now() - res.timestamp) <= 60000; // One minute } return { getPatient: function(personnummer) { var res=cache[personnummer]; if(_isValid(res)) { return res.patient; } else { // Handle cache miss

JavaScript

Vi kan alltså ha (closure-)privatavariabler och funktioner

Crockford = (function(initParam) { var privVar1, privVar2, _privFunc1 = function() {

}, _privFunc2 = function() {

}; return { pubVar1: ”value”, pubFunc1: function() {

} };}(initParam));

JavaScript

RevealingModule Pattern

För att möjliggöra enhetstesteranvänder man någon form av

Crockford = (function(initParam) { var _privFunc = function() {

}; return { unitTestHandle: { privFunc: _privFunc } };}(initParam));

JavaScript

Crockford = (function(initParam) { var _privFunc = function() {

}; return { unitTestHandle: { privFunc: function() { log(”Only use in unit test”); return _privFunc(); } } };}(initParam));

JavaScript

Namngivna parametrar

Metodsignaturer

function execPurchase(price, item, discountCode, wantsSpam, user)

public void execPurchase(int, String, String, boolean, User)

Java

JavaScript

Metodsignaturer

function execPurchase(purchase) { purchase.price … purchase.item … purchase.wantsSpam … purchase.user …

builder = new Purchase.Builder();Purchase purchase = builder.setPrice(9900) .setItem(”keps”).wantsSpam(false) .setUser(user).build();

Java

JavaScript

Vad innebär ett JavaScript-bygge?

JavaScript-bygge

• Hantering av ramverk och bibliotek

• Beroendehantering egna filer/moduler/klasser

• Enhetstester och statisk analys

• Konkatenering och minifiering

• final.html

Hantering av ramverk

• Vill ofta cache:a tredjepartsbibliotek hårt

• Ibland hotlink:a till CDN eller dylikt

• Därför sällan slå ihop med egen kod

• Vill sällan ha i sitt eget repo

Beroendehantering egen kod

• AMD – Asynchronous Module Definition

• Bundlat i större ramverk såsom YUI och Ext JS

• Fristående require.js m.fl.

Enhetstester ochstatisk analys

• Enhetstester med Jasmine, JSTestDriver etc.

• phantom.js – webbläsare utan GUI

• sinon.js – mockramverk

• Statisk analys med JSHint eller JSLint

Konkatenering och minifiering

• Google Closure Compiler

• YUI Compressor

• uglify.js

• require.js utför konkatenering och minifiering med Closure Compiler och uglify.js

Utan beroendehantering<head> <link rel="stylesheet" type="text/css" href="css/main.css"> <script src="js/lib/jquery-1.7.1.min.js"></script> <script src="js/lib/jquery-encoder-0.1.0.js"></script> <script src="js/base.js"></script> <script src="js/JW/util/util.js"></script> <script src="js/JW/personnummer/Personnummer.js"></script> <script src="js/JW/patient/Patient.js"></script> <script src="js/JW/cache/cache.js"></script> <script src="js/JW/proxy/proxy.js"></script> <script src="js/JW/gui/gui.js"></script></head>

HTML

Med beroendehantering<head> <link rel="stylesheet" type="text/css" href="css/main.css"> <script data-main="js/base" src="js/lib/require-1.0.5/require.js"></script></head>

HTML

java -classpath /path/to/rhino/js.jar:/path/to/Closure_Compiler/compiler.jar org.mozilla.javascript.tools.shell.Main ../../r.js -o name=base out=min.js baseUrl=. paths.jquery=empty: paths.jquery-encoder=empty:

final.html<head> <link rel="stylesheet" type="text/css" href="css/main.css"> <script src="js/lib/require-1.0.5/require.js"></script> <script> require.config({ paths: { "base": "js/min", "jquery": "js/lib/jquery-1.7.1.min", "jquery-encoder": "js/lib/jquery-encoder-0.1.0" } }); require(["base"]); </script></head>

HTML

Några extras

Strict Mode (ES5)

• Inled JavaScript-fil eller funktions-scope med ”use strict”;

• Omöjliggör ofrivilliga globala variabler

• Inget läckage av globala objektet till this

• Omöjliggör duplikat i objekt à la{ item: ”keps”, item: ”boll”}

• Ingen skuggning via eval(”var x;”)

seal (ES5)JW = {name: "John"};

JW.name = "Joe";JW.job = "coder";

console.log(JW.name + ", " + JW.job);

JavaScript

seal (ES5)JW = {name: "John"};

Object.seal(JW);

JW.name = "Joe";JW.job = "coder";

console.log(JW.name + ", " + JW.job);

JavaScript

freeze (ES5)JW = {name: "John"};

Object.freeze(JW);

JW.name = "Joe";JW.job = "coder";

console.log(JW.name + ", " + JW.job);

JavaScript

freeze (ES5)JW = {name: "John"};

Object.freeze(JW);

JW.name = "Joe";JW.job = "coder";

console.log(JW.name + ", " + JW.job);

JavaScript

Men … obj.prototype kanfortfarande ändra på förseglade och frysta objekt. Jag har testat att frysa prototypenoch det verkar fungera men det måste förstås analyseras för sidoeffekter.

argumentsJW = function() { console.log(arguments.length); for(var i=0; i<arguments.length; i++) { console.log(arguments[i]); } return "done";}

JW(1, "Joe", [2, 3, 4]);

JavaScript

jsfiddle.net

Att följahttp://javascriptweekly.com/

@javascript_news@BrendanEich @littlecalculist@addy_osmani @addyosmani

@paul_irish @badass_js@rwaldron @slicknet

@kangax @unscriptable@sthlmjs