this
this是 JavaScript 中一个动态绑定的关键字,它的值取决于函数的调用方式,而不是定义位置。
this是一个指针,指向调用函的对象。不同的场景下指向不同:
- 默认绑定:
this指向顶层对象window,在 Nodejs 环境下为global- 独立函数调用
- 匿名函数调用
- 隐式绑定:函数调用在某个对象方法上触发,
this指向运行时调用该方法的对象 - 显示绑定:使用
call、apply、bind等方法触发并显示的绑定this指向 new绑定:使用new关键字运行函数会首先创建一个空对象,并将this指向这个对象,详见构造函数
除了作为构造函数触发,可以归纳为永远指向调用自己的对象,直接运行实际是通过顶层对象调用。
默认绑定和隐式绑定
var a = {
b : {
m : function() {
console.log(this.p);
},
p : 'Hello'
}
};
// 作为对象方法触发,最后一层作为this指向
a.b.m() // hello
var hello = a.b.m;
hello() // undefined,hello函数通过对象赋值,但是执行的时候是作为独立函数执行,默认绑定到window
var p = "window p";
hello() // "window p"
function sayHi(){
console.log('Hello,', this.name);
}
var person1 = {
name: 'Rose',
sayHi: function(){
setTimeout(function(){
console.log('Hello,',this.name);
})
}
}
var person2 = {
name: 'Lee',
sayHi: sayHi
}
var name='Mike';
person1.sayHi(); // Hello Mike,setTimeout中的匿名函数this指向全局
setTimeout(person2.sayHi,100); // Hello Mike,person2.sayHi是通过setTimeout触发的,属于默认绑定(匿名函数调用)
setTimeout(function(){
person2.sayHi(); // Hello Lee,通过person2触发,属于隐式绑定
},200);
显示绑定
使用apply()、call()和bind()显示的设定this的指向
function.call(thisObj, arg1, arg2, ...)function.apply(thisObj, [arg1, arg2, ...])function.bind(thisObj, args1, args2, ...)
call和apply的作用一样会立刻执行方法,并将this指向传入的对象,唯一区别是方法参数的传入方式不一样,apply()是通过数组传入;
bind方法不会立刻执行,而是返回一个新的函数,函数的this指针绑定到传入的对象,如果bind方法的第一个参数是null或undefined,等于将this绑定到全局对象
var sum = 100;
function add(a, b) {
return this.sum + a + b;
}
add.call(this, 1, 2) // 103
var a = new Object();
a.x = 123;
a.toString = function(){
console.log(this.x);
}
a.toString();// 123
var b = new Object();
b.x = 345;
b.toString = a.toString;
b.toString();// 345
// 使用`bind`显示绑定的函数,即使通过对象触发也不会影响this的指向
b.toString = a.toString.bind(a);
b.toString();// 123;
对于不支持bind方法的浏览器我们可以自己实现
if(!('bind' in Function.prototype)){
Function.prototype.bind = function(){
if (typeof this !== "function") {
throw new TypeError("Bind must be called on a function");
}
const fn = this; // 保存原函数
// 将参数拆解为数组
const argArr = Array.prototype.slice.call(arguments);
// 数组第一项为运行时的this对象
const thisObj = argArr.shift();
// 返回一个函数
return function () {
return fn.apply(thisObj, argArr);
};
}
}
箭头函数中的this
和普通函数需要根据如何被调用来确定this的指向,箭头函数不会创建自己的this,会从自己的作用域链上一层来继承this,即箭头函数的this指向最近一层非箭头函数的this。
因为箭头函数的this指向是通过作用域链查找的,所以箭头函数的this指针并不是静态的。
var obj = {
hi: function() {
console.log(this);
return () => {
console.log(this);
}
},
}
let hi = obj.hi;
let deepHi = hi(); // window
deepHi(); // window
deepHi = obj.hi(); // obj {hi: f}
deepHi(); // obj {hi: f}
// 箭头函数的this为上一层非箭头函数hi的this,hi执行过程中this的变化会影响到箭头函数最终的this
注意:箭头函数this的另外一个重要特性是不可变,箭头函数的上下文绑定是不可改变的,不能使用call或apply改变,通过call或apply调用传入的this会被忽略。
TODO:深入 this
在变量声明、赋值中介绍过执行上下文,EC 在创建的时会绑定this指向。
不同类型的 EC 的this会绑定到不同的对象
- 全局执行上下文:绑定到全局对象
- 函数执行上下文:
- 如果是被某个对象调用的函数,那么
this指向到这个对象 - 否则指向到全局对象或
undefined(严格模式下)
- 如果是被某个对象调用的函数,那么