资讯专栏INFORMATION COLUMN

我要懂系列1-call和apply

Stardustsky / 3050人阅读

摘要:和相同点改变指向可以传参立即调用区别接收一个数组参数,直接接收参数的性能会比差,因为要对数组参数进行判断和解构模拟实现传递的如果是或者,那么就是,否则传递进来的就是要指向的从第二个参数开始就是要调用时用到参数的第二个参数为数组,数组中的元素

call和apply
fn.call(isThis, arg1, arg2, ....)
fn.apply(isThis, [arg1, arg2, ....])

相同点:

改变 this 指向

可以传参

立即调用

区别:

apply 接收一个数组参数,call 直接接收参数

apply 的性能会比call差,因为要对数组参数进行判断和解构

模拟实现:

Function.prototype.callLike = function (isThis) {
    //...
}

Function.prototype.applyLike = function (isThis) {
    //...
}

传递的 isThis 如果是 undefined 或者 null,那么 this 就是 window,否则传递进来的就是要指向的this

call 从第二个参数开始就是要调用时用到参数

apply 的第二个参数为数组,数组中的元素就是调用时用到参数

第一个条件很简单,判断下 isThis 的类型即可

isThis = typeof isThis === "undefined" || isNull(isThis) ? window : isThis;


function isNull (value) {
    return typeof value === "object" && !value === true
}

第二个条件和第三个条件是一样的,call 的参数我们需要处理下,因为我们预期不到它的参数个数

我们从第二个参数开始遍历一遍 arguments,然后放到一个数组里面去

var args = [];
for (var i = 1, l = arguments.length; i< l; i++) {
    args.push(arguments[i]);
}

但是最关键的调用怎么办呢,怎么改变 this 的指向,一般来说谁调用谁就是 this,我们要想改变 this,那么就要用传递进来的 isThis 来调用

isThis.fn = this;
isThis.fn();
delete isThis.fn;

这样的话就改变了 this 的指向,之后再 delete 掉,就 ok 了,但是这里会有一个问题,如果传递进来的是值类型呢,值类型我们是不能给它添加属性和方法的,所以 isThis.fn() 肯定会提示 isThis.fn is not a function,这里我们可以想一想值类型也是可以像对象一样有属性和方法的,并且可以添加属性和方法,但是为什么赋值完后就找不到呢

这里要说下包装类型了,值类型按理说是不可能有自己的属性和方法的,但是考虑到有时候需要处理下杂七杂八的琐事,所以当我们访问或者赋值的时候,它会临时给我们创建一个对应的包装对象,在我们访问或者赋值结束后那么这个包装对象就会被清理掉,那么我们就可以这样做,来模拟下包装对象

var valueType = typeof isThis;
if (valueType === "string") {
    isThis = new String(isThis)
}else if (valueType === "number") {
    isThis = new Number(isThis)
}else if (valueType === "boolean") {
    isThis = new Boolean(isThis)
}else if (valueType === "symbol") {
    isThis = Object(isThis)
}

恩,这样一来就差不多了,接下来看看传参,args 的元素才是我们想要的参数,所以怎么拆开

var result = eval("isThis.fn(" + args.join() + ")");

使用 eval,得益于 eval 强大的能力,我们可以把字符串当做 js 代码来执行,并且可以得到返回值,完美

但是这里会有一个问题,如果参数是个对象,那么 eval 对参数 toString 后我们我们就得不到想要的参数了,所以这里改造下,因为 eval 可以动态的改变作用域

var args = [];
for (var i = 1, l = arguments.length; i< l; i++) {
    args.push("arguments[" + i  + "]");
}
var result = eval("isThis.fn(" + args.join() + ")");

这里我们用 args 存放着 arguments[1] arguments[1]... 这样的字符串,那么 eval 执行的时候会在上下文查找并绑定所需要的变量,这样就可以实现参数传递了

完整代码:

Function.prototype.callLike = function (isThis) {
    isThis = typeof isThis === "undefined" || isNull(isThis) ? window : isThis;

    var valueType = typeof isThis;
    if (valueType === "string") {
        isThis = new String(isThis)
    }else if (valueType === "number") {
        isThis = new Number(isThis)
    }else if (valueType === "boolean") {
        isThis = new Boolean(isThis)
    }else if (valueType === "symbol") {
        isThis = Object(isThis)
    }
    
    isThis.fn = this;

    var args = [];
    for (var i = 1, l = arguments.length; i< l; i++) {
        args.push("arguments[" + i  + "]");
    }

    var result = eval("isThis.fn(" + args.join() + ")");
    delete isThis.fn;

    function isNull (value) {
        return typeof value === "object" && !value === true
    }

    return result;
}
Function.prototype.applyLike = function (isThis, args) {
    isThis = typeof isThis === "undefined" || isNull(isThis) ? window : isThis;

    var valueType = typeof isThis;
    if (valueType === "string") {
        isThis = new String(isThis)
    }else if (valueType === "number") {
        isThis = new Number(isThis)
    }else if (valueType === "boolean") {
        isThis = new Boolean(isThis)
    }else if (valueType === "symbol") {
        isThis = Object(isThis)
    }
    
    isThis.fn = this;

    var args = [];
    for (var i = 1, l = arguments.length; i< l; i++) {
        args.push("arguments[" + i  + "]");
    }
    
    var result = eval("isThis.fn(" + args.join() + ")");
    delete isThis.fn;

    function isNull (value) {
        return typeof value === "object" && !value === true
    }

    return result;
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/89551.html

相关文章

  • 关于this的知识归纳(通俗易版)

    摘要:于是决定写个归纳。如果懂了,那么下面的例子也就会做了已知调用函数的对象是,所以指向,即。相当于在全局作用域声明了变量,并且赋值。实际上,调用函数的是关键字。使用来调用函数,即函数的构造调用时,我们会构造一个新对象,并把它绑定到调用中的上。 对this的理解,我一直都是用一句话概括:谁调用它,它就指向谁。 好像也没有什么问题,但仔细看了这本书和网上一些文章后,发现this的原理还挺讲究的...

    enda 评论0 收藏0
  • this总结【2】—— call/applybind

    摘要:和概览我们要将归为一类,单独归为一类三者的共同点是都可以指定和都是绑定在的原型上的,所以的实例都可以调用这三个方法至于为什么,看完这篇文章你就懂了如果你不懂什么是实例的话,请移步深入浅出面向对象和原型概念篇深入浅出面向对象和原型概念篇第一个 1.call/apply和bind概览 我们要将call/apply归为一类,bind单独归为一类 三者的共同点是都可以指定this call/...

    wudengzan 评论0 收藏0
  • JavaScript中的this

    摘要:指向的改变构造函数中的操作符会调用函数的内部的方法,创建对象,之后调用函数的方法,把新创建对象作为值。调用函数时与设置的值以及箭头函数皆为动态的改变指针的方法。这一特性使得箭头函数在中的函数中使用起来很方便。 原文地址 JavaScript中的this 原理 错误的this指向 通常所说的:如果是全局环境中,this指向全局对象,如果是对象的方法,这this指向这个对象。 例子1: ...

    Salamander 评论0 收藏0
  • 写给初入门/半路出家的前端er

    摘要:半路出家的前端程序员应该不在少数,我也是其中之一。年,冯冯同事兼师兄看我写太费劲,跟我说对面楼在找,问我要不要学,说出来可能有点丢人,但是在那之前,我真得不知道什么是,什么是。 半路出家的前端程序员应该不在少数,我也是其中之一。 为何会走向前端 非计算机专业的我,毕业之后,就职于一家电力行业公司,做过设备调试、部门助理、测试,也写过一段时间的QT,那三年的时间,最难过的不是工作忙不忙,...

    Cc_2011 评论0 收藏0
  • 【好好面试】学完Aop,连动态代理的原理都不

    摘要:总结动态代理的相关原理已经讲解完毕,接下来让我们回答以下几个思考题。 【干货点】 此处是【好好面试】系列文的第12篇文章。文章目标主要是通过原理剖析的方式解答Aop动态代理的面试热点问题,通过一步步提出问题和了解原理的方式,我们可以记得更深更牢,进而解决被面试官卡住喉咙的情况。问题如下 SpringBoot默认代理类型是什么 为什么不用静态代理 JDK动态代理原理 CGLIB动态代理...

    Keven 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<