this关键字
定义
this
关键字被自动定义在所有函数的作用域中,只能在函数内部使用,指向调用它的对象。
绑定规则
默认绑定
直接使用不带任何修饰符的函数引用进行调用;严格模式下,不能将全局对象用于默认绑定,this会绑定到undefined
,只有函数运行在非严格模式下,默认绑定才能绑定到全局对象。
js
// 非严格模式
function foo() {
console.log(this.a);
}
var a = 18;
foo(); // 18
// 严格模式
"use strict"
function foo() {
console.log(this.a);
}
var a = 18;
foo(); // VM10846:3 Uncaught TypeError: Cannot read properties of undefined (reading 'a') at foo
// 虽然this的绑定规则完全取决与调用位置,但是只有foo()运行在非严格模式下时,this才能默认绑定到全局对象
// 在严格模式下调用foo()不影响默认绑定
function foo() {
console.log(this.a);
}
var a = 18;
(function() {
"use strict"
foo(); // 18
})();
隐式绑定
当函数引用有上下文对象时,隐式绑定规则会把函数调用的this
绑定到这个上下文对象。
js
function foo() {
console.log(this.a);
}
var obj = {
a: 18,
foo,
fn: {
foo
}
}
obj.foo(); // 18
// 对象属性引用链中只有上一层(最后一层)在调用位置中起作用
obj.fn.foo(); // undefined
// 此时this指向的是window,
// this永远指向的是最后调用它的对象,虽然foo是对象obj的方法,但是obj.foo赋值给fooFn时候并没有执行,所以最终指向window
var fooFn = obj.foo;
fooFn(); // undefined
显式绑定
通过apply()、call()、bind()
函数改变函数的调用对象。
js
function foo(param1, params2) {
console.log(this.a, param1, params2);
}
var obj = {
a: 18
}
// call函数第一个参数要绑定的对象(没传、传null或undefined指向全局对象),后面的参数为foo执行的参数,临时改变,立即执行一次
foo.call(obj, 1, 2); // 18 1 2
// apply函数第一个参数要绑定的对象(没传、传null或undefined指向全局对象),后面的参数为foo执行的参数(数组形式),临时改变,立即执行一次
foo.apply(obj, [1,2]); // 18 1 2
// bind函数第一个参数要绑定的对象(没传、传null或undefined指向全局对象),后面的参数为foo执行的参数(可不传或传全部、部分参数),永久改变,返回一个新函数,不会执行
var fooFn = foo.bind(obj, 1);
fooFn(2); // 18 1 2
// 手写bind
Function.prototype.myBind = function (context) {
// 防止非函数调用
if (typeof this !== "function") {
throw new TypeError("Error");
}
// 获取参数
const bindArgs = [...arguments].slice(1);
const originalFn = this;
return function Fn() {
// 通过new调用时,this指向新创建的对象
// 普通调用时,this指向context
// 合并bind参数和调用时传入的参数
return originalFn.apply(this instanceof Fn ? new originalFn(...arguments) : context, bindArgs.concat(...arguments));
}
}
new绑定
通过构建函数new
关键字生成一个实例对象,此时this
指向这个实例对象
js
function foo(a) {
this.a = a;
}
var obj = new foo(18);
console.log(obj.a); // 18
// 手动实现new操作符的功能
function mynew(Func, ...args) {
// 1、创建一个空对象
const obj = {};
// 2、设置原型,将对象的隐式原型指向函数的prototype对象
Object.setPrototypeOf(obj, Func.prototype);
// 3、改变this指向,执行构造函数的代码(为新对象添加属性和方法)
let result = Func.apply(obj, args);
// 4、返回 如果构造函数没有return或者返回的是简单数据类型,直接返回创建的对象
// 如果return的是引用类型,就返回这个引用类型的对象。
return result instanceof Object ? result : obj;
}
优先级
new绑定优先级 > 显示绑定优先级 > 隐式绑定优先级 > 默认绑定优先级
js
// 显式绑定 > 隐式绑定
function foo() {
console.log(this.a);
}
var obj1 = {
a: 18,
foo
}
var obj2 = {
a: 19,
foo
}
obj1.foo(); // 18
obj2.foo(); // 19
obj1.foo.call(obj2); // 19
obj2.foo.call(obj1); // 18
js
// new绑定 > 隐式绑定
function foo(a) {
this.a = a;
}
var obj1 = {
foo: foo
};
var obj2 = {};
obj1.foo(18);
console.log(obj1.a); // 18
obj1.foo.call(obj2, 19);
console.log( obj2.a ); // 19
var bar = new obj1.foo(20);
console.log( obj1.a ); // 18
console.log( bar.a ); // 20
js
// new绑定 > 显式绑定
function foo(a) {
this.a = a;
}
var obj1 = {};
var bar = foo.bind(obj1);
bar(18);
console.log(obj1.a); // 18
var baz = new bar(19);
console.log(obj1.a); // 18
console.log(baz.a); // 19
this的判断
- 是否由new调用?绑定到新创建的对象上
- 由call、applay、bind调用?绑定到指定的对象
- 由上下文对象调用?绑定到上下文对象
- 默认:在严格模式下绑定到undefined,否则绑定到全局对象
箭头函数的this
箭头函数的this
指向是在函数定义时绑定的,而不是在执行过程中绑定的。这意味着箭头函数内部的this
值是在它被创建的上下文中确定的,而不是在它被调用的时候确定的,并且无法修改。
js
const obj = {
sayThis: () => {
console.log(this);
}
};
obj.sayThis(); // window
const globalSay = obj.sayThis;
globalSay(); // window
function foo() {
return (a) => {
// this继承自foo()
console.log(this.a);
}
}
var obj2 = {
a: 18,
foo
}
var doFoo1 = obj2.foo();
doFoo1(); // 18
var doFoo2 = foo();
doFoo2(); // undefined