第一种:事件绑定
给元素的某个事件行为绑定方法,事件触发,方法执行,此时方法中的 this 一般都是当前元素本身;
DOM0 级事件
// DOM0
btn.onclick = function anonymous() {
console.log(this); // 元素
};
DOM2 级事件
// => DOM2:不兼容 IE6 7 8
btn.addEventListener('click', function anonymous() {
console.log(this); // 元素
}, false);
btn.attachEvent('onclick', function anonymous() {
// IE8 浏览器中的 DOM2 事件绑定
console.log(this); // window
});
第二种:上下文对象调用
普通函数执行,它里面的 this 取决于方法执行前面是否有 “点”,有 “点” 前面是谁 this 就是谁,没有则指向 window (严格模式下是 undefinde);
function fn() {
console.log(this);
}
let obj = { name: 'OBJ', fn: fn };
fn(); // window
obj.fn(); // { name: 'OBJ', fn: [Function: fn] }
第三种:构造函数执行
构造函数执行
new xxx
,函数中的 this 是当前类的实例
function Fn() {
console.log(this);
// this.xxx = xxx 是给当前实例设置私有属性
}
let f = new Fn;
第四种:箭头函数
箭头函数中没有自身的 this ,箭头函数的 this 指向始终为外层的作用域;
箭头函数没有的东西很多:
- 没有 prototype (也就是没有构造器),所以不能被 new 执行;
- 没有 arguments 实参集合 (可以基于 …args 剩余运算符获取);
let obj = {
name: 'OBJ',
fn: function () {
// console.log(this); //=>obj
let _this = this;
return function () {
// console.log(this); //=>window
_this.name = "旺旺";
};
}
};
let ff = obj.fn();
ff();
console.log(obj.name); // 旺旺
let obj = {
name: 'OBJ',
fn: function () {
// console.log(this); //=>obj
return () => {
// console.log(this); //=>obj
this.name = "旺旺";
};
}
};
let ff = obj.fn();
ff();
console.log(obj.name); // 旺旺
let obj = {
name: 'OBJ',
fn: function () {
setTimeout(_ => {
// console.log(this); //=>obj
this.name = "旺旺";
}, 10);
}
};
obj.fn();
console.log(obj.name); // OBJ
第五种:API 改变 this 指向
基于 call/apply/bind 可以改变函数中 this 的指向(强行改变),call、apply、bind 是 Function.prototype 上的方法;
func.call(context, 10, 20):context 为改变的 this 指向(非严格模式下,传递 null/undefined 指向的也是 window);
call
Function.prototype.call = function call(context = window, ...args) {
context === null ? context = window : null;
//=> 此处 this 为 apply 前的函数,也就是调用 apply 的函数
//=> 把函数赋值给需要指定 this 的对象 context
context.$fn = this;
let result = context.$fn(...args); // 这时函数执行 this 就变成了 context
delete context.$fn;
return result;
}
apply
Function.prototype.apply = function apply(context = window, args) {
context === null ? context = window : null;
//=> 此处 this 为 apply 前的函数,也就是调用 apply 的函数
//=> 把函数赋值给需要指定 this 的对象 context
context.$fn = this;
let result = context.$fn(...args); // 这时函数执行 this 就变成了 context
delete context.$fn;
return result;
}
bind
bind 不是立即执行函数,属于预先改变 this 和传递一些内容
// es5 版本
Function.prototype.bind = function bind(context) {
context = context || window;
// 获取传递的实参集合
var args = [].slice.call(arguments, 1);
var _this = this;
// 需要最终执行的函数
return function anonymous() {
// 此函数的参数
var amArg = [].slice.call(arguments, 0);
_this.apply(context, args.concat(amArg));
};
}
// es6 版本
Function.prototype.bind = function bind(context = window, ...args) {
// 经过测试:apply 的性能不如 call(3 个参数以上)
return (...amArg) => this.call(context, ...args.concat(...amArg));
}
面试题
阿里原题
function fn1() {
console.log(1);
}
function fn2() {
console.log(2);
}
// fn2.$fn = fn1;
// fn2.$fn() => fn2.fn1();this 指向 fn2,执行 fn1,输出 1
fn1.call(fn2); // 1
// 把 fn1.call 看成 AF0,AF0.call(fn2)
// fn2.$fn() => fn2.AF0(); fn1.call 和 fn1.call.call 都是同一个 call 函数
// 则 AF0 可以看成 call 函数,即 fn2.call 执行,this 为 window,输出 2
fn1.call.call(fn2); // 2
// 同上
Function.prototype.call.call(fn2); // 2
剑指 Offer 53 - II.0~n-1中缺失的数字
上一篇