this实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。
function baz() {
console.log('baz');
bar();
console.log('baz-end');
}
function bar() {
console.log('bar');
foo();
console.log('bar-end');
}
function foo() {
console.log('foo');
console.log('foo-end');
}
baz();
// 理解栈调用:后进先出
// 输出顺序:baz -> bar -> foo -> food-end -> bar-end -> baz-end
第一条规则:在全局作用域下调用,this绑定的是全局window对象,如果在严格模式下,this绑定的是undefined
实例一:
var name = "bob";
function getName() {
var name = "lynn";
console.log(this); // this指向的是window对象
console.log(this.name); //bob
}
getName();
实例二:
// 严格模式下
"use strict";
var name = "bob";
function getName() {
var name = "lynn";
console.log(this); // this指向的是 undefined 对象
console.log(this.name); // TypeError Cannot read property 'name' of undefined
}
getName();
实例三:
var name = "lynn";
function getName() {
console.log(this.name);
}
var obj1 = {
name: 'peter',
getName: getName
}
var getUserName = obj1.getName;
getUserName(); // 输出:lynn,this指向的是全局对象
// 原因:getUserName实际上是保存着是getName函数的指针,调用的环境是全局对象
实例四:
var name = "lynn";
function getName() {
console.log(this.name);
}
function getUserInfo(fn) {
// fn
fn()
}
var obj1 = {
name: 'bob',
getName: getName
}
getUserInfo(obj1.getName) // lynn
// 同样getUserInfo调用的环境是全局对象中,this绑定的是全局对象
第二条规则:调用位置是否有上下文对象,如果包含多个对象引用链,那this指向是最后一个上下文对象。
实例一:全局下调用函数 与 对象中的调用函数
var name = "lynn"
function getName() {
console.log(this.name);
}
var obj1 = {
name: 'bob',
getName: getName
}
getName(); // 输出:lynn, this指向的是全局对象
obj1.getName(); // 输出:bob, this绑定的是 o 对象
实例二:多个对象嵌套调用时,取决于最后一个对象的绑定
var name = "lynn"
function getName() {
console.log(this.name);
}
var obj2 = {
name: 'peter',
getName: getName
}
var obj1 = {
name: 'bob',
obj2: obj2
}
obj1.obj2.getName(); // 输出:peter,this绑定的是 obj2 对象
第三条规则:是否是否call(),apply(),bind()方法显式绑定,如果是的话,this绑定的是指定的对象
实例一:call绑定
var name = "bob";
var o = {name: "lynn"};
function getName() {
console.log(this.name);
}
getName.call(o); // 输出:lynn,this绑定的是 o 对象
实例二:apply绑定
var name = "bob";
var o = {name: "lynn"};
function getName() {
console.log(this.name);
}
getName.apply(o); // 输出:lynn,this绑定的是 o 对象
实例三:bind绑定
var name = "bob";
var o = {name: "lynn"};
function getName() {
console.log(this.name);
}
var getUserInfo = getName.bind(o);
getUserInfo(); // 输出:lynn this绑定的是 o 对象
第四条规则:函数是否被new调用,如果是的话this绑定的是新创建的对象。
function Person(name) {
this.name = name;
}
let person = new Person("bob");
console.log(person.name);
第五条规则:ES6箭头函数不会使用前面4条规则,是根据当前此法作用域来决定this,箭头函数会继承外层函数调用的this绑定。
var name = "bob";
var o = {name: 'lynn'};
function getName() {
return () => {
console.log(this.name);
}
}
getName()(); // 输出:bob 这时候绑定的是全局window对象
getName().call(o);
// 输出:bob
// 这时候在调用 箭头函数 使用call改变绑定,
// 但 箭头函数 会 继承外层函数 调用的this绑定,
// 但这时候绑定的仍然是 外层函数 的this绑定的对象,即全局window对象。
getName.call(o)();
// 输出:lynn
// 在调用箭头函数之前,箭头函数 的 外层函数 改变this绑定o对象,
// 箭头函数 会继承 外层函数的this绑定,this指向的是o对象