zoukankan      html  css  js  c++  java
  • call apply bind

    语法:

    fun.call(thisArg, arg1, arg2, ...)

    thisArg

    第一个参数thisArg是函数fun运行时指定的this值,这个值在非严格模式下,null和undefined都指向window,在严格模式下,是谁就指向谁自己,例子:

    var name = 'leah'
        var obj = {
            name: 'lihhh'
        }
        function test1 () {
            console.log(this.name);
        }
        test1.call(obj) //lihhh this指向obj
        test1.call(null) //leah this指向window

    arg1, arg2, ...
    指定的参数列表
    返回值
    返回值是你调用的方法的返回值,若该方法没有返回值,则返回undefined

    func.apply(thisArg, [argsArray])

    第一个参数thisArg是函数fun运行时指定的this值,这个值在非严格模式下,null和undefined都指向window

    第二个参数argsArray是可选参数,可以是数组或者类数组,将作为单独的参数传给fun。

    相同点:

    第一个参数都是函数运行时指定的this,在非严格模式下,null和undefiend都指向window。都可以只传一个参数。

    区别:

    1、调用方式不同

            function fun(a, b) {
                alert(this.name);
            }
    
            var obj = {
                name: "obj",
                sayName: function () {
                    alert(this.name);
                }
            };
            var obj2 = {
                name: "obj2"
            };
            fun.call(obj2)

    alert的答案是obj2,此时this指向了obj2

    对于apply可以这样:

    fun.apply(obj2)

    对于bind需要这样:

    fun.bind(obj)()

    注意:使用call和apply会直接执行这个函数,而bind是将绑定好的this重新返回一个新函数,什么时候调用由你自己决定。需要自己手动调用。

    var objName = {name:'tom'};
        var obj = {
            name:'hhhh',
            sayHello:function(){
                console.log(this.name);
            }.bind(objName)
        };
        obj.sayHello(); 

    这个输出tom

    var objName = {name:'tom'};
        var obj = {
            name:'hhhh',
            sayHello:function(){
                console.log(this.name);
            }.call(objName)
        };
        obj.sayHello();

    如果是call会报错obj.sayHello is not a function,这是因为call会立即执行,执行完内存就会释放这个方法,没有这个方法了,而bind不会。

    2、传参

    call就是挨个传值,apply传一个数组,bind也是挨个传值

    call()方法第一个参数传的是一个你要借用的对象,第二个是实参,可以在对象之后依次传递

    fun.call(obj,2,3);
    apply()方法需要将实参封装到一个数组中统一传递
    fun.apply(obj,[2,3]);
    bind在调用的时候传参
    fun.bind(obj)(2, 3)

    深入理解:

    想要知道call是怎样被执行的,涉及到了原型链查找机制。

    fun.call(obj)

    其实是首先通过fun的原型链,找到Function.ptototype中的call方法,call方法中的this指向的就是fun,然后在执行call方法的时候,改变了this的指向,成了obj

     call、apply在数组中的巧妙用法

    //利用Math.max方法求数组中的最大值
        var arr = [2,20,30,34,50]
        console.log(Math.max.apply(null, arr)); // 50
        //让伪数组调用数组的方法
        function fn () {
            [].push.call(arguments,3)
            console.log(arguments);
        }
        fn(1,2,3) // [1,2,3,3]
        //让数组使用字符串的方法
        var arrs = ['abcabcasa']
        console.log(''.indexOf.call(arrs, 's'));// 7

    call源码实现

    先从例子开始解析call的实现

    var name = 'leah'
        var obj = {
            name: 'lihhh'
        }
        function test1 () {
            console.log(this.name);
        }
        test1.call(obj) //lihhh this指向obj
        test1.call(null) //leah this指向window

    在这个过程中,call修改 了this指向,并且执行了test1函数,那么接下来我们看看他是如何修改了this指向的

     在这个例子当中,将函数test2变成了obj里面的一个方法,然后再执行这个函数,这个函数里的this就指向了obj对象。所以我们只要在 thisArg里添加这个函数,然后在调用这个函数,执行后再删除这个函数即可。

    第一步:改变this指向

    Function.prototype.mycall = function (obj) {
            obj.fn = this //此时this就是fn
            obj.fn() //执行fn
            delete obj.fn //删除fn
        }

    通过在Function的原型上绑定mycall方法,这样所有函数就都可以访问到这个方法。

    将fn设置成obj对象的一个属性,并让它等于this。

    接着调用,此时this指向了obj。

    最后删除。不然会导致obj上的属性越来越多。

    例子:

    var name = 'leah'
        var obj = {
            name: 'lihhh'
        }
        function test1 () {
            console.log(this.name);
        }
        test1.mycall(obj) //lihhh this指向obj

    第二部:传参

    上面我们实现了一个简单的call函数,但是还不能传参,接着我们处理一下参数问题。
    我们知道有一个arguments参数表示传入的所有参数,不妨打印出来看看

     可以看到一共有四个,第一个是我们传入的对象,后面三个才是我们想要的参数。那怎么取出后面这三个呢,arguments是伪数组,不能直接使用数组方法,之前我们都是通过Array.prototype.splice.call(arguments),这样来使用,但现在我们正在是实现call方法,所以call还不能使用,那最简单的就是通过for循环来截取了,如下:

    Function.prototype.mycall = function (obj) {
            obj.fn = this //此时this就是fn
            obj.fn() //执行fn
            delete obj.fn //删除fn
            console.log(arguments)
            var args = []
            for(var i = 1; i < arguments.length; i++){
                args.push(arguments[i])
            }
            console.log(args) //[1,2,3]
        }
        var name = 'leah'
        var obj = {
            name: 'lihhh'
        }
        function test1 () {
            console.log(this.name);
        }
        test1.mycall(obj,1,2,3)

    此时截取到的是一个数组,但是数组不能作为参数传给函数,而应该是将数组里的元素分别传给函数,此时有个evel方法应该了解一下。

    eval可计算某个字符串,并执行其中的的 JavaScript 代码

    例如:

    eval("x=10;y=20;document.write(x*y)")
    
    document.write(eval("2+2"))
    
    var x=10
    document.write(eval(x+17))

    因此我们就可以利用eval来处理这个问题了,接着来改写我们的模拟函数

    Function.prototype.mycall = function (obj) {
            console.log(arguments)
            var args = []
            for(var i = 1; i < arguments.length; i++){
    //            args.push(arguments[i])
                args.push("arguments[" + i + "]");
            }
            console.log(args) //[1,2,3]
            obj.fn = this //此时this就是fn
            eval("obj.fn(" + args + ")"); //执行fn
            delete obj.fn //删除fn
        }
        var name = 'leah'
        var obj = {
            name: 'lihhh'
        }
        function test1 (a,b,c) {
            console.log(a + b + c + this.name);
        }
        test1.mycall(obj,"我的", "名字", "是")

    一共改了两个地方

    第一个是将 args.push(arguments[i])改成了args.push("arguments[" + i + "]");若是不这么写,得到的args数组就是[我的,名字,是]这样的形式,那eval执行的时候就会是表达式变成了eval("obj.fn(我的,名字,是)"),没有引号的话就相当于是一个变量,而不是字符串,那找不到这样的变量肯定就会报错。所以改成了args.push("arguments[" + i + "]"),args最终就是这个样子["arguments[1]","arguments[2]","arguments[3]"],当执行eval时,arguments[1]此时确实是作为一个变量存在不会报错,于是被eval解析成了一个真正的字符传递给了函数。

    还有一种方法就是不使用eval,使用new Function()方法来执行。只知道有这么一种方法,具体的还不知道怎么实现,以后补充

    newFunction ([arg1[, arg2[, ...argN]],] functionBody)

    简单例子:

    var sum = newFunction('a', 'b', 'return a + b');
    console.log(sum(2, 6));

    也可以用扩展运算符。[...arguments]替换了es5中的Array.prototype.slice.call(arguments)写法,改写后如下

    Function.prototype.mycall = function (obj) {var args = []
            for(var i = 1; i < arguments.length; i++){
                args.push(arguments[i])
    //            args.push("arguments[" + i + "]");
            }
            console.log(args) //[1,2,3]
            obj.fn = this //此时this就是fn
            obj.fn(...args); //执行fn
            delete obj.fn //删除fn
        }
        var name = 'leah'
        var obj = {
            name: 'lihhh'
        }
        function test1 (a,b,c) {
            console.log(a + b + c + this.name);
        }
        test1.mycall(obj,"我的", "名字", "是") //lihhh this指向obj
        test1.mycall(null,"我的", "名字", "是") //leah this指向window
        test1.mycall(undefined,"我的", "名字", "是") //leah this指向window

    这种方法比eval简单

    第三步:判断this特殊指向

    如果第一个参数传的null或者undefined,在非严格模式下,null和undefined都指向window。所以要判断。

    Function.prototype.mycall = function (obj) {
            //判断是否为null或者undefined
            var object = obj || window;
            var args = []
            for(var i = 1; i < arguments.length; i++){
                args.push(arguments[i])
            }
            console.log(args) //[1,2,3]
            object.fn = this //此时this就是fn
            object.fn(...args); //执行fn
            delete object.fn //删除fn
        }
     var name = 'leah'
        var obj = {
            name: 'lihhh'
        }
        function test1 (a,b,c) {
            console.log(a + b + c + this.name);
        }
        test1.mycall(obj,"我的", "名字", "是") //lihhh this指向obj
        test1.mycall(null,"我的", "名字", "是") //leah this指向window
        test1.mycall(undefined,"我的", "名字", "是") //leah this指向window

     apply源码实现

    Function.prototype.myapply = function (obj,arr) {
            var object = obj || window;
            object.fn = this
            if(!arr){
                object.fn()
            }else{
                let args = arr
                object.fn(...args); //执行fn
            }
            delete object.fn
        }
     test1.myapply(obj,["我的", "名字", "是"]) //lihhh this指向obj
     test1.myapply(null,["我的", "名字", "是"]) //leah this指向window
     test1.myapply(undefined,["我的", "名字", "是"]) //leah this指向window
     
    不积跬步无以至千里
  • 相关阅读:
    3.14周末作业
    3.13作业
    文件处理
    字符编码
    基本数据类型总结
    基本数据类型--------------------集合set()
    python入门009
    作业009
    python入门008
    作业008
  • 原文地址:https://www.cnblogs.com/lyt0207/p/12028481.html
Copyright © 2011-2022 走看看