JS中 this 的五种情况
# 事件绑定
给元素的某个事件行为绑定方法,当事件触发方法执行,此时方法中的this一般都是当前元素本身。
<html>
<body>
<button id="btn">点我</button>
<script>
// DOM0事件绑定
btn.onclick=function() {
console.log(this) // <button id="btn">点我</button>
}
// DOM2事件绑定
btn.addEventListener('click', function() {
console.log(this) // <button id="btn">点我</button>
})
btn.onclick = function() {
handleClick();
};
function handleClick() {
// 这里的 this 指向 window
console.log(this); // window
}
</script>
</body>
</html>
我们可以通过 call、apply、bind 来改变this的指向。
btn.onclick = function() {
fn();
};
function fn() {
console.log(this); // window
}
btn.onclick = fn.bind(window); // fn.bind(window) 首先会先返回一个匿名函数,将这个匿名函数绑定给事件,点击按钮时触发这个匿名函数执行,这个函数中的 this 指的是元素,但是会在匿名函数中执行 fn,fn 中的 this 预先指定为 window了。
对于 Internet Explorer 来说,在IE 9之前的浏览器不支持DOM2事件绑定,必须使用 attachEvent来绑定事件,此时的this指向window
el.attachEvent('onclick', function() {
console.log(this) // window
});
# 普通函数执行
普通函数执行时,它里面的this是谁,取决于被谁调用,说白了就是看方法执行前面是否有点,有的话点前面是谁 this 就是谁没有的点的话 this 指向window,严格模式下指向undefined 。
function fn() {
console.log(this)
}
let obj = {
name: 'f',
fn: fn
}
fn() // window
obj.fn() // {name: "f", fn: ƒ}
// hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)
console.log(obj.hasOwnProperty('name')) // true
// 如果指定的属性在指定的对象或其原型链中,则in 运算符返回true
console.log('name' in obj); // true
console.log(obj.__proto__.hasOwnProperty('name')) //false
console.log(Object.prototype.hasOwnProperty.call(obj, 'name')); // true
hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)
如果指定的属性在指定的对象或其原型链中,则in 运算符返回true。
自执行函数执行,其中的 this 一般都是 window.
# 构造函数执行
构造函数执行,函数中的this是当前类的实例
function F() {
console.log(this)
}
let f = new F
# 箭头函数
箭头函数中没有 this,箭头函数中所用到的 this 是其上下文中的 this。
这里需要先明白执行上下文大概分为三种,分别为:全局执行上下文、函数执行上下文、eval执行上下文。
下面的代码 fn 使用的是箭头函数,它的执行上下文是全局上下文 ,所以 this 指向 window 。
const obj = {
fn: () => {
console.log(this) // window
}
}
obj.fn()
下面代码中定时器中使用箭头函数后,它的执行上下文指的是函数 fn,函数 fn 中的 this 指的是对象 obj, 所以使用箭头函数的定时器中的 this 指的是 obj,而没有使用箭头函数的的定时器中的 this 指的是 window,因为定时器方法实际是 window 对象调用的:window.setTimeout(fn, delay)。
const obj1 = {
name: 'f',
fn() {
setTimeout(function() {
console.log(this); // window
}, 0);
setTimeout(() => {
console.log(this) // {name: "f", fn: ƒ}
}, 0);
}
}
obj1.fn();
箭头函数中没有 prototype,不能当做构造函数用所以不能被 new 执行。
const FN = _ => {}
new FN(); // FN is not a constructor
箭头函数中还没有 arguments 实参集合。
const fn = _ => {
console.log(arguments);
};
fn() // arguments is not defined
如果箭头函数中想用实参,只能通过 ...args 剩余参数获取 。
const fn = (...args) => console.log(args); // [1, 2, 3]
fn(1, 2, 3);
# call、apply、bind
call、apply、bind 可以改变函数中 this 的指向。call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。该方法的语法和作用与 apply() 方法类似,只有一个区别,就是 call() 方法接受的是一个参数列表,而 apply() 方法接受的是一个包含多个参数的数组。
const obj = {
fn(){
console.log(this)
},
fn1() {
'use strict'
console.log('dd',this)
}
}
obj.fn() // obj
// 如果call没有传参,this默认指向 window,如果传 null、undefined 默认指向 window,严格模式下指向undefined,
obj.fn.call(); // window
obj.fn.call(null); // window
obj.fn.call(undefined); // window
// 严格模式下
obj.fn1.call(); // undefined
obj.fn1.call(null); // null
obj.fn1.call(undefined); // undefined
obj.fn.call(12); // Number {12}
// 严格模式下输出 12
obj.fn1.call(12); // 12
call、apply都是改变this的同时,直接把函数执行了。而bind不是立即执行,属于预先改变 this 和传递一些内容并返回一个函数。
function fn() {
console.log(this)
}
console.log(fn.call(window)); // undefined
console.log(fn.apply(window)); // undefined
console.log(fn.bind(window)); // ƒ fn
因为箭头函数没有 this 所以通过 call、apply、bind 改变 this 是没有意义的。
const obj = {
fn() {
return _ => console.log(this);
// return function() {
// console.log(this);
// };
}
}
obj.fn().call(window); // {fn: ƒ}
obj.fn().apply(window); // {fn: ƒ}
obj.fn().bind(window)(); // {fn: ƒ}
箭头函数也是 Function 的一个实例,箭头函数也有__proto__,所以可以调用call、apply、bind等方法。
Function 的原型通过原型链可以找到 Object。
# 手写 bind
# 手写 call
# 手写 apply
题目:
function fn1() {console.log(1)}
function fn2() {console.log(2)}
fn1.call(fn2) // 1
fn1.call.call(fn2) // 2
/*
第一次call执行
this => fn1.call
context => fn2
fn2.$fn() => fn1.call()
call第二次执行
this => fn2
context => undefined
context.fn() => fn2()
如果调用多个call执行的是fn2
*/
Function.prototype.call(fn1) // 没有输出
Function.prototype.call.call(fn1) // 1
/*
this => Function.prototype.call
context => fn1
fn1.$fn() = Function.prototype.call()
call第二次执行
this => fn1
context => undefined
undefined.$fn() => fn1()
*/
函数的原型 Function.prototype 是一个匿名函数