一、this理解
this 是执行上下文中的一个属性,this的绑定和函数声明位置无关,只取决于函数的调用方式。
二、this指向判断
函数调用模式
当一个函数不是一个对象的属性时,直接作为函数来调用时,this 指向全局对象。注意在严格模式下,全局对象无法使用默认绑定,this会绑定到undefined。
// 1. 普通的函数被独立调用
"use strict" // 严格模式this指向undefined
function foo() {
console.log('foo', this); // Window
}
foo()
// 2.函数定义在对象中,但是独立调用
let obj = {
bar: function () {
console.log('bar', this); // Window
}
}
let baz = obj.bar;
baz()
// 3.高阶函数
function test(fn) {
fn(); // Window
}
test(obj.bar)
方法调用模式
如果一个函数作为一个对象的方法来调用时,this 指向这个对象(对象属性引用链中最后一层影响调用位置)
// 隐式绑定
let obj2 = {
a: 2,
foo: foo
}
let obj1 = {
a: 1,
obj2: obj2
}
function foo() {
console.log(this.a);
}
obj1.obj2.foo(); // 2
**隐式丢失:**被隐式绑定的函数丢失绑定对象,会应用默认绑定。
let obj = {
a: 1,
foo: foo
}
function foo() {
console.log(this.a);
}
var a = 'global';
let bar = obj.foo;
bar() // global
let obj = {
a: 1,
foo: foo
}
function foo() {
console.log(this.a);
}
function bar(fn) {
fn()
}
var a = 'global';
bar(obj.foo); // global
构造器调用模式
如果一个函数用 new 调用时,函数执行前会新创建一个对象,this 指向这个新创建的对象。
使用new调用函数执行操作
- 创建一个全新对象
- 新对象执行原型连接
- 对象绑定到函数调用的this
- 如果函数没有返回其他对象,new表达式的函数调用会自动返回这个新对象
function foo(a) {
this.a = a
}
let bar = new foo(1)
console.log(bar.a) // 1
apply 、call 和 bind 调用模式
三、call,apply,bind
call和apply区别
- 传参格式不一样
function.call(thisArg, arg1, arg2, ...)
function.apply(thisArg, argsArray)
注意:当这个函数处于非严格模式下,thisArg指定为null或undefined时会自动替换为全局对象,原始值会被包装。
bind
bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
let obj = { a: 1}
function foo() {
console.log(this.a); // 1
}
let bar = foo.bind(obj);
bar()
let obj = { name: 'a' }
function foo(name, age, height, address) {
console.log('foo:', this); // { name: 'a' }
console.log('参数:', name, age, height, address); // 参数: kobe 18 1.88 james
}
let bar = foo.bind(obj, 'kobe', 18, 1.88);
bar('james')
四、绑定优先级
显示绑定优先级高于隐式
function foo() {
console.log('foo:', this)
}
let obj = {
foo: foo
}
obj.foo.apply({ name: 'tom' }) // { name: 'tom' }
obj.foo.call({ name: 'tom' }) // { name: 'tom' }
new绑定优先级高于隐式
let obj = {
name: 'tom',
foo: function() {
console.log('foo:', this) // foo {}
console.log('foo:', this === obj) // false
}
}
new obj.foo()
new优先级高于bind
function foo() {
console.log('foo:',this) // foo {}
}
let bindFn = foo.bind({name:'tom'})
new bindFn
bind优先级高于apply/call
function foo() {
console.log('foo:', this) // { name: 'tom' }
}
let bindFn = foo.bind({ name: 'tom' })
bindFn.call({ name: 'jerry' })
总结:new > bind > call/apply > 隐式绑定 > 默认绑定
五、判断this总结
- 函数如果在new中调用绑定,this指向新创建的对象
- 通过call、apply、bind显示绑定,this指向绑定的对象
- 函数是否在某个上下文对象中调用,this指向那个上下文对象
- 都不是则使用默认绑定,严格模式下绑定到undefined,否则绑定到全局对象
六、关于箭头函数
箭头函数没有自己的this、arguments。箭头函数不会创建自己的this,它只会从自己的作用域链的上层继承this。
七、this指向练习题
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();
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' }
person1.foo1(); // 隐式绑定: person1
person1.foo1.call(person2); // 显式绑定: person2
person1.foo2(); // 上层作用域: window
person1.foo2.call(person2); // 上层作用域: window
person1.foo3()(); // 默认绑定: window
person1.foo3.call(person2)(); //person1.foo3.call(person2)等同于foo3里的this指向person2 默认绑定: window
person1.foo3().call(person2); // 显式绑定: person2
person1.foo4()(); //person1.foo4()this指向person1,箭头函数上层找为person1
person1.foo4.call(person2)(); //person1.foo4.call(person2)this指向person2,箭头函数上层找为person2
person1.foo4().call(person2); // person1
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(隐式绑定)
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')
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(隐式绑定)