Slide 1

Slide 1 text

this othree@JSDC

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

this • ‘the object’ it belongs in OOP

Slide 4

Slide 4 text

C++ class Box { public: Box(double l=2.0, double b=2.0, double h=2.0) { this->length = l; this->breadth = b; this->height = h; } double Volume() { return length * breadth * height; } int compare(Box box) { return this->Volume() > box.Volume(); } private: double length; double breadth; double height; };

Slide 5

Slide 5 text

Continued.. int main(void) { Box Box1(3.3, 1.2, 1.5); Box Box2(8.5, 6.0, 2.0); cout << Box1.Volume() << endl; // 3.3*1.2*1.5 = 5.94 cout << Box2.Volume() << endl; // 8.5*6.0*2.0 = 102 return 0; }

Slide 6

Slide 6 text

this • Context in JavaScript • Can mean the object also

Slide 7

Slide 7 text

JavaScript var Box = function Box (l, b, h) { this.length = l; this.breadth = b; this.height = h; }; Box.prototype.Volume = function () { return this.length * this.breadth * this.height; }; Box.prototype.compare = function (box) { return this.Volume() > box.Volume(); };

Slide 8

Slide 8 text

this in Function • Context • Depends on how you call the function

Slide 9

Slide 9 text

3 Way to Call Function var big = function () {/*...*/}; var foo = { small: function () {/*...*/} }; big(); // 1. this: window object foo.small(); // 2. this: foo var small = foo.small; small();

Slide 10

Slide 10 text

3 Way to Call Function var big = function () {/*...*/}; var foo = { small: function () {/*...*/} }; big(); // 1. this: window object foo.small(); // 2. this: foo var small = foo.small; small(); // 3. this: window object

Slide 11

Slide 11 text

Borrowing Method var foo = { small: function () { this; } }; var bar = {}; foo.small(); // this: foo bar.borrowedSmall = foo.small; bar.borrowedSmall(); // this: bar

Slide 12

Slide 12 text

this in Global Scope • Root object

Slide 13

Slide 13 text

Root Object • `window` in browsers • Root object in other environment

Slide 14

Slide 14 text

to Support Both (function () { root = this; //blah.... }());

Slide 15

Slide 15 text

Strict Mode • No more global context

Slide 16

Slide 16 text

Don’t Forget `new` function Foobar() { "use strict"; this.a = 'foo'; this.b = 'bar'; } var foobar1 = Foobar(); // Cannot set property 'a' of undefined var foobar2 = new Foobar(); // this: new empty object

Slide 17

Slide 17 text

One More Way to Call Function

Slide 18

Slide 18 text

apply/call var foo = {}; function foobar(v1, v2) { this.bar1 = v1; this.bar2 = v2; } foobar.call(foo, v1, v2); // this: foo foobar.apply(foo, [v1, v2]); // this: foo

Slide 19

Slide 19 text

bind var foo = {}; var otherFoo = {}; function setBar(v1, v2) { this.bar1 = v1; this.bar2 = v2; } var fooSetBar = setBar.bind(foo); fooSetBar(1, 2); // this: foo otherFoo.foobar = fooSetBar; otherFoo.foobar(3, 4); // this: foo

Slide 20

Slide 20 text

Protect Your Method • Bind context and function together

Slide 21

Slide 21 text

$.proxy/_.bind • Use apply to implement bind

Slide 22

Slide 22 text

Implement Bind var bind = function (func, context) { return function () { func.apply(context, arguments); }; };

Slide 23

Slide 23 text

Solve Event Handler • Use apply to assign context • JavaScript Libraries did it for you

Slide 24

Slide 24 text

this in Event Handler // W3C Dom aElement.addEventListener('click', function () { this; // aElement }, false); // old IE aElement.attachEvent('onclick', function () { this; // window });

Slide 25

Slide 25 text

Callback Function function Foobar (input) { this.prefix = input; } Foobar.prototype.echo = function (result) { return this.prefix + result; }; fb = new Foobar(); $.get('/info', function (result) { fb.echo(result); });

Slide 26

Slide 26 text

Reduce One Stack function Foobar (input) { this.prefix = input; } Foobar.prototype.echo = function (result) { return this.prefix + result; }; fb = new Foobar(); $.get('/info', fb.echo); // this.prefix is 'undefined'

Slide 27

Slide 27 text

Use bind function Foobar (input) { this.prefix = input; this.echo = this.echo.bind(this); // Protect method } Foobar.prototype.echo = function (result) { return this.prefix + result; }; fb = new Foobar(); $.get('/info', fb.echo);

Slide 28

Slide 28 text

Cons • Performance is bad • Old browser don’t support

Slide 29

Slide 29 text

Performance http://jsperf.com/bind-vs-closure-setup/10

Slide 30

Slide 30 text

Pros • Clearer code

Slide 31

Slide 31 text

Use _.bind function Foobar (input) { this.prefix = input; this.echo = _.bind(this.echo, this); // function, context } function Foobar (input) { this.prefix = input; _.bindAll(this); // context } http://underscorejs.org/#bind

Slide 32

Slide 32 text

Use $.proxy function Foobar (input) { this.prefix = input; this.echo = $.proxy(this.echo, this); // function, context } function Foobar (input) { this.prefix = input; this.echo = $.proxy(this, 'echo'); // context, method name } http://api.jquery.com/jQuery.proxy/

Slide 33

Slide 33 text

Bind by Need fb = new Foobar(); $.get('/info', $.proxy(fb, 'echo')); $.get('/info', $.proxy(fb.echo, fb)); $.get('/info', $.bind(fb.echo, fb));

Slide 34

Slide 34 text

http://www.flickr.com/photos/othree/8544069132/

Slide 35

Slide 35 text

Avoid Using this

Slide 36

Slide 36 text

Closure var isIE = true; function foobar() { if (!isIE) { // access isIE is possible because of closure return; } // blah... };

Slide 37

Slide 37 text

that/self function Foobar(input) { var that = this; // that or self this.prefix = input; this.echo = function (result) { return that.prefix + result; // that is accessible because of closure }; } fb = new Foobar('res: '); $.get('/info', fb.echo);

Slide 38

Slide 38 text

CoffeeScript Fat Arrow Foobar = (input) -> @prefix = input @echo = (result) => @prefix + result fb = new Foobar('res: ') $.get('/info', fb.echo)

Slide 39

Slide 39 text

CoffeeScript Fat Arrow Foobar = (input) -> @prefix = input @echo = (result) => @prefix + result fb = new Foobar('res: ') $.get('/info', fb.echo)

Slide 40

Slide 40 text

Compile to JS var Foobar, fb; Foobar = function(input) { var _this = this; this.prefix = input; return this.echo = function(result) { return _this.prefix + result; }; }; fb = new Foobar('res: '); $.get('/info', fb.echo);

Slide 41

Slide 41 text

Compile to JS var Foobar, fb; Foobar = function(input) { var _this = this; this.prefix = input; return this.echo = function(result) { return _this.prefix + result; }; }; fb = new Foobar('res: '); $.get('/info', fb.echo);

Slide 42

Slide 42 text

Compile to JS var Foobar, fb; Foobar = function(input) { var _this = this; this.prefix = input; return this.echo = function(result) { return _this.prefix + result; }; }; fb = new Foobar('res: '); $.get('/info', fb.echo);

Slide 43

Slide 43 text

LiveScript use ~>

Slide 44

Slide 44 text

Pros • No more this issue, context free • Reduce one call stack • No call/apply, no impact on performance • Encapsulation

Slide 45

Slide 45 text

Cons • Can’t use this tip on normal constructor

Slide 46

Slide 46 text

How about AMD • Define modules can return constructor, function, array, primitive data • Define a singleton module on most cases • Always have data on module

Slide 47

Slide 47 text

AMD Singleton Module define('foobar', function () { return { init: function (prefix) { this.prefix = prefix; } echo: function (input) { return this.prefix + input; } }; });

Slide 48

Slide 48 text

Cons • Function can be borrowed • Not doing on right `this` you expect

Slide 49

Slide 49 text

Avoid Use this define('foobar', function () { var self = {}; return { init: function (prefix) { self.prefix = prefix; } echo: function (input) { return self.prefix + input; } }; });

Slide 50

Slide 50 text

Constructors? • Use bind to protect methods if necessary

Slide 51

Slide 51 text

Constructor Without this function Person(birth, gender) { var person = { birth: (birth || '1970/01/01'), gender: (gender || 'M') }; return { getBirth: function () { return person.birth; }, getGender: function () { return person.gender; } }; }

Slide 52

Slide 52 text

to new or not to new var p1 = Person('2013/01/02'); p1.getBirth(); // "2013/01/02" var p2 = new Person('2000/01/02', 'F'); p2.getBirth(); // "2000/01/02" p1.getBirth(); // "2013/01/02"

Slide 53

Slide 53 text

Cons • No prototype inheritance • More memory usage for methods

Slide 54

Slide 54 text

Backbone Model define(function (require) { return Backbone.Model.extend( initialize: function (attrs) { return _.bindAll(this); }, toITEM: function () { return this.toJSON(); }, toConfig: function () { return { name: this.get('name'), package_name: this.get('package_name') }; } ); });

Slide 55

Slide 55 text

Who Use this Tip • jQuery.Deferred • jQuery.Callbacks

Slide 56

Slide 56 text

Deferred Chaining var firstDfd = $.Deferred(), secondDfd = $.Deferred(), thirdDfd = $.Deferred(); firstDfd.done(secondDfd.resolve); secondDfd.done(thirdDfd.resolve); firstDfd.resolve(); // All Deferred object resolved here

Slide 57

Slide 57 text

Callbacks https://github.com/jquery/jquery/blob/master/src/deferred.js // promise[ done | fail | progress ] = list.add promise[ tuple[1] ] = list.add; // skip... // deferred[ resolve | reject | notify ] deferred[ tuple[0] ] = function() { deferred[ tuple[0] + "With" ] (this === deferred ? promise : this, arguments); return this; }; deferred[ tuple[0] + "With" ] = list.fireWith;

Slide 58

Slide 58 text

Callbacks https://github.com/jquery/jquery/blob/master/src/deferred.js // promise[ done | fail | progress ] = list.add promise[ tuple[1] ] = list.add; // skip... // deferred[ resolve | reject | notify ] deferred[ tuple[0] ] = function() { deferred[ tuple[0] + "With" ] (this === deferred ? promise : this, arguments); return this; }; deferred[ tuple[0] + "With" ] = list.fireWith;

Slide 59

Slide 59 text

Actually Are promise['done'] = resolveCallbacks.add; promise['fail'] = rejectCallbacks.add; promise['progress'] = notifyCallbacks.add; // skip... deferred["resolveWith"] = resolveCallbacks.fireWith; deferred["rejectWith"] = rejectCallbacks.fireWith; deferred["notifyWith"] = notifyCallbacks.fireWith;

Slide 60

Slide 60 text

Summary • Understand `this` • Understand how not to use `this` • Use `this` carefully if necessary

Slide 61

Slide 61 text

Trade-Off • ‘Not use this’ requires more memory on methods definition and not easy to inheritance object • Benefit is you can write more clear, simple code

Slide 62

Slide 62 text

References

Slide 63

Slide 63 text

https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/this

Slide 64

Slide 64 text

Questions? http://www.flickr.com/photos/roman/5610736/