函数作用域修改大法

  • Function.prototype.bind()

    bind()方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入 bind() 方法的第一个参数作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。

    语法

    1
    2
    3
    4
    5
    6
    7
    /**
    * thisArg
    * 当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用new 操作符调用绑定函数时,该参数无效。
    * arg1, arg2, ...
    * 当绑定函数被调用时,这些参数加上绑定函数本身的参数会按照顺序作为原函数运行时的参数。
    */

    fun.bind(thisArg[, arg1[, arg2[, ...]]])

    支持情况:ie >= 9、chrome、safari。

    举例(摘自MDN):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    function LateBloomer() {
    this.petalCount = Math.ceil(Math.random() * 12) + 1;
    }

    // Declare bloom after a delay of 1 second
    LateBloomer.prototype.bloom = function() {
    window.setTimeout(this.declare.bind(this), 1000);
    };

    LateBloomer.prototype.declare = function() {
    console.log('I am a beautiful flower with ' +
    this.petalCount + ' petals!');
    };

    var flower = new LateBloomer();
    flower.bloom(); // 一秒钟后, 调用'declare'方法

    为了兼容低版本浏览器,可以使用如下 polyfill

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    if (!Function.prototype.bind) {
    Function.prototype.bind = function (oThis) {
    if (typeof this !== "function") {
    // closest thing possible to the ECMAScript 5
    // internal IsCallable function
    throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var aArgs = Array.prototype.slice.call(arguments, 1),
    fToBind = this,
    fNOP = function () {},
    fBound = function () {
    return fToBind.apply(this instanceof fNOP
    ? this
    : oThis || this,
    aArgs.concat(Array.prototype.slice.call(arguments)));
    };

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();

    return fBound;
    };
    }
  • Function.prototype.call 和 Function.prototype.apply

    举例(摘自MDN):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    function Product(name, price) {
    this.name = name;
    this.price = price;

    if (price < 0) {
    throw RangeError('Cannot create product ' +
    this.name + ' with a negative price');
    }
    }

    function Food(name, price) {
    Product.call(this, name, price);
    this.category = 'food';
    }

    function Toy(name, price) {
    Product.call(this, name, price);
    this.category = 'toy';
    }

    var cheese = new Food('feta', 5);
    var fun = new Toy('robot', 40);
  • $.proxy(fn, context[, arg1[, arg2[, …]]])

    这里使用的 jQuery 方法,接受两个参数,第一个为输入的函数,第二个为要绑定到的作用域,之后的参数加上绑定函数本身的参数会按照顺序作为原函数运行时的参数。此方法返回一个作用域为 context 的新函数。

    举例(摘自jQuery):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    <!doctype html>
    <html lang="en">
    <head>
    <meta charset="utf-8">
    <title>jQuery.proxy demo</title>
    <script src="https://code.jquery.com/jquery-1.10.2.js"></script>
    </head>
    <body>

    <p><button type="button" id="test">Test</button></p>
    <div id="log"></div>

    <script>
    var me = {
    type: "zombie",
    test: function( event ) {
    // Without proxy, `this` would refer to the event target
    // use event.target to reference that element.
    var element = event.target;
    $( element ).css( "background-color", "red" );

    // With proxy, `this` refers to the me object encapsulating
    // this function.
    $( "#log" ).append( "Hello " + this.type + "<br>" );
    $( "#test" ).off( "click", this.test );
    }
    };

    var you = {
    type: "person",
    test: function( event ) {
    $( "#log" ).append( this.type + " " );
    }
    };

    // Execute you.test() in the context of the `you` object
    // no matter where it is called
    // i.e. the `this` keyword will refer to `you`
    var youClick = $.proxy( you.test, you );

    // attach click handlers to #test
    $( "#test" )
    // this === "zombie"; handler unbound after first click
    .on( "click", $.proxy( me.test, me ) )

    // this === "person"
    .on( "click", youClick )

    // this === "zombie"
    .on( "click", $.proxy( you.test, me ) )

    // this === "<button> element"
    .on( "click", you.test );
    </script>


    </body>
    </html>