JS 中的 this
在 JS
中,this
的指向是调用时决定的,在绝大多数情况下,函数的调用方式决定了 this
的值。
this
对象是是执行上下文中的一个属性,它指向最后一次调用这个方法的对象,在全局函数中,this
等于 window
,而当函数被作为某个对象调用时,this 等于那个对象。 在实际开发中,this
的指向可以通过四种调用模式来判断。
-
函数调用,当一个函数不是一个对象的属性时,直接作为函数来调用时,
this
指向全局对象,严格模式下指向undefined
。 -
方法调用,如果一个函数作为一个对象的方法来调用时,
this
指向这个对象。 -
构造函数调用,
this
指向这个用new
新创建的对象。 -
第四种是
apply 、 call 和 bind
调用模式,这三个方法都可以显示的指定调用函数的 this 指向。apply
接收参数的是数组,call
接受参数列表,bind
方法通过传入一个对象,返回一个this
绑定了传入对象的新函数。这个函数的this
指向除了使用new
时会被改变,其他情况下都不会改变。
new
-
首先创建了一个新的空对象
-
设置原型,将对象的原型设置为函数的
prototype
对象。 -
让函数的
this
指向这个对象,执行构造函数的代码(为这个新对象添加属性) -
判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象。
调用位置
调用位置是函数在代码中被调用的位置,而不是声明的位置 ----- 分析调用栈
function baz() {
console.log("baz");
bar();
}
function bar() {
console.log("bar");
foo();
}
function foo() {
console.log("foo");
}
baz();
当调用 baz()
时,调用链为baz() -> bar() -> foo()
foo()
的调用位置在bar()
中;
bar()
的调用位置在baz()
中;
baz()
的调用位置在全局作用域中。
全局上下文
在全局执行上下文中this
都指代全局对象。
-
this
等价于window
对象; -
var
=this.
=window
console.log(window === this); // true
var a = 1;
this.b = 2;
window.c = 3;
console.log(a + b + c); // 6
在浏览器
中,this
等价于window
对象,如果声明全局变量,则这些变量都会作为this
和window
的属性。
函数上下文
在函数内部,this
的值取决于函数被调用的方式。
1. 直接调用
this
指向全局变量。
function foo() {
return this;
}
console.log(foo() === window); // true
2. call()、apply()
this
指向被绑定的对象。
var person = {
name: "B1ackZz",
age: 20
};
function saySome(job) {
console.log(`${this.name}:${this.age} ${job}`)
}
saySome.call(person, "student"); // B1ackZz:20 student
saySome.apply(person, ["student"]); // B1ackZz:20 student
定义一个saySome
函数输出name, age, job
,该函数本身没有name
和age
属性,将这个函数利用call()
和apply()
绑定到person
对象上,从而让this
指向对象person
。
如果传入一个原始值(String, Boolean, Number
)来作为this
的绑定对象,这个原始值会转为其对象形式(new String()
),这个过程通常称为“装箱”。
call
和apply
从this
的绑定角度上来说是一样的,唯一不同的是它们的第二个参数。
3. bind()
this
将永久地被绑定到bind
的第一个参数,类似于call
和apply
。
var person = {
name: "B1ackZz",
age: 20
};
function saySome() {
console.log(`${this.name}: ${this.age}`);
}
var f = saySome.bind(person);
console.log(f()); // B1ackZz: 20
4. 箭头函数
所有的箭头函数都没有自己的this
,会捕获其所在上下文的this
值,作为自己的this
值。
function Person(name) {
this.name = name;
this.say = () => {
let name = "bbb";
return this.name;
}
}
var person = new Person("B1ackZz");
console.log(person.say()); // B1ackZz
箭头函数常常用于回调函数中,如定时器:
function foo() {
setTimeout(() => {
console.log(this.a);
}, 100);
}
var obj = {
a: 2
}
foo.call(obj); // 2
5. 作为对象的一个方法
this
指向调用函数的对象。
var name = "aa"
var person = {
name: "B1ackZz",
sayName: function() {
return this.name;
}
}
console.log(person.sayName()); // B1ackZz
var sayName = person.sayName();
console.log(sayName()); // aa, 因为这里的调用者是window.sayName()
6. 作为构造函数
this
指向new
创建的对象上。
function Person(name) {
this.name = name;
this.age = 20;
this.say = () => {
console.log(`${this.name}: ${this.age}`);
}
}
var person = new Person("B1ackZz");
console.log(person.name); // B1ackZz
person.say(); // B1ackZz: 20
7. 作为一个 DOM 事件处理函数
this
指向触发事件的元素,即事件处理程序所绑定到的 DOM 节点。
var el = document.getElementById("id");
el.addEventListener("click", function(e) {
console.log(this);
console.log(this === e.target); // true
})
HTML 标签内联事件处理函数
this
指向所在的 DOM 元素。
<button onclick="console.log(this);">Click</button>
总结
如果要判断一个运行中函数的 this
绑定, 就需要找到这个函数的直接调用位置。 找到之后就可以顺序应用下面这四条规则来判断 this
的绑定对象。
-
new
调用:绑定到新创建的对象,注意:显示return
函数或对象,返回值不是新创建的对象,而是显式返回的函数或对象。 -
call
或者apply
( 或者bind
) 调用:严格模式下,绑定到指定的对象。 -
对象上的函数调用:绑定到那个对象。
-
普通函数调用: 在严格模式下绑定到
undefined
,否则绑定到全局对象。
ES6 中的箭头函数:不会使用上文的四条标准的绑定规则, 而是根据当前的词法作用域来决定。