前提说明
1.js中this绑定和作用域时机不同。作用域是在code被解析时就会确定,而this是执行时才会确定。
2.js中this绑定规则分以下五种:
- 独立函数调用绑定的this
- 隐式调用函数绑定的this
- 显示调用函数绑定的this
- 通过new关键字调用函数绑定的this
- 箭头函数中的this
1.独立函数调用绑定的this
独立函数调用:在调用的时候没有主题
this指向:this指向的window (通过以独立调用的方式来调用bind绑定后的函数除外)
function foo1() {
console.log(this);
}
function foo2() {
console.log(this);
foo1();
}
function foo3() {
console.log(this);
foo2();
}
foo3();
//window
//window
//window
2.隐式调用函数绑定的this
隐式调用函数:通过对象.的方式来调用
this指向:指向调用的对象。(通过隐式调用方式来调用bind绑定后的函数除外)
function foo() {
console.log(this);
}
const obj = {
name: "obj",
bar: foo,
};
obj.bar(); //Object { name: "obj", bar: foo() }
3.显示调用函数绑定的this
显示调用函数:通过call、apply、bind函数来调用
this指向:代码说明
function foo(num1, num2) {
const num3 = num1 + num2;
console.log(this);
}
foo.call({ name: "call" }, 1, 2); //Object { name: "call" }
foo.apply({ name: "apply" }, [1, 2]); //Object { name: "apply" }
const bind = foo.bind({ name: "bind" }, 1, 2);
bind();//Object { name: "bind" }
4.通过new关键字调用函数绑定的this
通过new关键字来调用函数
- 先创建一个新对象,该对象是个空对象
- 新对象的隐式原型
__proto__指向函数的显式原型prototype - 函数内部的
this指向该对象 - 执行函数内部代码
- 如果该函数没有 复杂数据类型 的返回值则默认返回
this指向的对象,否则返回引用类型的返回值;如果有简单数据类型的话也是返回this指向的对象
function Foo(name) {
this.name = name;
console.log(this);
}
// 通过new方式来调用的函数,这个函数会创建一个新的对象,该函数内部的this会指向该对象。
new Foo("new"); //Object { name: "new" }
// 如果该函数没有返回值则默认返回this指向的对象
const o = new Foo("o");
console.log(o); //Object { name: "o" }
function Bar(name) {
this.name = name;
console.log(this);
// 如果该函数有返回值则为返回值
return { age: 18 };
}
const o2 = new Bar("o2") //Object { name: "o2" }
console.log(o2); //Object { age: 18 }
5.箭头函数中的this
箭头函数比较特殊,其不会绑定this,在箭头函数中使用this会向外层作用域中寻找this,与在函数内部使用一个未定义的变量会向外层作用域寻找原理相同。
const obj = {
foo: () => {
console.log(this);
},
bar: function () {
console.log(this);
},
};
obj.foo(); //Window
obj.bar(); //Object { foo: foo(), bar: bar() }
6.特殊规则(一):忽略显式绑定
结论:显式绑定null和undefined时,this指向window
function foo() {
console.log(this);
}
foo.call(null); //Window
foo.call(undefined); //Window
foo.apply(null); //Window
foo.apply(undefined); //Window
const bindNull = foo.bind(null);
const bindUndefined = foo.bind(undefined);
bindNull(); //Window
bindUndefined(); //Window
7.特殊规则(二):间接函数调用
const obj1 = {
name: "obj",
foo: function (){
console.log(this);
},
};
const obj2 = { name: "obj2" };
obj2.foo = obj1.foo;
obj2.foo(); //Object { name: "obj2", foo: foo() }
//赋值表达式把obj1.foo作为结果来进行调用,为独立函数调用
(obj2.foo = obj1.foo)(); //Window
8.this绑定优先级
先说结论:new绑定 > 显式绑定 > 隐式绑定 > 默认绑定
- 显式与隐式绑定比较; 显式 > 隐式
//1.隐式绑定与call和apply比较
const obj = {
name: "obj",
foo: function () {
console.log(this);
},
};
obj.foo.call({ name: "call" }); //Object { name: "call" }
obj.foo.apply({ name: "apply" });//Object { name: "apply" }
//2.隐式绑定与bind比较
function foo() {
console.log(this);
}
const obj = {
name: "obj",
foo: foo.bind({ name: "bind" }),
};
obj.foo();//Object { name: "bind" }
-
显式绑定与new比较; new>显式
注意:new关键字不能与
apply和call关键字一起使用,所以只用bind来进进行比较
function foo() {
console.log(this);
}
const bind = foo.bind({ name: "bind" });
new bind(); //Object { }
9.this面试题
var name = "window"
var person = {
name: "person",
sayName: function () {
console.log(this.name);
},
};
function foo() {
var sayName = person.sayName;
sayName(); //window
person.sayName(); //person
(person.sayName)(); //person
(bar = person.sayName)(); //window
}
foo();
var name = "window";
var person1 = {
name: "person1",
foo1: function () {
console.log(this.name);
},
foo2: () => console.log(this.name),
foo3: function () {
return function () {
console.log(this.name);
};
},
foo4: function () {
return () => {
console.log(this.name);
};
},
};
var person2 = { name: "person2" };
console.log("==================== foo1 ====================");
person1.foo1(); //person1 独立函数调用
person1.foo1.call(person2); //person2 显式绑定
console.log("==================== foo2 ====================");
person1.foo2(); //window 箭头函数
person1.foo2.call(person2); //window 箭头函数
console.log("==================== foo3 ====================");
person1.foo3()(); //window 独立函数调用
person1.foo3.call(person2)(); //window 独立函数调用
person1.foo3().call(person2); //person2 显式绑定
console.log("==================== foo4 ====================");
person1.foo4()(); //person1 箭头函数
person1.foo4.call(person2)(); //person2 箭头函数
person1.foo4().call(person2); //person1 箭头函数
var name = "window";
function Person(name) {
this.name = name;
this.foo1 = function () {
console.log(this.name);
};
this.foo2 = () => {
console.log(this.name);
};
this.foo3 = function () {
return function () {
console.log(this.name);
};
};
this.foo4 = function () {
return () => {
console.log(this.name);
};
};
}
var person1 = new Person("person1");
var person2 = new Person("person2");
console.log("========================= foo1 =========================");
person1.foo1(); //person1
person1.foo1.call(person2); //person2
console.log("========================= foo2 =========================");
person1.foo2(); //person1
person1.foo2.call(person2);//person1
console.log("========================= foo3 =========================");
person1.foo3()(); //window
person1.foo3.call(person2)(); //window
person1.foo3().call(person2); //person2
console.log("========================= foo4 =========================");
person1.foo4()(); //person1
person1.foo4.call(person2)(); //person2
person1.foo4().call(person2); //person1
var name = "window";
function Person(name) {
this.name = name;
this.obj = {
name: "obj",
foo1: function () {
return function () {
console.log(this.name);
};
},
foo2: function () {
return () => {
console.log(this.name);
};
},
};
}
var person1 = new Person("person1");
var person2 = new Person("person2");
console.log("======================== foo1 =================================");
person1.obj.foo1()(); //window
person1.obj.foo1.call(person2)(); //window
person1.obj.foo1().call(person2); //person2
console.log("======================== foo2 =================================");
person1.obj.foo2()(); //obj
person1.obj.foo2.call(person2)(); //person2
person1.obj.foo2().call(person2); //obj