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