this的5种绑定方式
- 默认绑定(非严格模式下this指向全局对象, 严格模式下
this会绑定到undefined)- 隐式绑定(当函数引用有上下文对象时, 如
obj.foo()的调用方式,foo内的this指向obj)- 显示绑定(通过
call()或者apply()方法直接指定this的绑定对象, 如foo.call(obj))- new绑定
- 箭头函数绑定(
this的指向由外层作用域决定的)
function foo () {
console.log(this.a)
};
var obj = { a: 1, foo };
var a = 2;
var foo2 = obj.foo;
var obj2 = { a: 3, foo2: obj.foo }
obj.foo();
foo2();
obj2.foo2();
// 1 2 3
// foo3()发生了隐式丢失,调用者是obj2,使得foo()中的this指向obj2
function foo () {
console.log(this.a)
}
function doFoo (fn) {
console.log(this)
fn()
}
var obj = { a: 1, foo }
var a = 2
doFoo(obj.foo)
w 2
function foo () {
console.log(this.a)
}
function doFoo (fn) {
console.log(this)
fn()
}
var obj = { a: 1, foo }
var a = 2
var obj2 = { a: 3, doFoo }
obj2.doFoo(obj.foo)
// { a:3, doFoo: f }
// 2
-
如果你把一个函数当成参数传递到另一个函数的时候,也会发生隐式丢失的问题,且与包裹着它的函数的this指向无关。在非严格模式下,会把该函数的this绑定到window上,严格模式下绑定到undefined。
-
如果
call、apply、bind接收到的第一个参数是空或者null、undefined的话,则会忽略这个参数
function foo () {
console.log(this.a)
}
var a = 2
foo.call()
foo.call(null)
foo.call(undefined)
// 2 2 2
var obj1 = {
a: 1
}
var obj2 = {
a: 2,
foo1: function () {
console.log(this.a)
},
foo2: function () {
setTimeout(function () {
console.log(this)
console.log(this.a)
}, 0)
}
}
var a = 3
obj2.foo1()
obj2.foo2()
// 2 w 3
var obj1 = {
a: 1
}
var obj2 = {
a: 2,
foo1: function () {
console.log(this.a)
},
foo2: function () {
function inner () {
console.log(this)
console.log(this.a)
}
inner()
}
}
var a = 3
obj2.foo1()
obj2.foo2()
// 2 w 3
function Person (name) {
this.name = name
this.foo1 = function () {
console.log(this.name)
}
this.foo2 = function () {
return function () {
console.log(this.name)
}
}
}
var person1 = new Person('person1')
person1.foo1()
person1.foo2()()
// person1
// ''
- 箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined。
- // w o o
var obj = {
name: 'obj',
foo1: () => {
console.log(this.name)
},
foo2: function () {
console.log(this.name)
return () => {
console.log(this.name)
}
}
}
var name = 'window'
obj.foo1()
obj.foo2()()
- // o w
var name = 'window'
var obj1 = {
name: 'obj1',
foo: function () {
console.log(this.name)
}
}
var obj2 = {
name: 'obj2',
foo: () => {
console.log(this.name)
}
}
obj1.foo()
obj2.foo()
- 'person1' 'person1' 'window'
var name = 'window'
function Person (name) {
this.name = name
this.foo1 = function () {
console.log(this.name)
}
this.foo2 = () => {
console.log(this.name)
}
}
var person2 = {
name: 'person2',
foo2: () => {
console.log(this.name)
}
}
var person1 = new Person('person1')
person1.foo1()
person1.foo2()
person2.foo2()
person1.foo2()为箭头函数,this由外层作用域决定,且指向函数定义时的this而非执行时,在这里它的外层作用域是函数Person,且这个是构造函数,并且使用了new来生成了对象person1,所以此时this的指向是为person1。
var name = 'window'
function Person (name) {
this.name = name
this.foo1 = function () {
console.log(this.name)
return function () {
console.log(this.name)
}
}
this.foo2 = function () {
console.log(this.name)
return () => {
console.log(this.name)
}
}
this.foo3 = () => {
console.log(this.name)
return function () {
console.log(this.name)
}
}
this.foo4 = () => {
console.log(this.name)
return () => {
console.log(this.name)
}
}
}
var person1 = new Person('person1')
person1.foo1()() // 'person1' 'window'
person1.foo2()() // 'person1' 'person1'
person1.foo3()() // 'person1' 'window'
person1.foo4()() // 'person1' 'person1'
- 箭头函数的
this无法通过bind、call、apply来直接修改,但是可以通过改变作用域中this的指向来间接修改。
var name = 'window'
var obj1 = {
name: 'obj1',
foo1: function () {
console.log(this.name)
return () => {
console.log(this.name)
}
},
foo2: () => {
console.log(this.name)
return function () {
console.log(this.name)
}
}
}
var obj2 = {
name: 'obj2'
}
obj1.foo1.call(obj2)() // 'obj2' 'obj2'
obj1.foo1().call(obj2) // 'obj1' 'obj1'
obj1.foo2.call(obj2)() // 'window' 'window'
obj1.foo2().call(obj2) // 'window' 'obj2'
-
总结
OK👌,来总结一下箭头函数需要注意的点吧:
- 它里面的
this是由外层作用域来决定的,且指向函数定义时的this而非执行时 - 字面量创建的对象,作用域是
window,如果里面有箭头函数属性的话,this指向的是window - 构造函数创建的对象,作用域是可以理解为是这个构造函数,且这个构造函数的
this是指向新建的对象的,因此this指向这个对象。 - 箭头函数的
this是无法通过bind、call、apply来直接修改,但是可以通过改变作用域中this的指向来间接修改。
- 它里面的
-
避免使用的场景
- 使用箭头函数定义对象的方法
let obj = { value: 'LinDaiDai', getValue: () => console.log(this.value) } obj.getValue() // undefined- 定义原型方法
function Foo (value) { this.value = value } Foo.prototype.getValue = () => console.log(this.value) const foo1 = new Foo(1) foo1.getValue() // undefined- 构造函数使用箭头函数
const Foo = (value) => { this.value = value; } const foo1 = new Foo(1) // 事实上直接就报错了 Uncaught TypeError: Foo is not a constructor console.log(foo1);- 作为事件的回调函数
const button = document.getElementById('myButton'); button.addEventListener('click', () => { console.log(this === window); // => true this.innerHTML = 'Clicked button'; });
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')
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'
person1.obj.foo1()()返回的是一个普通的匿名函数,调用它的是window,所以打印出window。person1.obj.foo1.call(person2)()中是使用.call(person2)改变第一层函数中的this,匿名函数和它没关系,依旧是window调用的,所以打印出window。person1.obj.foo1().call(person2)是通过.call(person2)改变匿名函数内的this,所以绑定有效,因此打印出person2。person1.obj.foo2()()第一层为普通函数,第二层为匿名箭头函数。首先让我们明确匿名箭头函数内的this是由第一层普通函数决定的,所以我们只要知道第一层函数内的this是谁就可以了。而这里,第一层函数最后是由谁调用的呢 🤔️?是由obj这个对象,所以打印出obj。person1.obj.foo2.call(person2)()中使用.call(person2)改变了第一层函数中的this指向,所以第二层的箭头函数会打印出person2。person1.obj.foo2().call(person2)中使用.call(person2)想要改变内层箭头函数的this指向,但是失败了,所以还是为外层作用域里的this,打印出obj。
function foo() {
console.log( this.a );
}
var a = 2;
(function(){
"use strict";
foo();
})();
// 2