this 的绑定
当一个函数被调用时,会创建一个会话记录(有时也称为执行上下文)。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方式、传入的参数信息等。this
就是这个记录中的其中一个属性。
this
的值是在函数被调用时确定,而不是在函数被定义时确定。
默认绑定
非严格模式下,直接调用函数 this
绑定为全局对象(浏览器中为 window
)。
function foo() {
console.log(this);
}
foo(); // window
严格模式下,直接调用函数 this
绑定为 undefined
。
"use strict";
function foo() {
console.log(this);
}
foo(); // undefined
注意:
默认绑定,决定 this 绑定对象的并不是调用位置是否为严格模式, 而是函数体是否处于严格模式。函数体处于严格模式 this
绑定到 undefind
, 否则 this
会绑定到全局对象
function foo1() {
console.log(this);
}
(function () {
"use strict";
function foo2() {
console.log(this);
}
foo1(); // window
foo2(); // undefined
})();
隐式绑定
对象的方法调用中,this
绑定为调用该方法的对象。
const obj = {
a: 2,
foo() {
console.log(this.a);
},
};
function foo2() {
console.log(this.a);
}
var a = 1;
obj.foo2 = foo2;
obj.foo(); // 2
obj.foo2(); // 2
foo2(); // 1
对象属性引用链中只有最后一层在调用位置中起作用。
const obj = {
a: 1,
foo() {
console.log(this.a);
},
};
const obj2 = {
a: 2,
obj,
};
obj2.obj.foo(); // 1
注意:
当将对象的方法赋值给其他变量时,会发生隐式丢失
const obj = {
a: 1,
foo() {
console.log(this.a);
},
};
const obj2 = {
a: 2,
};
const foo2 = obj.foo;
obj2.foo = obj.foo;
var a = 3;
obj.foo(); // 1
obj2.foo(); // 2
foo2(); // 3
显示绑定
call 和 apply
使用 call
或者 apply
直接执行函数,this
绑定为第一个参数。
它们的区别是, call
除了第一个参数外,还可以传入多个参数作为被绑定函数的参数,而 apply
则是传入一个参数数组,数组内的元素作为被绑定函数的参数。
const obj = {
total: 1,
};
function sum(num1, num2) {
console.log(this.total + num1 + num2);
}
sum.call(obj, 2, 3);
sum.apply(obj, [2, 3]);
bind
bind 会返回一个新函数,这个函数的 this
绑定为 bind 调用时传递的第一个参数。
const obj = {};
function foo() {
console.log(this);
}
const bar = foo.bind(obj);
bar(); // obj
bind
可以解决隐式丢失的问题,因为 bind
的新函数无法重新绑定 this
(new 调用函数除外)。
const obj1 = {
a: 1,
};
const obj2 = {
a: 2,
};
const obj3 = {
a: 3,
};
function foo() {
// this.a = 4;
console.log(this.a);
}
const bar = foo.bind(obj1);
obj2.foo = foo;
obj3.foo = bar;
bar(); // 1
obj2.foo(); // 2
obj3.foo(); // 1
bar.call(obj2); // 1
new bar(); // undefined
bind
具有函数柯里化的功能。
function foo(a, b) {
console.log(this.num + a + b);
}
const obj = {
num: 1,
}
const bar = foo.bind(obj, 1)
bar(2) // 4
注意:
当 不需要使用this,而只是想使用 apply
的参数展开或者 bind
的柯里化功能时,可以使用 null
来进行占位。 不过此时的绑定是默认绑定,将 this
指向全局对象。
new 绑定
INFO
在 JavaScript 中,构造函数与传统面向对象语言有所不同。构造函数只是一些使用 new
操作符时被调用的函数。并不属于某个类,也不会实例化一个类。 他们甚至不能说是一种特殊的函数类型,它们只是被 new
操作符调用的普通函数而已。 实际上并不存在所谓的 “构造函数”,只有对于函数的 “构造调用”。
使用 new 调用函数,或者说发生构造函数调用时,会自动执行以下操作:
- 创建一个全新的对象
- 这个新对象会被执行[[Prototype]]连接
- 这个新对象会绑定到函数调用的
this
- 如果函数没有返回其他对象,那么
new
表达式中的函数会自动返回这个新对象
function foo(a) {
this.a = a;
}
const bar = new foo(2);
console.log(bar.a); // 2
箭头函数
箭头函数的 this
是根据外层(函数或者全局)作用域来决定的,会继承外层函数调用的 this 绑定。箭头函数的绑定无法被修改(new
也不行)
function foo() {
return () => {
// 继承foo的this
console.log(this.a);
};
}
const obj1 = {
a: 1,
};
const obj2 = {
a: 2,
};
const bar = foo.call(obj1);
bar.call(obj2); // 1
function fn1() {
console.log("fn1", this);
function fn2() {
console.log("fn2", this);
}
const fn3 = () => {
console.log("fn3", this);
};
fn2();
fn3();
new fn2();
}
const obj = { a: 1 };
fn1.call(obj);
// fn1 { a: 1 }
// fn2 window
// fn3 { a: 1 }
// fn2 {}