this指向(学习笔记)

67 阅读3分钟

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 浏览器->windowconst 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: ƒ}