函数this的指向

简单一句话概括就是:

调用 我, 我就指向谁
小技巧:
没点没new是window, 有new是实例,有点是点左边的对象

1.普通函数: 函数名( )		this==>window 
2.构造函数:	new 函数名( ) 		this==>new创建实例
3.对象方法:	对象名.方法名( ) 		this ==> 对象

默认情况下,函数内部的this是固定的,无法动态修改,如果想要动态修改函数this指向,则需要使用函数上下文调用.
那么接下来我们就来讲讲怎么动态修改函数this指向

函数上下文调用: 动态修改this指向

函数上下文三个方法: call( ) ,apply( ) ,bind( ),他们定义在Function构造函数的原型中
如果将this指向修改为值类型:(number,boolean.string),则函数会自动帮我们包装成对应的引用类型(基本包装类型)
值类型: 123,1,true
基本包装类型; String('123'),Number(1),Boolean(true)
下面咱们来说说三种方法的使用:

call()方法

语法: 函数名.call(修改的this,参数1,参数2....)
函数名.call(修改后的this) 加了call第一个参数一定是修改this
示例:

    function fn(a, b) {
      console.log(a + b);
      console.log(this);
    }
    //函数名.call(修改后的this)   加了call第一个参数一定是修改this   
    fn.call({ name: '张三' }, 10, 20)

输出的结果为: 30 , {name:张三}

call的应用场景

场景一: 伪数组转真数组
伪数组: 有真数组的三要素(长度,下标,元素),但是没有数组的方法
伪数组的本质是对象
如果希望伪数组也可以调用数组的方法(排序,拼接),就需要把伪数组转成真数组
1.slice 可以查询数组,默认情况下不传参这个方法会得到数组本身
2.但是伪数组由于原型不是Array,所以无法调用slice
3.slice方法存储在哪里? : Array.prototype

let weiArr = {
0:88,
1:20,
2:50,
3:60,
length:4
}

//转真数组
weiArr = Array.prototype.slice.call(weiArr)


//ES6新增了专门用于伪数组转真数组的方法:  Array.from(伪数组)

let arr = Array.from(weiArr)

输出结果为:
在这里插入图片描述
场景二:万能数据检测
1.typeof 数据 : 有两种数据类型无法检测
null数组 无法检测,结果都为object
2.解决方案: 万能数据类型检测
Object.prototype.toString.call(数据)
示例:

    //值类型
    let str = 'abc'
    let num = 123
    let bol = true
    let und = undefined
    let nul = null
    //引用类型
    let arr = [10, 20, 30]
    let fn = function () { }
    let obj = { name: '张三' }

    console.log(typeof str);//string
    console.log(typeof num);//number
    console.log(typeof bol);//boolean
    console.log(typeof und);//undefined
    console.log(typeof nul);//object
    console.log(typeof arr);//object
    console.log(typeof fn);//function
    console.log(typeof obj);//object

    //Object.prototype.toString() 返回固定格式字符串 '[object  数据类型]'  这个的this指向的是Object 所以用call修改this指向 就可以检测类型了
    console.log(Object.prototype.toString.call(str));//String
    console.log(Object.prototype.toString.call(num));//Number
    console.log(Object.prototype.toString.call(bol));//Boolean
    console.log(Object.prototype.toString.call(und));//Undefined
    console.log(Object.prototype.toString.call(nul));//Null
    console.log(Object.prototype.toString.call(arr));//Array
    console.log(Object.prototype.toString.call(fn));//Function
    console.log(Object.prototype.toString.call(obj));//Object

apply( ) 方法

语法: 函数名(修改的this,数组/伪数组) 传参的值必须是放在数组或伪数组里
示例:

  function fn(a, b) {
      console.log(a + b);
      console.log(this);
    }
  //函数名.apply(修改后的this,数组或伪数组)
    //apply会自动帮你遍历数组,然后按照顺序一一传参
    fn.apply({ name: '李四' }, [40, 50])

输出结果为: 90 ,{name:‘李四’}

应用场景
** 场景一:伪数组转真数组**
示例:

	let weiArr = {
	0:88,
	1:20,
	2:50,
	3:60,
	length:4
	}
	let arr = []
 	//借助 apply自动遍历数组/伪数组  逐一传参特点
   //这里不需要修改this,知识借助apply传参的特点, this指向原来是谁,还是设置谁
   arr.push.apply(arr, obj)
   console.log(arr);

场景二:求数组最大值
示例:

	let arr = [20, 52, 60, 100, 30]
 	 //这里使用apply只是借助传参的特点,this指向不用修改,还是原来的this
    let max1 = Math.max.apply(Math, arr)
    console.log(max1);
    
       //ES6中有更简单的方式可以求数组最大值
    // ... 展开运算符,自动遍历数组传参(类似于apply传参特点)
    let max2 = Math.max(...arr)
    console.log(max2);

bind( ) 方法

语法: 函数名.bind(修改的this) 括号里一般不传值参,传参会把参数定死,后面就不能传参了
bind一般用于修改: 定时器,事件处理函数
示例:

function fn(a, b) {
      console.log(a + b);
      console.log(this);
    }
//函数名bind(修改后的this)
//bind不会立即执行函数,而是返回一个修改this之后的新函数 (相当于复制了一份,修改了this指向)
let newFn = fn.bind({ name: '王五' })
 newFn(100, 200)

应用场景一:修改定时器this指向
定时器中的this:默认指向window,不管是套对象还是什么,都是window,只能通过bind修改
示例:

 setTimeout(
      function () {
        console.log(this);
      }.bind({ name: '1111' }), 2000
    )

面试题: call( ) apply( ) bind( ) 三者异同点

相同点:
作用一致,都是修改this指向
不同点:
1.传参方式不同:call是一个一个传参,apply是数组/伪数组传参
2.执行机制不同: call和apply是立即执行函数,bind不会立即执行,而是返回一个修改this之后的新函数

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐