this

64
this othree@JSDC

description

Explain this of JavaScript

Transcript of this

Page 1: this

this

othree@JSDC

Page 2: this
Page 3: this

this

• ‘the object’ it belongs in OOP

Page 4: this

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

Page 5: this

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

Page 6: this

this

• Context in JavaScript

• Can mean the object also

Page 7: this

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

Page 8: this

this in Function

• Context

• Depends on how you call the function

Page 9: this

3 Way to Call Function

var big = function () {/*...*/};

var foo = { small: function () {/*...*/}};

big(); // 1. this: window objectfoo.small(); // 2. this: foo

var small = foo.small;small();

Page 10: this

3 Way to Call Function

var big = function () {/*...*/};

var foo = { small: function () {/*...*/}};

big(); // 1. this: window objectfoo.small(); // 2. this: foo

var small = foo.small;small(); // 3. this: window object

Page 11: this

Borrowing Method

var foo = { small: function () { this; }};var bar = {};

foo.small(); // this: foo

bar.borrowedSmall = foo.small;bar.borrowedSmall(); // this: bar

Page 12: this

this in Global Scope

• Root object

Page 13: this

Root Object

• `window` in browsers

• Root object in other environment

Page 14: this

to Support Both

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

Page 15: this

Strict Mode

• No more global context

Page 16: this

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

Page 17: this

One More Way to Call Function

Page 18: this

apply/call

var foo = {};

function foobar(v1, v2) { this.bar1 = v1; this.bar2 = v2;}

foobar.call(foo, v1, v2); // this: foofoobar.apply(foo, [v1, v2]); // this: foo

Page 19: this

bindvar 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

Page 20: this

Protect Your Method

• Bind context and function together

Page 21: this

$.proxy/_.bind

• Use apply to implement bind

Page 22: this

Implement Bind

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

Page 23: this

Solve Event Handler

• Use apply to assign context

• JavaScript Libraries did it for you

Page 24: this

this in Event Handler

// W3C DomaElement.addEventListener('click', function () { this; // aElement}, false);

// old IEaElement.attachEvent('onclick', function () { this; // window});

Page 25: this

Callback Functionfunction Foobar (input) { this.prefix = input;}

Foobar.prototype.echo = function (result) { return this.prefix + result;};

fb = new Foobar();

$.get('/info', function (result) { fb.echo(result);});

Page 26: this

Reduce One Stackfunction 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'

Page 27: this

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

Page 28: this

Cons

• Performance is bad

• Old browser don’t support

Page 29: this

Performance

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

Page 30: this

Pros

• Clearer code

Page 31: this

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

Page 32: this

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/

Page 33: this

Bind by Need

fb = new Foobar();

$.get('/info', $.proxy(fb, 'echo'));$.get('/info', $.proxy(fb.echo, fb));

$.get('/info', $.bind(fb.echo, fb));

Page 35: this

Avoid Using this

Page 36: this

Closure

var isIE = true;

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

Page 37: this

that/selffunction 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);

Page 38: this

CoffeeScript Fat Arrow

Foobar = (input) -> @prefix = input @echo = (result) => @prefix + result fb = new Foobar('res: ')

$.get('/info', fb.echo)

Page 39: this

CoffeeScript Fat Arrow

Foobar = (input) -> @prefix = input @echo = (result) => @prefix + result fb = new Foobar('res: ')

$.get('/info', fb.echo)

Page 40: this

Compile to JSvar 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);

Page 41: this

Compile to JSvar 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);

Page 42: this

Compile to JSvar 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);

Page 43: this

LiveScript use ~>

Page 44: this

Pros

• No more this issue, context free

• Reduce one call stack

• No call/apply, no impact on performance

• Encapsulation

Page 45: this

Cons

• Can’t use this tip on normal constructor

Page 46: this

How about AMD

• Define modules can return constructor, function, array, primitive data

• Define a singleton module on most cases

• Always have data on module

Page 47: this

AMD Singleton Module

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

Page 48: this

Cons

• Function can be borrowed

• Not doing on right `this` you expect

Page 49: this

Avoid Use this

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

Page 50: this

Constructors?

• Use bind to protect methods if necessary

Page 51: this

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

Page 52: this

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"

Page 53: this

Cons

• No prototype inheritance

• More memory usage for methods

Page 54: this

Backbone Modeldefine(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') }; } );});

Page 55: this

Who Use this Tip

• jQuery.Deferred

• jQuery.Callbacks

Page 56: this

Deferred Chaining

var firstDfd = $.Deferred(), secondDfd = $.Deferred(), thirdDfd = $.Deferred(); firstDfd.done(secondDfd.resolve);secondDfd.done(thirdDfd.resolve);

firstDfd.resolve(); // All Deferred object resolved here

Page 57: this

Callbacks

https://github.com/jquery/jquery/blob/master/src/deferred.js

// promise[ done | fail | progress ] = list.addpromise[ 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;

Page 58: this

Callbacks

https://github.com/jquery/jquery/blob/master/src/deferred.js

// promise[ done | fail | progress ] = list.addpromise[ 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;

Page 59: this

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;

Page 60: this

Summary

• Understand `this`

• Understand how not to use `this`

• Use `this` carefully if necessary

Page 61: this

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

Page 62: this

References

Page 64: this

Questions?

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