- 函数在调用时js会默认给this绑定一个值
- this的绑定和定义的位置无关
- this的绑定和调用方式以及调用的位置有关
- this是在运行时被绑定的
JavaScript中this指向分类:
- 默认绑定
- 隐式绑定
- new绑定
- 显式绑定
一.默认绑定
1. 函数直接调用,this指向window
function foo() {
console.log("foo", this);
}
foo() //window
2.对象中的函数 单独调用 this指向window
const obj = {
say() {
console.log("say", this);
}
}
const a = obj.say;
a() //window
3.高阶函数
const obj = {
say() {
console.log("say", this);
}
}
function bar(fn) {
fn()
}
bar(obj.say)//window
4.在严格模式下,this指向undefined
二.隐式绑定
通过对象的引用this执行调用的对象
function foo() {
console.log("foo函数:", this)
}
var obj = {
bar: foo
}
obj.bar() // foo函数: {bar: ƒ}
三.new构造函数绑定
- 创建新的空对象
- 将this指向这个空对象
- 执行函数体中的代码
- 没有显示返回非空对象时, 默认返回这个对象
function foo() {
this.name = "why"
console.log("foo函数:", this)
}
new foo() // foo函数: foo {name: "why"}
四.显式绑定
- call
- bind
- apply
三者区别:1.传参区别 bind call 传参后续的参数以多参数的形式传递 apply传参通过数组 2.call apply绑定后立即执行函数 bind执行后会形成一个新的函数 需要再次执行函数
// call 参数 参数一:绑定的对象 参数二: xxx,xxx 后续的参数以多参数的形式传递
// apply 参数 参数一:绑定的对象 参数二:[xxx,xxx]
// call/apply
function foo(name, age, height) {
console.log("foo函数被调用:", this)
console.log("打印参数:", name, age, height)
}
// ()调用
// foo("why", 18, 1.88)
// apply
// 第一个参数: 绑定this
// 第二个参数: 传入额外的实参, 以数组的形式
// foo.apply("apply", ["kobe", 30, 1.98])
// call
// 第一个参数: 绑定this
// 参数列表: 后续的参数以多参数的形式传递, 会作为实参
foo.call("call", "james", 25, 2.05)
// bind 参数 参数一:绑定的对象 参数二:【xxx,xxx】
// bind 会返回一个新的函数
function foo(name, age, height, address) {
console.log("foo:", this)
console.log("参数:", name, age, height, address)
}
var obj = { name: "why" }
// 需求: 调用foo时, 总是绑定到obj对象身上(不希望obj对象身上有函数)
// 1.bind函数的基本使用
// var bar = foo.bind(obj)
// bar() // this -> obj
// 2.bind函数的其他参数(了解)
var bar = foo.bind(obj, "kobe", 18, 1.88)
bar("james")
五.this的优先级
- 显式绑定绑定的优先级高于隐式绑定
- bind高于默认绑定
- new绑定优先级高于隐式绑定
- new不可以和apply/call一起使用
- new优先级高于bind
- bind优先级高于apply/call
// function foo() {
// console.log("foo:", this)
// }
// 比较优先级:
// 1.显式绑定绑定的优先级高于隐式绑定
// 1.1.测试一:apply高于默认绑定
var obj = { foo: foo }
obj.foo.apply("abc")
obj.foo.call("abc")
// 1.2.测试二:bind高于默认绑定
var bar = foo.bind("aaa")
var obj = {
name: "why",
baz: bar
}
obj.baz()
// 2.new绑定优先级高于隐式绑定
var obj = {
name: "why",
foo: function() {
console.log("foo:", this)
console.log("foo:", this === obj)
}
}
new obj.foo()
// 3.new/显式
// 3.1. new不可以和apply/call一起使用
// 3.2. new优先级高于bind
function foo() {
console.log("foo:", this)
}
var bindFn = foo.bind("aaa")
new bindFn()
// 4.bind/apply优先级
// bind优先级高于apply/call
function foo() {
console.log("foo:", this)
}
var bindFn = foo.bind("aaa")
bindFn.call("bbb")
六.this绑定之外的情况
// 1.情况一: 显式绑定null/undefined, 那么使用的规则是默认绑定
function foo() {
console.log("foo:", this)
}
foo.apply("abc")
foo.apply(null)
foo.apply(undefined)
// 2.情况二: 间接函数引用
var obj1 = {
name: "obj1",
foo: function () {
console.log("foo:", this)
}
}
var obj2 = {
name: "obj2"
};
// {}[]()
// obj2.foo = obj1.foo
// obj2.foo()
(obj2.foo = obj1.foo)() // obj2.foo = obj1.foo 返回的是一个函数,然后立即执行 执行的时候是默认绑定 所以this指向window
七.箭头函数中的this
箭头函数中没有this 显式绑定this也绑不上去 箭头函数的this指向只会向上一级作用域找
// 1.普通函数中是有this的标识符
function foo() {
console.log("foo", this)
}
foo()
foo.apply("aaa")
// 2.箭头函数中, 压根没有this
var bar = () => {
console.log("bar:", this)
}
bar()
// 通过apply调用时, 也是没有this
bar.apply("aaaa")
console.log("全局this:", this)关于JS的this指向问题
var message = "global message"
// 3.this的查找规则
var obj = {
name: "obj",
foo: () => {
var bar = () => {
console.log("bar:", this)
}
return bar
}
}
var fn = obj.foo()
fn.apply("bbb")
八.关于this的面试题
面试题1
var name = 'window';
var person = {
name: 'person',
sayName: function () {
console.log(this.name);
},
};
function sayName() {
var sss = person.sayName;
sss(); // 绑定: 默认绑定, window -> window
person.sayName(); // 绑定: 隐式绑定, person -> person
person.sayName(); // 绑定: 隐式绑定, person -> person
(b = person.sayName)(); // 术语: 间接函数引用, window -> window
}
sayName();
面试题2
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 () {
// console.log(this) // 第一个表达式this -> person1
// console.log(this) // 第二个表达式this -> person2
// console.log(this) // 第三个表达式this -> person1
return () => {
console.log(this.name)
}
}
}
var person2 = { name: 'person2' }
// 开始题目:
person1.foo1(); // 隐式绑定: person1
person1.foo1.call(person2); // 显式绑定: person2
person1.foo2(); // 上层作用域: window
person1.foo2.call(person2); // 上层作用域: window
person1.foo3()(); // 默认绑定: window
person1.foo3.call(person2)(); // 默认绑定: window
person1.foo3().call(person2); // 显式绑定: person2
person1.foo4()(); // person1
person1.foo4.call(person2)(); // person2
person1.foo4().call(person2); // person1
面试题3
var name = 'window'
/*
1.创建一个空的对象
2.将这个空的对象赋值给this
3.执行函数体中代码
4.将这个新的对象默认返回
*/
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)
}
}
}
// person1/person都是对象(实例instance)
var person1 = new Person('person1')
var person2 = new Person('person2')
// 面试题目:
person1.foo1() // 隐式绑定: person1
person1.foo1.call(person2) // 显式绑定: person2
person1.foo2() // 上层作用域查找: person1
person1.foo2.call(person2) // 上层作用域查找: person1
person1.foo3()() // 默认绑定: window
person1.foo3.call(person2)() // 默认绑定: window
person1.foo3().call(person2) // 显式绑定: person2
person1.foo4()() // 上层作用域查找: person1(隐式绑定)
person1.foo4.call(person2)() // 上层作用域查找: person2(显式绑定)
person1.foo4().call(person2) // 上层作用域查找: person1(隐式绑定)
面试题4
var name = 'window'
/*
1.创建一个空的对象
2.将这个空的对象赋值给this
3.执行函数体中代码
4.将这个新的对象默认返回
*/
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')
person1.obj.foo1()() // 默认绑定: window
person1.obj.foo1.call(person2)() // 默认绑定: window
person1.obj.foo1().call(person2) // 显式绑定: person2
person1.obj.foo2()() // 上层作用域查找: obj(隐式绑定)
person1.obj.foo2.call(person2)() // 上层作用域查找: person2(显式绑定)
person1.obj.foo2().call(person2) // 上层作用域查找: obj(隐式绑定)