您的位置:澳门新葡8455最新网站 > Web前端 > 深刻之call和apply的依葫芦画瓢实现

深刻之call和apply的依葫芦画瓢实现

发布时间:2019-11-04 10:57编辑:Web前端浏览(69)

    JavaScript 浓重之call和apply的模仿完成

    2017/05/25 · JavaScript · apply, call

    最早的小讲出处: 冴羽   

    call

    一句话介绍 call:

    call() 方法在行使一个点名的 this 值和几何个钦赐的参数值的前提下调用有些函数或措施。

    举个例证:

    var foo = { value: 1 }; function bar() { console.log(this.value); } bar.call(foo); // 1

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var foo = {
        value: 1
    };
     
    function bar() {
        console.log(this.value);
    }
     
    bar.call(foo); // 1

    注意两点:

    1. call 改变了 this 的指向,指向到 foo
    2. bar 函数推行了

    宪章达成率先步

    这正是说大家该怎么模拟完结那五个效用呢?

    试想当调用 call 的时候,把 foo 对象更改成如下:

    var foo = { value: 1, bar: function() { console.log(this.value) } }; foo.bar(); // 1

    1
    2
    3
    4
    5
    6
    7
    8
    var foo = {
        value: 1,
        bar: function() {
            console.log(this.value)
        }
    };
     
    foo.bar(); // 1

    那时候 this 就针对了 foo,是还是不是很简短吗?

    可是这么却给 foo 对象自己加多了贰个性子,那可那多少个啊!

    不过也不用想念,大家用 delete 再删除它不就好了~

    进而大家模拟的手续能够分为:

    1. 将函数设为对象的天性
    2. 实践该函数
    3. 删去该函数

    如上个例证为例,就是:

    // 第一步 foo.fn = bar // 第二步 foo.fn() // 第三步 delete foo.fn

    1
    2
    3
    4
    5
    6
    // 第一步
    foo.fn = bar
    // 第二步
    foo.fn()
    // 第三步
    delete foo.fn

    fn 是指标的属性名,反正最终也要去除它,所以起成什么都不留意。

    依照那个思路,大家得以品味着去写第意气风发版的 call2 函数:

    // 第意气风发版 Function.prototype.call2 = function(context) { // 首先要收获调用call的函数,用this能够博得 context.fn = this; context.fn(); delete context.fn; } // 测量试验一下 var foo = { value: 1 }; function bar() { console.log(this.value); } bar.call2(foo); // 1

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // 第一版
    Function.prototype.call2 = function(context) {
        // 首先要获取调用call的函数,用this可以获取
        context.fn = this;
        context.fn();
        delete context.fn;
    }
     
    // 测试一下
    var foo = {
        value: 1
    };
     
    function bar() {
        console.log(this.value);
    }
     
    bar.call2(foo); // 1

    适逢其时可以打印 1 哎!是或不是很欢娱!(~ ̄▽ ̄)~

    宪章完成第二步

    最一带头也讲了,call 函数仍为能够给定参数试行函数。比如:

    var foo = { value: 1 }; function bar(name, age) { console.log(name) console.log(age) console.log(this.value); } bar.call(foo, 'kevin', 18); // kevin // 18 // 1

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    var foo = {
        value: 1
    };
     
    function bar(name, age) {
        console.log(name)
        console.log(age)
        console.log(this.value);
    }
     
    bar.call(foo, 'kevin', 18);
    // kevin
    // 18
    // 1

    留意:传入的参数并不明确,那可如何是好?

    不急,我们得以从 Arguments 对象中取值,取出第3个到终极三个参数,然后放到多个数组里。

    例如那样:

    // 以上个例子为例,那时候的arguments为: // arguments = { // 0: foo, // 1: 'kevin', // 2: 18, // length: 3 // } // 因为arguments是类数组对象,所以能够用for循环 var args = []; for(var i = 1, len = arguments.length; i len; i++) { args.push('arguments[' + i + ']'); } // 执行后 args为 [foo, 'kevin', 18]

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 以上个例子为例,此时的arguments为:
    // arguments = {
    //      0: foo,
    //      1: 'kevin',
    //      2: 18,
    //      length: 3
    // }
    // 因为arguments是类数组对象,所以可以用for循环
    var args = [];
    for(var i = 1, len = arguments.length; i  len; i++) {
        args.push('arguments[' + i + ']');
    }
     
    // 执行后 args为 [foo, 'kevin', 18]

    不定长的参数难点解决了,大家跟着要把这几个参数数组放到要实行的函数的参数里面去。

    // 将数组里的因素作为八个参数放进函数的形参里 context.fn(args.join(',')) // (O_o)?? // 那些艺术自然是充足的啊!!!

    1
    2
    3
    4
    // 将数组里的元素作为多个参数放进函数的形参里
    context.fn(args.join(','))
    // (O_o)??
    // 这个方法肯定是不行的啦!!!

    也许有人想到用 ES6 的方法,但是 call 是 ES3 的办法,我们为了参照他事他说加以考察完结一个ES3 的主意,要用到ES6的秘技,好像……,嗯,也得以啦。可是大家此次用 eval 方法拼成贰个函数,雷同于如此:

    eval('context.fn(' + args +')')

    1
    eval('context.fn(' + args +')')

    此处 args 会自动调用 Array.toString() 那个法子。

    就此大家的第二版克制了五个大难题,代码如下:

    // 第二版 Function.prototype.call2 = function(context) { context.fn = this; var args = []; for(var i = 1, len = arguments.length; i len; i++) { args.push('arguments[' + i + ']'); } eval('context.fn(' + args +')'); delete context.fn; } // 测验一下 var foo = { value: 1 }; function bar(name, age) { console.log(name) console.log(age) console.log(this.value); } bar.call2(foo, 'kevin', 18); // kevin // 18 // 1

    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
    // 第二版
    Function.prototype.call2 = function(context) {
        context.fn = this;
        var args = [];
        for(var i = 1, len = arguments.length; i  len; i++) {
            args.push('arguments[' + i + ']');
        }
        eval('context.fn(' + args +')');
        delete context.fn;
    }
     
    // 测试一下
    var foo = {
        value: 1
    };
     
    function bar(name, age) {
        console.log(name)
        console.log(age)
        console.log(this.value);
    }
     
    bar.call2(foo, 'kevin', 18);
    // kevin
    // 18
    // 1

    (๑•̀ㅂ•́)و✧

    仿照落成第三步

    效仿代码已经达成 八成,还恐怕有多少个小点要注意:

    1.this 参数能够传 null,当为 null 的时候,视为指向 window

    举个例证:

    var value = 1; function bar() { console.log(this.value); } bar.call(null); // 1

    1
    2
    3
    4
    5
    6
    7
    var value = 1;
     
    function bar() {
        console.log(this.value);
    }
     
    bar.call(null); // 1

    虽说那几个事例本人不应用 call,结果照旧相像。

    2.函数是足以有再次来到值的!

    举个例证:

    澳门新葡8455最新网站,var obj = { value: 1 } function bar(name, age) { return { value: this.value, name: name, age: age } } console.log(bar.call(obj, 'kevin', 18)); // Object { // value: 1, // name: 'kevin', // age: 18 // }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    var obj = {
        value: 1
    }
     
    function bar(name, age) {
        return {
            value: this.value,
            name: name,
            age: age
        }
    }
     
    console.log(bar.call(obj, 'kevin', 18));
    // Object {
    //    value: 1,
    //    name: 'kevin',
    //    age: 18
    // }

    只是都很好消释,让我们从来看第三版也正是终极豆蔻年华版的代码:

    // 第三版 Function.prototype.call2 = function (context) { var context = context || window; context.fn = this; var args = []; for(var i = 1, len = arguments.length; i len; i++) { args.push('arguments[' + i + ']'); } var result = eval('context.fn(' + args +')'); delete context.fn return result; } // 测验一下 var value = 2; var obj = { value: 1 } function bar(name, age) { console.log(this.value); return { value: this.value, name: name, age: age } } bar.call(null); // 2 console.log(bar.call2(obj, 'kevin', 18)); // 1 // Object { // value: 1, // name: 'kevin', // age: 18 // }

    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
    // 第三版
    Function.prototype.call2 = function (context) {
        var context = context || window;
        context.fn = this;
     
        var args = [];
        for(var i = 1, len = arguments.length; i  len; i++) {
            args.push('arguments[' + i + ']');
        }
     
        var result = eval('context.fn(' + args +')');
     
        delete context.fn
        return result;
    }
     
    // 测试一下
    var value = 2;
     
    var obj = {
        value: 1
    }
     
    function bar(name, age) {
        console.log(this.value);
        return {
            value: this.value,
            name: name,
            age: age
        }
    }
     
    bar.call(null); // 2
     
    console.log(bar.call2(obj, 'kevin', 18));
    // 1
    // Object {
    //    value: 1,
    //    name: 'kevin',
    //    age: 18
    // }

    到此,大家成功了 call 的模拟实现,给谐和一个赞 b( ̄▽ ̄卡塔 尔(阿拉伯语:قطر‎d

    apply的效仿达成

    apply 的完成跟 call 相近,在那处直接给代码,代码来自于今日头条 @郑航的兑现:

    Function.prototype.apply = function (context, arr) { var context = Object(context) || window; context.fn = this; var result; if (!arr) { result = context.fn(); } else { var args = []; for (var i = 0, len = arr.length; i len; i++) { args.push('arr[' + i + ']'); } result = eval('context.fn(' + args + ')') } delete context.fn return result; }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    Function.prototype.apply = function (context, arr) {
        var context = Object(context) || window;
        context.fn = this;
     
        var result;
        if (!arr) {
            result = context.fn();
        }
        else {
            var args = [];
            for (var i = 0, len = arr.length; i  len; i++) {
                args.push('arr[' + i + ']');
            }
            result = eval('context.fn(' + args + ')')
        }
     
        delete context.fn
        return result;
    }

    根本参照

    天涯论坛难点 不能够选取call、apply、bind,怎样用 js 完毕 call 只怕 apply 的效果与利益?

    深入体系

    JavaScript深远连串目录地址:。

    JavaScript深切连串测度写十三篇左右,目的在于帮我们捋顺JavaScript底层知识,重点教学如原型、功用域、试行上下文、变量对象、this、闭包、按值传递、call、apply、bind、new、世袭等难点概念。

    大器晚成旦有不当恐怕不小心谨慎的地点,请必需授予指正,相当多谢。假若喜欢或许具备启迪,招待star,对笔者也是大器晚成种鞭笞。

    本系列:

    1. JavaScirpt 深切之从原型到原型链
    2. JavaScript 深切之词法功效域和动态成效域
    3. JavaScript 深入之实践上下文栈
    4. JavaScript 长远之变量对象
    5. JavaScript 深远之成效域链
    6. JavaScript 深刻之从 ECMAScript 典型解读 this
    7. JavaScript 长远之推行上下文
    8. JavaScript 深远之闭包
    9. JavaScript 深切之参数按值传递

      1 赞 收藏 评论

    澳门新葡8455最新网站 1

    本文由澳门新葡8455最新网站发布于Web前端,转载请注明出处:深刻之call和apply的依葫芦画瓢实现

    关键词:

上一篇:没有了

下一篇:没有了