the mighty js_function

36
The Mighty JS Function Timothee Groleau - 2010-11-04

Upload: timotheeg

Post on 30-Jun-2015

937 views

Category:

Technology


2 download

TRANSCRIPT

Page 1: The mighty js_function

The Mighty JS Function

Timothee Groleau - 2010-11-04

Page 2: The mighty js_function

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 :( )

Page 3: The mighty js_function

function factorial (n){ var fact = 1; while (n > 1) fact *= n--; return fact;}

x = 5;res = factorial(x);alert(x); // 5alert(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

Page 4: The mighty js_function

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)

Page 5: The mighty js_function

var foo = {x:5}, bar = {x:6};function whatsMyX(){

return this.x;}

foo.getX = whatsMyX;bar.getX = whatsMyX;

alert( foo.getX() ); // 5alert( 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

Page 6: The mighty js_function

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

Page 7: The mighty js_function

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!

Page 8: The mighty js_function

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)

Page 9: The mighty js_function

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

Page 10: The mighty js_function

Concept slide 2 – functions are first class objects

They can be assigned/accessed like any other variablefunction 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 functionsfunction foo() { alert('hello'); };function bar1(func) { func(); }bar1(foo);function bar2() { return foo; };bar2()();

Functions can hold datafunction foo(){}bar = foo;bar.aProperty = 'hello';alert(foo.aProperty);

Functions are instances of the Function class (more on that later... maybe)

Page 11: The mighty js_function

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()();

Page 12: The mighty js_function

Concept slide 4 – Scopes in javascript

No block scope in JS:var x = 5;{ var x = 6;}alert(x); // ?

Functions provide scopesvar x = 5;function foo(){ var x; x = 6; alert(x);}foo(); // 6alert(x); // 5

Page 13: The mighty js_function

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.

Page 14: The mighty js_function

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.

Page 15: The mighty js_function

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') ); // blaalert( getFromScope('someOtherLargeString') ); // blialert( getFromScope('theNumberIneed') ); // 5

Page 16: The mighty js_function

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,2g1(4); //4,3,2g1(5); //5,3,2g2 = f(6); //6,2g2(7); //7,6,2g2(8); //8,6,2

Page 17: The mighty js_function

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

Page 18: The mighty js_function

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

Page 19: The mighty js_function

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

Page 20: The mighty js_function

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);

Page 21: The mighty js_function

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 );

Page 22: The mighty js_function

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

Page 23: The mighty js_function

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.

Page 24: The mighty js_function

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

Page 25: The mighty js_function

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

Page 26: The mighty js_function

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(); // clickclick(); // clickclick(); // clickclick(); // forbidden

Page 27: The mighty js_function

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);

Page 28: The mighty js_function

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

Page 29: The mighty js_function

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!

Page 30: The mighty js_function

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!

Page 31: The mighty js_function

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');

Page 32: The mighty js_function

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'

Page 33: The mighty js_function

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'

Page 34: The mighty js_function

Trivia I

In a browser, the global scope is the window object itself :)

a = 5;alert( window.a ); // 5

Page 35: The mighty js_function

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

Page 36: The mighty js_function

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