函数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之后的新函数