Loading…

Flash Player 9 (or above) is needed to view presentations.
We have detected that you do not have it on your computer. To install it, go here.

Like this presentation? Why not share!

Like this? Share it with your network

Share
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
    Be the first to like this
No Downloads

Views

Total Views
1,191
On Slideshare
1,191
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
9
Comments
0
Likes
0

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. The Mighty JS Function Timothee Groleau - 2010-11-04
  • 2. Who I am ●Timothee (Tim) Groleau ●EcmaScript coder for 10+ years (AS1, AS2, JS) ●Currently working at mig33 ( http://mig33.com ) Disclaimer / warnings ●I hope you like black and white ●Spaghetti presentation ●Examples purposely simplistic/stupid to illustrate features ●I have not looked at a real JS VM implementation ●we'll start reaaaaallly simple, then move on to interesting stuff ●coding style not consistent (I had to save spaces in these slides) ●I didn't follow Sebastiaan's advice of splitting content, (and then I totally regretted it, but it was too late :( )
  • 3. function factorial (n) { var fact = 1; while (n > 1) fact *= n--; return fact; } x = 5; res = factorial(x); alert(x); // 5 alert(res); // 120 // alert(n); // error n is not defined // alert(fact); // error fact is not defined What do we have here? ●Function declaration ●Function invocation ●Function arguments and parameters passing (pass by value) ●Local parameters ●Memory reclamation / garbage collection
  • 4. function average () { if (arguments.length <= 0) return 0; var total = 0; for (var i=0; i<arguments.length; i++) { total += arguments[i]; } return total / arguments.length; } a = average(3, 5, 7, 9, 11, 8, 2); alert(a); // 6.428571428571429 What do we have here? ●undeclared arguments ●variable length argument list ●arguments array (!!beware!! FAKE array)
  • 5. var foo = {x:5}, bar = {x:6}; function whatsMyX() { return this.x; } foo.getX = whatsMyX; bar.getX = whatsMyX; alert( foo.getX() ); // 5 alert( bar.getX() ); // 6 alert( foo.getX === bar.getX ); // true What do we have here? ●function references can be assigned, just like any variable ●function call operator (), can be used on any valid function reference ●'this' in a function body is dynamically resolved, and resolves to the object the function is called from
  • 6. DOMAIN = 'http://www.mig33.com'; function getURL(path) { return DOMAIN + path; } url = getURL('/about-us/jobs'); alert(url); What do we have here? ●global variable access inside a function ●scope chain traversal
  • 7. function getModuloFunc(mod) { return function(n) { return n % mod; } } mod2 = getModuloFunc(2); mod3 = getModuloFunc(3); alert( [mod2(1), mod2(2), mod2(3), mod2(4), mod2(5)] ); alert( [mod3(1), mod3(2), mod3(3), mod3(4), mod3(5)] ); What do we have here? ●a function is being returned from another function ●the inner function is persisted ●when the inner function runs, it has access to the local variables of the outer function ===> closures!
  • 8. var x = 5; function setup(obj) { var x; obj.setIt = function(a, b) { x = a; y = b; } obj.getIt = function() { return [x, y]; } } o = {}; setup(o); o.setIt(6, 7); alert(o.getIt()); // [6, 7] alert( [x, y] ); // [5, 7] What do we have here? ●2 inner functions are persisted in a global object ●they both have access to the same outer function's scope ●assignment without explicit scope is done to first scope that declares variable, or to global scope (e.g. 'y' ends up in the global scope)
  • 9. Concept slide 1 – function declaration function foo() {} ●named function ●can be forward referenced foo = function() {} ●anonymous function ●assigned to variable foo ●makes it obvious that the function can be treated like any variable In both cases, foo is now a reference to a function object, aka a function reference
  • 10. Concept slide 2 – functions are first class objects They can be assigned/accessed like any other variable function foo(){}; var bar = foo; bar(); var o = {}; o.bla = foo; o.bla(); As a consequence, they can be passed as function parameters, and be return values from functions function foo() { alert('hello'); }; function bar1(func) { func(); } bar1(foo); function bar2() { return foo; }; bar2()(); Functions can hold data function foo(){} bar = foo; bar.aProperty = 'hello'; alert(foo.aProperty); Functions are instances of the Function class (more on that later... maybe)
  • 11. Concept slide 3 – function call operator: () The function call operator can be used on anything that evaluate to a function reference. var foo = function() {alert('hello');}; foo(); var bar = foo; bar(); var o = {method:foo}; o.method(); var name = 'method'; o[name](); (function() {alert('hello');})() var bar2 = function(){ return foo; }; bar2()();
  • 12. Concept slide 4 – Scopes in javascript No block scope in JS: var x = 5; { var x = 6; } alert(x); // ? Functions provide scopes var x = 5; function foo() { var x; x = 6; alert(x); } foo(); // 6 alert(x); // 5
  • 13. Concept slide 5 – Scope chain / Lexical scoping Javascript is lexically scoped (aka: just read it, it makes sense) var x = 5; function foo() { alert(x); } function bar() { var x=6; foo(); } bar(); // ? In other words, the scope chain of a function is determined at function creation, NOT at function invocation The scope chain is the sequence of objects inspected for variable resolution. Think of it as a linked list.
  • 14. Concept slide 6 – Closures / Scope chain / Memory considerations I function foo() { var someLargeString = 'bla'; var someOtherLargeString = 'bli'; var theNumberIneed = 5; return function(n) { return n + theNumberIneed; } } bar = foo(); alert( bar(5) ); // 10 What happened to someLargeString and someOtherLargeString? They are in memory for as long as a reference to the inner function exists, but they can never be reached.
  • 15. Concept slide 7 – Closures / Scope chain / Memory considerations II Want proof? function foo() { var someLargeString = 'bla'; var someOtherLargeString = 'bli'; var theNumberIneed = 5; return function(name) { return eval(name); } } getFromScope = foo(); alert( getFromScope('someLargeString') ); // bla alert( getFromScope('someOtherLargeString') ); // bli alert( getFromScope('theNumberIneed') ); // 5
  • 16. Concept slide 8 – Closures / Scope chain / Memory considerations III var global_var = 2; function f(arg) { var local_var_f = arg; alert([local_var_f, global_var]); return function(arg) { alert([ arg, local_var_f, global_var ]); } } g1 = f(3); //3,2 g1(4); //4,3,2 g1(5); //5,3,2 g2 = f(6); //6,2 g2(7); //7,6,2 g2(8); //8,6,2
  • 17. Concept slide 9 – Closures / Scope chain / Memory considerations IV Let's take a look at what's really happening (boo-ring... sorry) 1) At function creation time, a 'hidden' reference is stored in the function to point to the CURRENT scope object, this basically becomes the start of the scope chain. 2) At function invocation a) a new object is created to become the current scope b) all local variables, function parameters, and arguments array are stored in that object (i.e. 'var' is a keyword to add new members to the scope object) c) the new object gets the hidden link to point to the function's scope chain d) when end of the function is reached, the scope object gets destroyed, all local variables it carries are removed (yeah, garbage collection!) 3) IF an inner function is persisted (returned or assigned to a global object), then the 'hidden link' to the current scope is also persisted, and since ref count is not zero, the scope object (and ALL its local variables) are NOT garbage collected
  • 18. Concept slide 10 – to closure or not to closure? I function setup(obj) { var someVar = 'bla'; obj.count = 0; obj.aMethod = function() { this.count++; } } o1 = {}; setup(o1); o2 = {}; setup(o2); Good or bad? alert( o1.aMethod === o2.aMethod ); // false
  • 19. Concept slide 11 – to closure or not to closure? II function _method() { this.count++; } function setup(obj) { var someVar = 'bla'; obj.count = 0; obj.aMethod = _method; } o1 = {}; setup(o1); o2 = {}; setup(o2); alert( o1.aMethod === o2.aMethod ); // true
  • 20. Putting it all together – private scopes (function(){ var DOMAIN = 'http://www.mig33.com'; getURL = function(path) { return DOMAIN + path; } })(); url = getURL('/about-us/jobs'); alert(url);
  • 21. Putting it all together – Classes with private statics (function(){ // private static var prefix = 'M33_WIDGET_'; var count = 0; var domain = 'http://www.mig33.com'; MyClass = function() { this.id = prefix + (++count); }; o = MyClass.prototype; o.method1 = function(path) { /* … */ }; })(); m = new MyClass(); alert( m.id );
  • 22. Putting it all together – Instance privates (CrockFord style) function Foo(blarg) { var self = this; // Capture self-ref in closure this.datum = blarg; var private_data = "whatever"; // Captured in closure this.standard_method = function() { return this.datum; }; function private_method() { self.datum += 1; // Accesses member via closure return private_data; // Accesses closure } this.privileged_method = function() { private_data += "!"; // Accesses closure return private_method(); }; } o = new Foo(5); o.privileged_method(); alert( o.bar() ); alert( o.private_data ); // undefined
  • 23. Putting it all together – Classes with scopes, My own practice Enclosing closure for each class definition => allow for private static vars and methods Naming convention on instance private vars and methods (pragmatic, saves memory, easier to test) Enclosing closure for each namespace.
  • 24. Whatever – Recursion and scope chain I function factorial(n) { if (n < 2) return 1; return n * factorial(n-1); } Math.factorial = factorial; alert( Math.factorial(5) ); // 120 // later in code factorial = function(n) { return 1; } alert( Math.factorial(5) ); // 5
  • 25. Whatever – Recursion and scope chain II function factorial(n) { if (n < 2) return 1; return n * arguments.callee(n-1); } Math.factorial = factorial; alert( Math.factorial(5) ); // 120 // later in code factorial = function(n) { return 1; } alert( Math.factorial(5) ); // 120
  • 26. Whatever – Arguments.callee for function static variables function click() { if (--arguments.callee.tries < 0) { alert('forbidden'); return false; } alert('click'); return true; } click.tries = 3; click(); // click click(); // click click(); // click click(); // forbidden
  • 27. Function class – Function.apply / Function.call I Function.apply and function.call are used to manually set the meaning of 'this' function getX(y, z) { alert( [this.x, y, z] ); } o1 = {x:5}; o2 = {x:6}; getX.apply(o1, [6, 7]); getX.apply(o2, [7, 8]); getX.call(o1, 6, 7); getX.call(o2, 7, 8);
  • 28. Function class – Function.apply / Function.call II variable length arguments are typically not known in advance, use function.apply to pass an array that will translate to arguments function average () { if (arguments.length <= 0) return 0; var total = 0; for (var i=0; i<arguments.length; i++) { total += arguments[i]; } return total / arguments.length; } data = [3, 5, 7, 9, 11, 8, 2]; a = average.apply(null, data); alert(a); // 6.428571428571429
  • 29. Delegation I function MyClass(name){ this.name = name; this.setup(); } MyClass.prototype.setup = function() { var b = document.getElementsByTagName('body')[0]; b.onclick = this.sayMyName; } MyClass.prototype.sayMyName = function() { alert( this.name ); } m = new MyClass('tim'); // click in browser => undefined!
  • 30. Delegation II – Store a reference to 'this' in the closure function MyClass(name) { this.name = name; this.setup(); } MyClass.prototype.setup = function() { var b = document.getElementsByTagName('body')[0]; var self = this; b.onclick = function(){ self.sayMyName(); } } MyClass.prototype.sayMyName = function(){ alert( this.name ); } m = new MyClass('tim'); // click in browser => tim!
  • 31. Delegation III – use a helper function with function reference delegate = function(obj, func) { return function() { return func.apply(obj, arguments); } } o1 = { name: 'o1', sayMyName: function(a){ alert(a + ' ' + this.name) } }; o2 = {name: 'o2'}; o2.sayIt = delegate(o1, o1.sayMyName); o2.sayIt('hello');
  • 32. Delegation IV – use a helper function with a function name delegate = function(obj, funcName) { return function() { return obj[funcName].apply(obj, arguments); } } o1 = { name: 'o1', sayMyName: function(a){ alert(a + ' ' + this.name) } }; o2 = {name: 'o2'}; o2.sayIt = delegate(o1, 'sayMyName'); o2.sayIt('hello'); // 'hello o1'
  • 33. Delegation V – allow setting fixed parameters, when delegating delegate = function(obj, funcName, greeting) { return function() { return obj[funcName].apply(obj, [greeting]); } } o1 = { name: 'o1', sayMyName: function(a){ alert(a + ' ' + this.name) } }; o2 = {name: 'o2'}; o2.sayIt = delegate(o1, 'sayMyName', 'bonjour'); o2.sayIt('hello'); // 'bonjour o1'
  • 34. Trivia I In a browser, the global scope is the window object itself :) a = 5; alert( window.a ); // 5
  • 35. Trivia II - Assignment without explicit scope Assignment without explicit scope is done to the first scope object that had the variable declared locally, or global scope if none: var x = 5; function foo() { x = 6; } foo(); alert(x); // 6 /*======================================*/ var x = 5; function foo() { var x; return function() { x = 6; } } foo()(); alert(x); // 5
  • 36. Conclusion Functions in JS are VERY powerful Closures are probably the most useful feature of JS Beware, you can get bitten hard, especially with long closures (e.g. jQuery has one fat outer function scope of 6,500+ lines). Watch out for: ●local variables in outer scopes that are left alive forever ●function duplication where they are not required Especially true in the days of cool ajaxy libraries like jQuery or ExtJS, which make it very easy to use closures without completely understanding them. I'm not bashing jQuery, I looove jQuery, just know your tools well! :D