改变this指向:callapplybind

Posted 奥特曼 

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了改变this指向:callapplybind相关的知识,希望对你有一定的参考价值。

call、apply、bind都是用来改变this指向

1.call

语法:

    函数.call(newObj,参数1,参数2,...参数n)

    newObj: 要指向的this 当你不想改变this时 可以传null  undefined

    参数1..参数n: 函数自身需要的参数

        let fn = function (a, b) {
            console.log(this, a, b);
        }
        let obj = {
            name: "obj"
        };
        fn() //window undefined undefined
        fn.call(obj, 1, 2) // obj对象 1,2
        fn.call(this,1,2) // window 1,2
        fn.call({},1,2) // {} 1,2
        fn.call() // window undefined,undefined
        fn.call(window,1,2) // window 1,2
        fn.call(undefined,1,2) //window 1,2 
        fn.call(null,1,2) // window,1,2

fn.call(obj, 1, 2) this指向了第一个参数 obj对象,可以理解为 obj.fn()去调用这个函数

fn.call(this,1,2) 由于函数是在window下定义的 所以说就是指当前的this

对于下面几个花里胡哨的undefined、null   当这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象

fn.call中的call方法哪里来的?

回答这个问题非常简单  无非就是 控制台输出一下,mdn搜索一下

1. console.dir(fn) 

通过打印发现 fn函数中的prototype并没有call方法, 而在__proto__方法中有call方法,也就是通过原型链的查找机制去找到这个方法,

也就是在Function上的prototype上有一个call方法,再简单说也就是每个函数都有call、apply、bind方法

2.mdn 

    结论:   console.log(fn.__proto__.call===Function.prototype.call);  // true

使用场景 

① 继承:将父构造函数中的this指向改为子构造函数的this 

        function Father(uname, age) {
            this.uanme = uname;
            this.age = age;
        }

        function Son(uname, age) { //this 指向实例化对象

            // 就是把Father 的参数放到son中运行

            Father.call(this, 1, 1); //this 改为new Son 就变成了回调函数
            // Father();  //
        }
        let obj1 = new Son();
        console.log(obj1);

 下面两个例子来自 凡人进阶

  ② 判断变量的数据类型    Object.prototype.totString.call

console.log(Object.prototype.toString.call("1"))
//  [object String]
console.log(Object.prototype.toString.call(1))
//  [object Number]
console.log(Object.prototype.toString.call(false))
//  [object Boolean]
console.log(Object.prototype.toString.call([]))
//  [object Array]
console.log(Object.prototype.toString.call({}))
//  [object Object]
console.log(Object.prototype.toString.call(function(){}))
//  [object Function]

 Object的原型链上有一个属性叫toString特殊的方法,相当于一个特殊的function 就可以使用call方法,通过里面的参数就可以执行不同类型的this,Object.prototype.toString的返回值是字符串,并不是数组(只是看起来前后有[ ] )。它的返回结果是固定格式的:[object 当前对象的构造器的名字]。

③ 把类数组转成数组

slice

var 新数组 = 数组.slice(起点下标,终点下标)

截取 返回值是一个新数组,参数为包含起点不包含中点  如果不传值相当于进行一次浅拷贝

    <ul>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
    </ul>
    <script>
        const lis = document.getElementsByTagName('li')
        console.log(lis);
        const newLis = Array.prototype.slice.call(lis)
        console.log(newLis);
    </script>

当然 直接const newList = Array.from(lis) 更快哈! 

  2.apply 

apply方法和call方法的用法大致一样 只不过传递的参数方式不同 ,与call的区别就是 apply传递的是数组格式

语法:

    函数.call(newObj,[参数1,参数2,...参数n])

    newObj: 要指向的this  当你不想改变时可以写null、undefined  

    参数1..参数n: 函数自身需要的参数 

        let fn = function (a, b) {
            console.log(this, a, b);
        }
        let obj = {
            name: "obj"
        };

        fn.apply(obj,[1,2]) //obj对象 1,2
        fn.apply(window,[1,2]) //window undefined undefined
        fn.apply()//window undefined undefined

使用场景

添加数组

        const arr = [1,2,3]
        const arr2 = [4,5,6]
        arr.push.apply(arr,arr2)
        console.log(arr); [1,2,3,4,5,6]

 第一个参数执行了arr 把arr2中的数据添加到了arr中

拿到数组中最大值

        const arr = [1, 2, 3]
        console.log(Math.max.apply(null, arr));  //等价于Math.max(numbers[0], ...)

避开了使用循环的方式进行遍历判断 如果使用循环比较麻烦些

for (var i = 0; i < numbers.length; i++) {
  if (numbers[i] > max)
    max = numbers[i];
  if (numbers[i] < min)
    min = numbers[i];
}

3.bind

使用bind方法会创建一个新的函数, bind 语法和call一样,和call的区别在于 bind不会立即执行

返回值:返回一个原函数的拷贝,并拥有指定的 this 值和初始参数。

语法:

    函数.bind(newObj,参数1,参数2,...参数n)

    newObj: 要指向的this 当你不想改变this时 可以传null  undefined

    参数1..参数n: 函数自身需要的参数

        let fn = function (a, b) {
            console.log(this, a, b);
        }
        let obj = {
            name: "obj"
        };
        const newFn = fn.bind(obj, 1, 2)
        newFn()

知识点 一般情况下 我们有可能吧对象中的方法拿出来进行调用

        this.x = 9; // 在浏览器中,this 指向全局的 "window" 对象
        var module = {
            x: 81,
            getX: function () {
                return this.x;
            }
        };

        module.getX(); // 81

        var retrieveX = module.getX;
        console.log(retrieveX());//9

   当把函数拿到外面来时 已经变成了全局作用域下的函数 所以会打印出全局作用域上的x

var boundGetX = retrieveX.bind(module);
boundGetX(); // 81

再去指回原来的对象 

使用场景

 发送验证码

<input type="button" value="点击发送验证码">
	<script type="text/javascript">	
		let btn = document.querySelector('input');
		btn.addEventListener('click', function () {
			// 禁用元素
			this.disabled = true;
			window.setTimeout(function () {
				this.disabled = false;
			}.bind(this), 5000);
		});

	</script>

 看到定时器后面有一个改变this执行中的bind方法 如果使用call或者apply他会立即执行达不到5秒后再去关闭disable的效果

如果不加改变this指向 相当于给window上的 disable设为false了 最终永远不会触发当前的disable,只有通过bind(this)指向当前的btn才可以达到效果

总结

改变this指向
callapplybind
语法call:fun.call(this, arg1, arg2......);apply:fun.apply(this, [arg1, arg2......]);bind:fun.bind(this, arg1, arg2......);
相同点都是用来改变this指向
不同点第二个参数为任意类型第二个参数为数组类型第二个参数为任意类型
立即执行立即执行等待调用执行
没有返回值没有返回值返回一个改变this之后的函数

以上是关于改变this指向:callapplybind的主要内容,如果未能解决你的问题,请参考以下文章

245 改变函数内部 this 指向:call,apply,bind,callapplybind 三者的异同

2022前端面经---改变this指向问题(callapplybind)

2022前端面经---改变this指向问题(callapplybind)

JavaScript中的callapplybind方法的区别

JavaScript中的callapplybind是怎么回事?

Javascript方法callapplybind的解析与实现