# This
# 分辨 this 指向
综述
- 在函数体中,非显式或隐式地简单掉用函数时,严格模式下,函数内的 this 指向 undefined;非严格模式下,指向全局对象 window/global 上。
- 通过上下文对象调用函数时,函数体内的 this 指向调用函数的对象。
- 通过 call/apply/bind 方法显式调用函数时,函数体内的 this 指向指定的参数对象上。
- 使用 new 方法调用构造函数时,构造函数内的 this 会指向新创建的对象上。
- 箭头函数中, this 指向由外层(函数或全局)作用域来决定。
1. 在函数体中,非显式或隐式地简单掉用函数时,严格模式下,函数内的 this 指向 undefined;非严格模式下,指向全局对象 window/global 上。
function fn1() {
console.log(this);
}
function fn2() {
"use strict";
console.log(this);
}
fn1(); // window
fn2(); // undefined
2. 通过上下文对象调用函数时,函数体内的 this 指向调用函数的对象。
const o1 = {
text: "o1",
fn: function () {
return this.text;
},
};
const o2 = {
text: "o2",
fn: function () {
return o1.fn();
},
};
const o3 = {
text: "o3",
fn: function () {
var fn = o1.fn;
return fn();
},
};
console.log(o1.fn());
console.log(o2.fn());
console.log(o3.fn());
// 输出1: o1对象调用自己的函数,输出 o1
// 输出2: o2调用fn,但内部实际仍是 o1调用自己的函数f,输出 o1
// 输出3: o3调用fn,内部 声明表达式将 o1.fn 赋值给了 fn,调用 fn的其实是 window,window没有 text,所以是 undefined
3. 通过 bind、call、apply 改变 this 指向
区别
- call 与 apply 是直接进行相关函数调用。fn1.call(target, args)
- call 与 apply 主要区别是传参形式不同,apply 参数是数组。
- bind 不执行函数,而是返回一个新的函数,this 已经指向了新的函数,需要开发者手动调用新函数才行。fn1.bind(target, args)()
// call
const target = {};
fn.call(target, "arg1", "arg2");
// apply
const target = {};
fn.apply(target, ["arg1", "arg2"]);
// bind
const target = {};
fn.bind(target, "arg1", "arg2")();
const foo = {
name: "foo",
logName: function () {
console.log(this.name);
},
};
const bar = {
name: "bar",
};
console.log(foo.logName.call(bar)); // bar
4. 使用 new 方法调用构造函数时,构造函数内的 this 会指向新创建的对象上。
function Foo() {
this.bar = "foo-bar";
}
const instance = new Foo();
console.log(instance.bar); // foo-bar
new 时做了什么?
- 创建一个新的对象
- 将构造函数的 this 指向新的对象
- 给这个对象添加属性、方法、原型链等
- 返回新的对象
new 一个构造函数过程如下:
var obj = {};
obj.__proto__ = Foo.prototype;
Foo.call(obj);
构造函数踩坑点
- 如果构造函数中显式的 return 了一个对象(复杂类型),this 就指向这个返回的对象
function Foo() {
this.user = "user";
const o = {
user: "o-user",
};
return o;
}
const instance = new Foo();
console.log(instance.user); // o-user, this 指向了返回的对象 o
- 如果构造函数返回的不是一个对象(返回基本类型),this 指向当前实例
function Foo() {
this.user = "user";
return 1;
}
const instance = new Foo();
console.log(instance.user); // user
# this 指向优先级
通常 call、apply、bind、new 这些对 this 进行绑定的情况称为 显式绑定;根据调用关系来确定 this 指向的情况称为隐式绑定。
综述
- call、apply、bind 优先级高于普通调用方式
- new 一个构造函数的方式优先级高于 call、apply、bind 的方式
- 箭头函数的 this 指向无法改变
另外,如果一个构造函数已经被 bind
方法改变过了 this 指向,再调用 call
方法也是无法改变 this 指向的。
所以,bind 方法的独特之处在于它创建了一个永久绑定的 this 值。
function greet() {
return `你好,我是 ${this.name}`;
}
let person1 = { name: "Alice" };
let person2 = { name: "Bob" };
// 创建一个与 `person1` 绑定的函数
let greetPerson1 = greet.bind(person1);
console.log(greetPerson1()); // 你好,我是 Alice
// 尝试使用 `call` 方法更改上下文;但是,它仍然使用 `person1` 作为 `this` 上下文
console.log(greetPerson1.call(person2)); // 你好,我是 Alice
// 相比之下,正常函数调用允许使用 `call` 方法设置 `this` 上下文
console.log(greet.call(person2)); // 你好,我是 Bob
1. call、apply、bind 优先级高于普通调用方式
function foo(a) {
console.log(this.a)
}
const obj1 = {
a: 1,
foo: foo
}
const obj2 = {
a: 2
foo: foo
}
obj1.foo.call(obj2) // 2
obj2.foo.call(obj1) // 1
2. new 一个构造函数的方式优先级高于 call、apply、bind 的方式
function foo(a) {
this.a = a;
}
const obj1 = {};
var bar = foo.bind(obj1);
bar(2);
console.log(obj1.a); // 2, bind 之后 obj1 就变成了 {a:xx} 的形式了
// new 开始调用
var baz = new bar(3);
console.log(baz.a); // 3
bar 函数通过 bind 方法生成了新函数,将 this 指向了 obj1。当新的函数被 new 调用后,返回的实例已经与 obj1 解绑了。也就是说,new 改变了 bind 的 this 指向
3. 箭头函数的 this 指向无法改变
function foo() {
return (a) => {
console.log(this.a);
};
}
const obj1 = {
a: "obj1",
};
const obj2 = {
a: "obj2",
};
const bar = foo.call(obj1);
console.log(bar.call(obj2)); // obj1
foo.call(obj1) 执行之后,bar 函数就是内部的箭头函数,它的 this 指向在函数上下文环境中是 obj1
后面虽然通过 call 绑定了 obj2,但是箭头函数的 this 指向不能改变,所以 this 仍然是 obj1
看个简单的
var a = "全局的a";
const foo = () => {
return (a) => {
console.log(this.a);
};
};
const obj1 = {
a: "obj1-a",
};
const obj2 = {
a: "obj2-a",
};
const bar = foo.call(obj1);
console.log(bar.call(obj2)); // 全局的 a
因为 foo 的 this 是 window,a 默认是 window 的属性。所以,就找到了全局的 a
给这个简单的,挖一个坑:
const a = "全局的a";
const foo = () => {
return (a) => {
console.log(this.a);
};
};
const obj1 = {
a: "obj1-a",
};
const obj2 = {
a: "obj2-a",
};
const bar = foo.call(obj1);
console.log(bar.call(obj2)); // undefined
因为,const 声明的变量不会挂载到 window 的全局变量上