this指向分析
先看两个🌰
// 定义函数
function foo() {
console.log(this);
}
1.直接调用
foo(); // node->global 浏览器->window
2.对象调用
const obj = {
name: 'obj',
foo: foo
}
obj.foo(); // { name: 'obj', foo: [Function: foo] }
1⃣️分析
1.函数调用的时候 js会默认给this绑定一个值
2.this的绑定和定义的位置(编写的位置) 没有关系
3.this的绑定和调用方式以及调用的位置有关系
4.this是运行时被绑定的
2⃣️规则
1.默认绑定
1.独立函数调用(没有绑定到任何对象上)
foo(); // node->global 浏览器->window
const obj = {
name: 'obj',
foo: function() {
console.log('foo', this);
}
}
const bar = obj.foo;
bar(); // node->global 浏览器->window
注意:严格模式下独立函数调用this的值为undefined
'use strict'
foo(); // undefined
2.内置函数的调用绑定(举例)
// 定时器
setTimeout(function () {
console.log(this); // node-->Timeout 浏览器 --> window
}, 1000);
// forEach(item, thisArg)
const num = [1, 2, 3];
num.forEach(function (){
console.log(this); // node-->global 浏览器 --> window
})
num.forEach(function (){
console.log(this); // {name: 'obj'}
}, {name: 'obj'});
2.隐式绑定
通过某个函数进行调用
function foo() {
console.log(this, 'foo');
}
const obj = {
name: 'obj',
bar: foo,
}
obj.bar(); // obj
3.显示绑定
前置知识储备
/*
* call(thisArg, args1,args2...)
* thisArg 绑定this args1,args2...额外的实参(参数列表)
* 作用:this绑定到指定对象上
* */// call apply语法
const obj = {
name: 'obj'
}
function foo() {
console.log(this)
}
function person(name, age, friend) {
console.log(`this: ${this}`);
console.log(`name: ${name}, age: ${age}, friend: ${friend}`);
}
/*
* call(thisArg, args1,args2...)
* thisArg 绑定this args1,args2...额外的实参(参数列表)
* 作用:this绑定到指定对象上
* */
foo.call(obj); // { name: 'obj' }
person.call(obj, 'cc', 18, 'gg'); // { name: 'obj' } name: cc, age: 18, friend: gg
/*
* apply(thisArg, argArray[])
* thisArg 绑定this argArray额外的实参(数组的形式)
* 作用:this绑定到指定对象上
* */
foo.apply(obj); // { name: 'obj' }
person.apply(obj, ['cc', 18, 'gg']); // { name: 'obj' } name: cc, age: 18, friend: gg
// bind语法
/*
* bind(thisArg, args1,args2...)
* thisArg 绑定this args1,args2...额外的实参(参数列表)
* 作用:一个函数总是显式的绑定到一个对象上 创建一个新的绑定函数
* 绑定函数对象又称为怪异函数对象(ES5)
* */
const bar = foo.bind(obj);
bar(); //
const baz = person.bind(obj, 'cc', 18, 'gg');
baz(); // { name: 'obj' } name: cc, age: 18, friend: gg
1.call
const obj = {
name: 'obj'
}
function foo() {
console.log(this, 'this')
}
foo.call(obj, 1, 2, 3);
2.apply
foo.apply(obj, [1,2,3]);
3.bind
const bar = foo.bind(obj);
bar();
4.new绑定
将函数作为一个构造函数 通过new关键字来使用
function foo() {
console.log(this, 'foo');
}
new foo();
new关键字执行步骤(先不考虑原型与原型链)
1.创建新的对象
2.将this指向这个空对象
3.执行函数体内的代码
4.没有显式返回非空对象时 默认返回这个对象
3⃣️优先级(由低到高)
1.默认绑定(最低)
2.隐式绑定
// 显示(高) -- 隐式(低)
function foo() {
console.log(this);
}
const bar = foo.bind({name: 'obj1'})
const obj = {
name: 'obj',
baz: bar
}
obj.baz(); // { name: 'obj1' }
3.显示绑定
// new[低] -- 显式(不能使用apply/call)[高]
const bindFn = foo.bind({name: 'obj'});
new bindFn(); // foo {}
4.new绑定 (不能和apply和call一起使用 bind使用时高于显示绑定优先级)
// new(高) -- 隐式(低)
const obj = {
name: 'obj',
foo: function () {
console.log(this);
}
}
new obj.foo(); // foo {}
注意
// bind/apply比较
const bar = foo.bind({name: 'bind'});
bar.apply({name: 'apply'}); // { name: 'bind' }
// bind/call比较
bar.call({name: 'call'}); // { name: 'bind' }
结论:bind比apply/call优先级更高
4⃣️四大规则之外的规则
1.显示绑定中 如果绑定null/undefined 则使用默认绑定规则
foo.apply(null); // node->global 浏览器->window
foo.apply(undefined); // node->global 浏览器->window
'use strict'
foo.apply(null); // null
foo.apply(undefined); // undefiend
2.间接函数引用 使用默认绑定规则
const obj1 = {
name: 'obj1',
foo,
}
const obj2 = {
name: 'obj2'
}
obj2.foo = obj1.foo;
obj2.foo(); // { name: 'obj2', foo: [Function: foo] }
(obj2.foo = obj1.foo)(); // node->global 浏览器->window
3.箭头函数的this
箭头函数中的this(箭头函数中没有this 能打印出来的this是根据变量的查找规则查找到的this)
规则:变量的查找规则
const foo = () => {
console.log(this === window, 'this绑定'); // true
}
const obj1 = {
name: 'obj1',
foo,
}
obj1.foo();
const obj2 = {
name: 'obj2',
foo: function () {
return () => {
console.log(this);
};
}
}
obj2.foo()(); // {name: 'obj2', foo: ƒ}