Upgrade to Pro — share decks privately, control downloads, hide ads and more …

this

 this

A introduction about this in JavaScript

othree

May 19, 2013
Tweet

More Decks by othree

Other Decks in Programming

Transcript

  1. this
    othree@JSDC

    View Slide

  2. View Slide

  3. this
    • ‘the object’ it belongs in OOP

    View Slide

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

    View Slide

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

    View Slide

  6. this
    • Context in JavaScript
    • Can mean the object also

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  10. 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

    View Slide

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

    View Slide

  12. this in Global Scope
    • Root object

    View Slide

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

    View Slide

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

    View Slide

  15. Strict Mode
    • No more global context

    View Slide

  16. 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

    View Slide

  17. One More Way to Call
    Function

    View Slide

  18. 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

    View Slide

  19. 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

    View Slide

  20. Protect Your Method
    • Bind context and function together

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  25. 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);
    });

    View Slide

  26. 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'

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  30. Pros
    • Clearer code

    View Slide

  31. 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

    View Slide

  32. 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/

    View Slide

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

    View Slide

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

    View Slide

  35. Avoid Using this

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  43. LiveScript use ~>

    View Slide

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

    View Slide

  45. Cons
    • Can’t use this tip on normal constructor

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  50. Constructors?
    • Use bind to protect methods if necessary

    View Slide

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

    View Slide

  52. 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"

    View Slide

  53. Cons
    • No prototype inheritance
    • More memory usage for methods

    View Slide

  54. 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')
    };
    }
    );
    });

    View Slide

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

    View Slide

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

    View Slide

  57. 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;

    View Slide

  58. 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;

    View Slide

  59. 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;

    View Slide

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

    View Slide

  61. 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

    View Slide

  62. References

    View Slide

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

    View Slide

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

    View Slide