Upload
giovanni-scerra
View
53
Download
5
Embed Size (px)
Citation preview
love.apply(i,[you]);
undefined
SayitwithJavaScript!
Topics:
� Objects & C.
� Functions & C.
� Closure
� Inheritance
� Namespaces & Temp Scope
� Module Pattern
� Dispatching
� Putting it all together
� Unit Testing
Objects & C.
//Creating objects var myObj1 = {}; var myObj2 = new Object(); //Creating arrays
var myArr1 = []; var myArr2 = new Array(); var myArr3 = [1, "a"]; myArr3.length == 2 //true //Exploiting objects typeof myObj1 //"object" myObj1 instanceof Object //true myObj1.constructor //function Object() { [native code] } //Exploiting arrays typeof myArr1 //"object" myArr1 instanceof Array //true (within same frame) myArr1 instanceof Object //true myArr1.constructor //function Array() { [native code] }
//Adding properties myObj1.x = 5; myObj2["x"] = 5; myObj2.double = function(x) { return x*2; } myObj1["x"] == myObj2.x //true myObj2.x //5 myArr1.x = "test"; myArr1["y"] = "test"; myArr1.y == myArr1["x"] //true
//Test var testArr = []; testArr["x"] = "element x"; testArr["y"] = "element 0"; testArr.length //??? //Literals var person = { name: "giovanni", age: 41, rejuvenate: function() { this.age--; } } //The proto chain var developer = { salary: 999999 } //Using Object.create var developer = Object.create(person); developer.salary = 999999; developer.name //"giovanni" developer.__proto__ == person; //true
//Test person.age = 10; developer.age //???
Functions&C.
//Defining functions
function sayHello1() { console.log("Hi there!"); } var sayHello2 = function() { console.log("Hi there!"); } //Test
sayHello1.name //?? sayHello2.name //?? typeof sayHello1 //function sayHello1 instanceof Object //true sayHello1.constructor //function Function() {…} var fn = new Function("","console.log('Hi there!')"); //Calling function
var sayHelloTo = function sayHelloTo(name) { return this.greeting + " " + name; } sayHelloTo("Giovanni"); //"undefined Giovanni" window.greeting = "Good morning" sayHelloTo("Giovanni"); //"Good morning Giovanni"
//Calling functions within a context var evening = { greeting: "Good evening"}; //"Good evening Giovanni" sayHelloTo.apply(evening, ["Giovanni"]); sayHelloTo.call(evening, "Giovanni"); var urbanGreeter = { greeting: "Sup", greet: sayHelloTo } urbanGreeter.greet("Giovanni") //"Sup Giovanni" //Test
//What is this function doing? function bind(fnContext, fn) { return function() { return fn.apply(fnContext, arguments); } } //Test
function reverseName() { return this.name.split("").reverse().join(""); } var r = bind({name: "giovanni"}, reverseName); r(); //??
Closures
//Defining closures
function counter(startAt){ var count = startAt; return function(){ console.log(++count) }; } var next = counter(0); next(); //1 next(); //2 next(); //3 //Closures with input parameters
var quo = function (status) { return { get_status: function ( ) { return status; } };
}; var myQuo = quo("active"); myQuo.get_status(); //"active" //Test var handlers = []; for (var i = 0; i < 10; i += 1) { handlers.push( function() { return i; } ); }; handlers[2](); //??? handlers[6](); //???
//Test var handlers = []; for (var i = 0; i < 10; i += 1) { handlers.push( function(i) { return function() { return i;}; }(i) ); };
var index = handlers.length; while(index) console.log(handlers[--index]()); //???
//Using closures for encapsulation
function buildProduct() { var productID = 999; return { getID: function () { return productID; }, setID: function (theNewID) { productID = theNewID; } } } var product = buildProduct(); product.getID(); // 999 product.setID(478); product.getID(); // 478
//Test var shortCut = product.getID; shortCut(); //???
Inheritance
//Inheritance with the ‘new’ operator
//constructor function Person(name, age){ this.name = name; this.age = age; } Person.prototype.getName = function(){ return this.name; }; Person.prototype.getAge = function(){ return this.age; }; Person.prototype.title = "Mr."; var badPerson = Person("Max",30); badPerson == undefined; //true window.name == "Max"; //true var goodPerson1 = new Person("Giovanni",41); var goodPerson2 = new Person("John",30); goodPerson1.getName() == "Giovanni"; //true goodPerson2.getAge() == "30"; //true goodPerson2.title = "Mr."; //true //Test goodPerson2.age = 31; goodPerson2.title = "Dr."; console.log(goodPerson1.age); //?? console.log(goodPerson1.title); //??
//inheritance chains
function Worker(name, age, profession, salary) { Person.call(this,name,age); this.profession = profession; this.salary = salary; } Worker.prototype = new Person(); //Added for legacy code compatibility Worker.prototype.constructor = Worker; Worker.prototype.fire = function() { this.profession = "unemployed"; } Worker.prototype.giveBigRaise = function() { this.salary += 10000; } var w1 = new Worker("Giovanni", 41, "developer", 0); var w2 = new Worker("John", 43, "developer", 0); //Test w1.giveBigRaise(); w1.salary; //?? w2.salary; //?? w1.getAge(); //?? w1.title == "Mr."; //?? w1 instance of Worker; //?? W2 instance of Worker; //?? w1 instance of Person; //?? w1 instance of Object; //??
//Extending native types – forEach polyfill
if (!Array.prototype.forEach) { Array.prototype.forEach = function (fn, scope) { for (var i = 0, l = this.length; i < l; ++i) { if (i in this) { fn.call(scope, this[i], i, this); } } }; } ['a','b','c'].forEach( function(elm, index, ctx) { console.log(index +'. ' + elm); }, window );
//Inheritance with Object.create
var myMammal = { name : 'Herb the Mammal', get_name : function ( ) { return this.name; }, says : function ( ) { return this.saying || ''; }
}; var myCat1 = createCat('Henrietta'); var myCat2 = createCat('Rocky'); function createCat(name) { var obj = Object.create(myMammal);
obj.name = name; obj.saying = 'meow'; obj.purr = function (n) { return 'r-r-r-r-r'; }; obj.get_name = function ( ) { return this.says( ) + ' ' + this.name + ' '; }; return obj;
} myCat2.get_name(); //?? myMammal.bloodTemperature = 'warm’; myCat1.bloodTemperature; //??
//Functional Inheritance
function base(spec) { var that = {}; that.name = spec.name; return that; // Return the object } function child(spec) { var that = base(spec); that.sayHello = function() { return 'Hello, I\'m ' + that.name; }; return that; // Return it } // Usage var object = child({ name: 'a functional object' }); console.log(object.sayHello());
//Mixins
var asButton = function() { this.hover = function() { console.log('hover handler'); }; this.press = function() { console.log('press handler'); }; this.fire = function() { return this.action(); }; return this;
}; var asCircle = function() {
this.area = function() { return Math.PI * this.radius * this.radius; }; this.grow = function() { this.radius++; }; this.shrink = function() { this.radius--; }; return this;
}; var RoundButton = function(radius, label, action) { this.radius = radius; this.label = label; this.action = action; }; asButton.call(RoundButton.prototype); asCircle.call(RoundButton.prototype); var button1 = new RoundButton(4, 'yes!', function() {return 'you said yes!';} ); button1.fire(); //'you said yes!'
Namespaces&Temp.Scope
//Creating namespaces manually
var afsi = afsi || {}; afsi.utilities = myApp.utilities || {}; afsi.utilities.sayHello = function(name) { return "Hi " + name; } //A namespace utility
var afsi = afsi || {}; afsi.namespace = function namespace(namespaceString) { var parts = namespaceString.split('.'), parent = window, currentPart = ''; for(var i = 0, length = parts.length; i < length; i++) { currentPart = parts[i]; parent[currentPart] = parent[currentPart] || {}; parent = parent[currentPart]; } } //Defining and using a namespace
afsi.namespace("afsi.utilities"); var afsi.utilities.sayHello = function(name) { return "Hi " + name; }
//‘Wrapping’ techniques
(function(){ var myLib = window.myLib = function(){ // Initialize }; // ... })(); --------------------------------------------- var myLib = (function(){ function myLib(){ // Initialize } // ... return myLib; })(); --------------------------------------------- (function( window, document, NS, undefined ){ NS.variableName = "string value"; NS.methodName = function() {}; // ... }(window, window.document, (window.NS = window.NS || {}) ) ); --------------------------------------------- function MyScript(){} (function() { var THIS = this; function defined(x) { alert(THIS); //points to MyScript() } // ... }).apply(MyScript);
ModulePattern–(singleton,revealing)
var afsi = afsi || {}; var afsi.calculator = (function () { var _m = 0; function _add(n1,n2) { return n1+n2; } function _substract(n1,n2) { return n1-n2; } function _setM(m) { return _m = m; } function _getM() { return _m; } return { add: _add, substract: _substract, setM: _setM, getM: _getM }; }());
ModulePattern–constructor(OOP)
var afsi = afsi || {}; afsi.MyClass = (function(){ var _privateStaticVar = "I am a static private variable"; var _privateFunction = function() { return "I am a private function"; }; var Constructor = function(options) { this.instanceVar = "I am an public instance variable"; this.setPrivateStaticVar = function(x) { _privateStaticVar = x; }; this.getPrivateStaticVar = function() { return _privateStaticVar; }; this.exposePrivateFunction = _privateFunction; }; Constructor.prototype = { constructor: afsi.MyClass, name: "afsi.MyClass", version: "1.0" }; return Constructor; }()); var v1 = new afsi.MyClass(options); var v2 = new afsi.MyClass(options);
ModulePattern–factory(functional,revealing)
var afsi = afsi || {}; afsi.calculator = (function(){ var _add = function (n1, n2) { return n1+n2; }; var _substract = function (n1, n2) { return n1-n2; }; var _calculatorFactory = function () { var _m = 0;
var _setM = function (m) { _m = m; }; var _getM = function () { return _m; };
return {
add: _add, substract: _substract, setM: _setM, getM: _getM
}; }; return _calculatorFactory; }()); var calc = afsi.calculator();
Dispatching(Publisher/Subscriber)
var afsi = afsi || {}; afsi.messenger = (function () { var _messengerFactory = function() {
var _messages = []; var _subscribeMessage = function (messageName, callBack) { if (typeof messageName === "string" && typeof callBack === "function") { if (typeof _messages[messageName] === "undefined") { _messages[messageName] = []; } _messages[messageName].push(callBack); } }; var _sendMessage = function (messageName, message) { if (typeof messageName === "string" && typeof _messages[messageName] !== "undefined") { for (var _counter = 0, max = _messages[messageName].length; _counter < max; _counter++) { _messages[messageName][_counter](message); } } }; return { subscribeMessage: _subscribeMessage, sendMessage: _sendMessage };
} return _messengerFactory; } ());
Puttingitalltogether
//Controller
afsi.namespace("afsi.myPage.controller"); afsi.myPage.controller = (function () { var _processSearchData (searchData) { //process the search data return searchData; } var _ctrFactory = function (model,view, messenger) { messenger.subscribeMessage( "myPage.search_onkeypress", function (text) { model.searchProducts(text); } ); messenger.subscribeMessage( "myPage.search_result", function (searchData) { searchData = _processSearchData(searchData); view.showSearchResult(searchData); } ); }; return _ctrFactory; } ());
//View
afsi.namespace("afsi.myPage.view"); afsi.myPage.view = (function () { var _showSearchResult = function(searchData){ //bind UI with searchData … } var _viewFactory = function (messenger) { $("#searchInput").on("keypress", {}, function (event) { messenger.sendMessage( "myPage.search_onkeypress", this.value ); } ); return { showSearchResult: _showSearchResult }; } return _viewFactory } ());
//Model
afsi.namespace("afsi.myPage.model"); afsi.myPage.model = (function () { var _modelFactory = function (messenger) { var _searchProducts = function(text){ $.ajax({ url: "/api/Products/SearchProducts?q="+text, type: 'GET', cache: false, success: function (result) { messenger.sendMessage( "myPage.search_result", Result ); }, error: function (result) { alert("Some error message") } }); } return { searchProducts: _searchProducts }; } return _modelFactory; } ());
//Page initialization
$(function () { var messenger = afsi.messenger(); afsi.myPage.controller( afsi.myPage.model(messenger), afsi.myPage.view(messenger), messenger ); });
UnitTesting(QUnit)
(function() { module('Dispatcher Test'); test( "Subscriber should be called and receceive the message", function() { //expected assertion #
expect(3); //reate instance
var m = afsi.messenger(); //set up m.subscribeMessage("afsi.test", function(message) { //assertions
ok( true, "function called" ); ok( typeof message !== 'undefined', "message is defined" ); ok( message.data === 'something', "data has the correct value" ); }); //stimulus m.sendMessage("afsi.test", { data: 'something' }) }); … }());
confirm("Questions?")
References
Eloquent JavaScript
http://eloquentjavascript.net/index.html
Learning Advanced Javascript http://ejohn.org/apps/learn/
Google Talks: How JavaScript Works http://www.youtube.com/watch?v=ljNi8nS5TtQ Prototypes and Inheritance in JavaScript http://msdn.microsoft.com/en-us/magazine/ff852808.aspx
Functional inheritance vs. prototypal inheritance
http://www.richard-foy.fr/blog/2011/10/30/functional-inheritance-vs-prototypal-inheritance/
Namespacing in JavaScript http://javascriptweblog.wordpress.com/2010/12/07/namespacing-in-javascript/
JavaScript Module Pattern: In-Depth
http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html
ECMAScript 5 Strict Mode, JSON, and More
http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/#!
QUnit: Introduction to Unit Testing
http://qunitjs.com/intro/
alert("Thank You!");