JavaScript中this概念
当前执行上下文(global、function 或 eval)的一个属性,
-
全局上下文,this总是指向window
-
函数上下文中,this取决于函数的调用方式。
- 在非严格模式下,如果没有设置this,那么this会指向全局的Window,而严格模式下,会指向undefined
function fn () { return this } fn() === window // true 非严格模式, 未设置this时,指向window function fn2 () { 'use strict' return this } fn2() === undefined // true 严格模式下 未设置this时,本身为undefined- 与this相关的函数
- 常见的隐式调用, 看调用函数时的主体,xxx.fn(), 一般情况下xxx是函数fn执行时的this
window.a = 'window-a' const obj = { a: 'obj-a', fn: function () { console.log(this.a) } } obj.fn() // 输出 obj-a- 箭头函数,用箭头函数声明中的this,不受调用方式影响,看声明当时的主体,就是执行中的this
window.a = 'window-a' const obj = { a: 'obj-a', fn: () => { console.log(this.a) } } obj.fn() // 输出 window-a-
call、apply改变this指向
window.a = 'window-a' const obj = { a: 'obj-a', fn: function () { console.log(this.a) } } const obj1 = { a: 'obj1-a' } obj.fn.call(obj1) // 输出 obj1-a, 那么把fn换成箭头函数,再使用call, 会改变this指向吗?可以试下~~ obj.fn.apply(obj1) // 也同样输出obj1-a, 那么apply与call有何区别呐? // 改造下函数 const obj = { a: 'obj-a', fn: function (...args) { console.log(this.a) console.log('args', args) } } const obj1 = { a: 'obj1-a' } obj.fn.call(obj1, '1', '2', '3', '4') obj.fn.apply(obj1, ['1', '2', '3', '4']) // 输出相同结果,不过apply支持数组传参,更便捷些, call参数要一个一个写- bind使用
window.a = 'window-a' const obj = { a: 'obj-a', fn: function () { console.log(this.a) } } const obj1 = { a: 'obj1-a' } const fn = obj.fn.bind(obj1) // 需要先绑定,返回可执行函数,返回的函数中this,指向bind第一个参数 fn() // 输出 obj1-a
常见this面试题及解析
window.a = 'window-a'
const obj = {
a: 'obj-a',
fn1: function () {
console.log('fn1:',this.a)
},
fn2: () => console.log('fn2:', this.a),
fn3: function () {
return function () {
console.log('fn3:', this.a)
}
},
fn4: function () {
return () => {
console.log('fn4:', this.a)
}
}
}
const obj1 = { a: 'obj1-a' }
obj.fn1(); // 输出 obj-a 隐式调用
obj.fn1.call(obj1); // 输出 obj1-a call 改变this指向
obj.fn2(); // 输出 window-a 箭头函数
obj.fn2.call(obj1); // 同样输出 window-a call不可改变箭头函数中的this指向
obj.fn3()(); // 输出 window-a fn3中返回的函数是, return function () {console.log('fn3:', this.a) } 再次执行时, 无前置的xxx主体,所以指向window
obj.fn3.call(obj1)(); // 输出 window-a call改变的是fn3中的this, 并不是执行后,返回函数中的this
obj.fn3().call(obj1); // 输出 obj1-a , call,改变了fn3执行后函数中的this
obj.fn4()(); // 输出 obj-a fn4中函数this指向obj, 箭头函数this也指向 fn4中的obj
obj.fn4.call(obj1)(); // 输出 obj1-a call 改变了fn4中函数this,指向obj1, 同时箭头函数也改变
obj.fn4().call(obj1); // 输出 obj-a call不可改变箭头函数中的this指向
手写call、apply、bind
call、apply、bind都是Function原型的函数,要写在Function.prototype上
// 手写bind
Function.prototype.myBind = function (_this, ...args) {
_this = _this || window
// this是当前可执行的函数
const fn = this
_this.fn = fn // 防止 fn函数名重复, 可以用Symbol生成唯一key, 用后再删
return function () {
_this.fn(...args)
}
}
// 手写 apply
Function.prototype.myApply = function (_this, args) {
_this = _this || window
// this是当前可执行的函数
const fn = this
_this.fn = fn // 防止 fn函数名重复, 可以用Symbol生成唯一key, 用后再删除
_this.fn(...args)
}
// 手写 call
Function.prototype.myCall = function (_this, ...args) {
_this = _this || window
// this是当前可执行的函数
const fn = this
_this.fn = fn // 防止 fn函数名重复, 可以用Symbol生成唯一key, 用后再删除
_this.fn(...args)
}