前言
在全局上下文中,严格默认 this 为 undefined,非严格模式下,this 为 window。块级作用域的 this 是继承所在上下文中的 this 。在函数的私有上下文中,this 情况多种多样。
注意 this 并不是执行上下文,EC 是执行上下文,this 是执行主体。
1. 元素的事件
给元素的某个事件行为绑定方法,事件触发,方法执行,此时方法中的 this 一般都是当前元素本身。
// DOM0
btn.onclick = function anonymous() {
console.log(this); // this指向元素本省
}
// DOM2
btn.addEventListener('click', function anonymous() {
console.log(this); // this指向元素本省
}, false);
// DOM2-ie中
btn.attachEvent('onclick', function anonymous() {
// <= IE8
console.log(this); // this -> window 特殊情况
})
2. 普通函数执行
普通函数执行,它里面的 this 取决于执行方法前面是否有“点”,有“点”,“点”前面是谁,this 就是谁,前面没有“点”,非严格模式 this 是 window,严格模式是 undefined。
3. 构造函数执行
构造函数执行,this 执行当前类的实例。
function Func() {
this.name = "F";
console.log(this); //=>构造函数体中的THIS在“构造函数执行”的模式下,是当前类的一个实例,并且THIS.XXX=XXX是给当前实例设置的私有属性
}
Func.prototype.getNum = function getNum() {
// 而原型上的方法中的THIS不一定都是实例,主要看执行的时候,“点”前面的内容
console.log(this);
};
let f = new Func;
f.getNum();
f.__proto__.getNum();
Func.prototype.getNum();
4. 箭头函数
箭头函数中没自身的this,所用的 this 都是上下文中的 this 。
- 箭头函数没有this
- 箭头函数没有prototype
- 箭头函数没有constructor
- 箭头函数不能被new执行
- 箭头函数没有arguments,如果想用实参集合只能使用...args。
普通函数执行:
- 形成私有上下文 (和AO)
- 初始化作用域链
- 初始化THIS
- 初始化ARGUMENTS
- 形参赋值
- 变量提升
- 代码执行
箭头函数执行:
- 形成私有上下文 (和AO)
- 形参赋值
- 代码执行
- 代码执行的时候遇到 this 直接找上级上下文中的 this。
let obj = {
i: 0,
// func:function(){}
func() {
// THIS:OBJ
let _this = this;
setTimeout(function () {
// THIS:WINDOW 回调函数中的THIS一般都是WINDOW(但是有特殊情况)
_this.i++;
console.log(_this);
}, 1000);
}
};
obj.func();
5. call/apply/bind
call/apply
- 第一个参数就是改变 this 的指向,写谁就是谁,在非严格模式下,null/undefined 指向的是 window。
- call/apply 的唯一区别就是,传递参数不一样,apply 第二个参数是数组,call的参数是一个一个传递。
- call 的性能要比 apply 好一些(尤其是传递给函数的参数超过三个的时候)
bind
- call/apply都是改变this的同时就把函数执行了,但是bind不是立即执行函数,属于预先改变this和传递一些内容,利用的是柯理化的思想。
这里提到这个 call/apply/bind 在面试中也常也会被问到关于如何自己实现这三个方法,其实不难,只要理解这三个方法就可以手写他们了。
apply源码-手写apply源码
~(function(proto){
function apply(content, args) {
if (content === undefined || contetn === null) {
content = window;
} else {
content = Object(content);
}
content.$fn = this;
var res = content.$fn(...args);
delete content.$fn;
return res;
}
proto.apply = apply;
})(Function.prototype)
call源码-手写call源码
~(function(proto){
function call(content, args) {
if (content === undefined || contetn === null) {
content = window;
} else {
content = Object(content);
}
content.$fn = this;
var res = content.$fn(...args);
delete content.$fn;
return res;
}
proto.call = call;
})(Function.prototype)
bind源码-手写bind源码
~(function(proto) {
function bind(content) {
if(content === undefined || content === null) {
content = window;
}
// 获取实参集合
var args = [].slice.call(arguments, 1);
// 最后要执行的函数
var _this = this;
return function anonymous() {
var amArgs = [].slice.call(arguments, 0);
_this.apply(content, args.concat(amArgs));
}
}
proto.bind = bind;
})(Function.prototype);
~(function(proto) {
function bind(content = window, ...args) {
if (content === null) {
content = window;
}
return (...amArgs) => {
// 经测试apply的性能在三个参数以上是没有call好的
this.call(content, ...args.content(amArgs));
}
}
proto.bind = bind;
})(Function.prototype);