什么是this
this是JS关键字,代表的是你当前函数的上下文对象,因此this实际上指向的是其被调用的位置,而不是其词法作用域;是包含它的函数作为方法被调用时所属的对象。所以说,调用方式的不同,实际上最后呈现的结果也是不一样的。
如何判断this指向
- 普通函数调用/直接调用,this 指向的是全局 window 对象。
var name = 'window'
function fun() {
var name = 'funcion'
console.log(this.name)
}
fun() // window
- 对象方法调用,this 指向该对象(前边谁调用 this 就指向谁)。
var name = 'window'
var obj = {
name: 'obj',
fun() {
console.log(this.name)
},
handle: {
name: 'handle',
handlefun() {
console.log(this.name)
}
}
}
obj.fun() // obj
obj.handle.handlefun() // handle
- 作为构造函数调用,this 永远被绑定在新创建的对象实例上,任何方式都改变不了 this 的指向。
function newObj(name){
this.name = name
console.log(this) // {name: '林'}
// return this
}
var result = new newObj('林')
调用new操作符,会执行的一系列操作,请查看(还未完成)----我理解的new操作符创建实例过程
- 箭头函数的this
由于嵌套函数中的 this 不会从外层函数中继承,那么有两种解决办法:
第一种是把 this 保存为一个 self 变量,再利用变量的作用域机制传递给嵌套函数。
第二种是继续使用 this,但是要把嵌套函数改为箭头函数,因为箭头函数没有自己的执行上下文,所以它会继承调用函数中的 this。
我们直接对比箭头函数和普通函数的区别:
- 箭头函数是匿名函数,不能作为构造函数,不能使用new
- 箭头函数不能绑定arguments,取而代之用rest参数...解决
function test(arg){
console.log(arguments)
}
test(1,2,3) // [1,2,3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
let fun = (...arg) => {
console.log(arg);
}
fun(4,5,6) // [4,5,6]
- 箭头函数没有原型属性
var test = ()=>{
return 1
}
function fun(){
return 2
}
console.log(test.prototype) // undefined
console.log(fun.prototype) // {constructor: ƒ}
- 箭头函数中没有this绑定,必须通过查找作用域链来决定其值。如果箭头函数被非箭头函数包含,则this绑定的是最近一层非箭头函数的this,否则this的值则被设置为全局对象。并且无法改变。
var name = 'window';
var test = {
name: 'inner',
fun: function(){
var innerFun = () => {
console.log(this.name)
}
innerFun()
},
fun2: () => {
console.log(this.name);
}
}
test.fun() // 'inner'
test.fun2() // 'window'
- 箭头函数通过 call()或apply() 方法调用一个函数时,只传入了一个参数,对 this 并没有影响。
const obj = {
a: 10
}
var test = (...arg)=>{
console.log(arg)
}
test.call(obj, 10) // [10]
- setTimeout的this
setTimeout(handleFun, time);定时器第一个参数传入一个处理函数,该函数的this会被改变指向window
var name = 'window'
const obj = {
name: 'inner',
fun1() {
console.log('fun1===>', this.name)
},
fun2() {
setTimeout(function() {
console.log('fun2===>', this.name)
}, 500)
},
fun3() {
setTimeout(() => {
console.log('fun3===>', this.name)
})
}
}
obj.fun1() // inner
obj.fun2() // window
obj.fun3() // inner
如何修改this指向
- call 方法
fun.call(thisArg, arg1, arg2, ...)
thisArg为要指向的对象,后面为要传入的参数值;改变this后立即执行
var name = 'window'
const obj = {
name: 'obj'
}
function test(...args) {
console.log(args)
console.log(this.name)
}
test(1,2,3) // [1,2,3] window
test.call(obj, 1, 2, 3) // [1,2,3] obj
- apply 方法
fun.apply(thisArg[, arg1[, arg2[, ...]]])
thisArg为要指向的对象,后面为要传入的参数数组;改变this后立即执行
var name = 'window'
const obj = {
name: 'obj'
}
function test(...args) {
console.log(args)
console.log(this.name)
}
test(1,2,3) // [1,2,3] window
test.apply(obj, [1,2,3]) // [1,2,3] obj
- bind 方法
fun.bind(thisArg[, arg1[, arg2[, ...]]])
thisArg为要指向的对象,后面为要传入的参数值;改变this后生成新函数;调用函数时this指向thisArg
var name = 'window'
const obj = {
name: 'obj'
}
function test() {
console.log(arguments)
console.log(this.name)
}
test(1,2,3) // [1,2,3] window
const newFun1 = test.bind(obj, [1,2,3])
const newFun2 = test.bind(obj, 4,5,6)
newFun1()
newFun2()
-
call、apply、bind 三者的区别是什么
共同点:- 都能改变 this 指向,第一个传递的参数都是 this 指向的对象。
- 三者都采用的后续传参的形式。
不同点:
- call的传参是单个传递的,apply后续传递的参数是数组形式,而bind没有规定,传递值和数组都可以。
- call和apply函数是立即执行,而bind函数会返回一个函数,随时调用。