首先需要说明的是,在 javascript 中,作用域和 this 绑定是两件事。
作用域
javascript 中的作用域是词法作用域(语法作用域、静态作用域)。词法作用域是在编译阶段就产生的,一整套函数内标识符的访问规则。也就是说,函数书写时在哪个作用域,使用时就能访问那个作用域的变量(直接通过变量名访问,而不是通过 this.identifierName
的方式访问)。
需要注意的是,箭头函数不会改变上述作用域规则,箭头函数会影响 this
的绑定规则。
验证代码如下:
let aa = 10;
function showInfo() {
console.log(aa);
}
const showInfo2 = () => {
console.log(aa);
};
function wrapper() {
let aa = 100;
function innerShowInfo() {
console.log(aa);
}
return innerShowInfo;
}
function wrapper2() {
let aa = 100;
const innerShowInfo = () => {
console.log(aa);
};
return innerShowInfo;
}
{
let aa = 1000;
setTimeout(showInfo, 1000); // 10
}
{
let aa = 1000;
setTimeout(showInfo2, 1000); // 10
}
{
let aa = 1000;
setTimeout(wrapper(), 1000); // 100
}
{
let aa = 1000;
setTimeout(wrapper2(), 1000); // 100
}
let obj = { bb: "this is obj", aa: 1000 };
showInfo.apply(obj); // 10 {bb: 'this is obj'}
showInfo2.apply(obj); // 10 Window
wrapper().apply(obj); // 100 {bb: 'this is obj', aa: 1000}
wrapper2().apply(obj); // 100 Window
this 绑定
在 es6 中函数有两种:通过 function
定义的函数(这里称为普通函数)、箭头函数。除非特殊指明,否则默认情况下我们说的函数指普通函数。
this
是函数发生调用时自动生成的一个量。this
不能被赋值。它代表的是调用这个函数的对象。在箭头函数中,这种情况为不同。箭头函数中没有 this
这个量。箭头函数中的 this
是依照词法作用域规则引用的父作用域的规则(按照此规则迭代直到找到一个对象)
当直接通过函数名调用(call)一个函数时,this
默认为 window(或者是 undefined 或其他值。不同引擎实现不一样)。
let aa = 10;
function showInfo() {
console.log(aa, this);
}
const showInfo2 = () => {
console.log(aa, this);
};
function wrapper() {
let aa = 100;
function innerShowInfo() {
console.log(aa, this);
}
return innerShowInfo;
}
function wrapper2() {
let aa = 100;
const innerShowInfo = () => {
console.log(aa, this);
};
return innerShowInfo;
}
{
let aa = 1000;
setTimeout(showInfo, 1000); // 10 window
}
{
let aa = 1000;
setTimeout(showInfo2, 1000); // 10 window
}
{
let aa = 1000;
setTimeout(wrapper(), 1000); // 100 window
}
{
let aa = 1000;
setTimeout(wrapper2(), 1000); // 100 window
}
let obj = { bb: "this is obj", aa: 1000 };
let obj2 = { bb: "this is obj2", aa: 1000, showInfo };
let obj3 = { bb: "this is obj3", aa: 1000, showInfo2 };
let obj4 = { bb: "this is obj4", aa: 1000, show: wrapper() };
let obj5 = { bb: "this is obj5", aa: 1000, show: wrapper2() };
showInfo.apply(obj); // 10 {bb: 'this is obj'}
showInfo2.apply(obj); // 10 Window
wrapper().apply(obj); // 100 {bb: 'this is obj', aa: 1000}
wrapper2().apply(obj); // 100 Window
obj2.showInfo(); // 10 {bb: 'this is obj2', showInfo: ƒ}
obj3.showInfo2(); // 10 Window
obj4.show(); // 100 {bb: 'this is obj4', aa: 1000, show: ƒ}
obj5.show(); // 100 Window
wrapper()(); // 100 Window
wrapper2()(); // 100 Window
let caller = {name: 'this is name'};
wrapper.call(caller)(); // 100 Window
wrapper2.call(caller)(); // 100 {name: 'this is name'}
修改 this 指向
js 中有两种方式可以改变函数的 this
指定,分别是 apply
和 call
。二者的区别在于给函数传参的方式不一样。apply
以数组的形式传参,call
以不定参数的方式传参。
需要注意的是:这两种方式都不会改变箭头函数的 this
指向。因为箭头函数是依照词法作用域查找 this
function test() {
console.log(arguments, this);
}
var name = "window name";
const test2 = ()=> {
console.log(this.name);
}
let caller = {
name: "caller"
}
// Arguments(2) ['01', '02', callee: ƒ, Symbol(Symbol.iterator): ƒ] {name: 'this is name'}
test.apply(caller, ['01', '02']);
// Arguments(2) ['01', '02', callee: ƒ, Symbol(Symbol.iterator): ƒ] {name: 'this is name'}
test.call(caller, '01', '02');
// window name
test2.apply(caller);
// window name
test2();