关于javascript:我不知道的javaScript一度困扰我的this指向问题竟然这么简单

7次阅读

共计 3693 个字符,预计需要花费 10 分钟才能阅读完成。

    我刚刚接触 javaScript 的时候,对于 this 指向一度是我胆怯触碰的一个问题,然而其实 this 指向其实很简略也很好了解,一句话简述就是this 取值是在函数执行时确认

一、绑定规定

1、作为一般函数(window)— 默认绑定

function fn1() {console.log(this.a)
}
var a = 1;
fn1()   // 1

    当调用 fn1(); 的时候,this.a 被解析成了全局变量 a,因为这里的 fn1();是间接应用不带任何润饰的函数援用进行调用的,因而只能应用 默认绑定,this 指向全局对象

    然而呢只有在 非严格模式下 ,默认绑定能力绑定到全局对象。严格模式下与 fn1() 的调用地位无关

"use strict";
function fn1() {console.log(this.a)
}
var a = 1;
fn1(); // Uncaught TypeError: Cannot read property 'a' of undefined

2、作为对象办法被调用(返回对象自身)— 隐式绑定

const name = '李四';
function sayHi() {console.log(this.name);
}
const obj = {
    name: '张三',
    sayHi
}
obj.sayHi();    // 张三

    首先,sayHi()无论是间接在 obj 中定义还是先定义再增加为援用属性,这个函数严格来说都不属于 obj 对象。当 sayHi(); 被调用的时,它的落脚点的确指向 obj 对象。当函数援用有上下文对象时,隐式绑定规定会把函数调用中的 this 绑定到这个上下文对象。因为调用 sayHi()时 this 被绑定到 obj,因而 this.name 和 obj.name 是一样的。

const name = '李四';
function sayHi() {console.log(this.name);
}
const obj = {
    name: '张三',
    sayHi
}
const objOut = {
    name: '王五',
    obj
}
objOut.obj.sayHi(); // 张三

对象属性援用链中只有最初一层会影响调用地位

上面是一个最常见的 this 绑定问题就是 被隐式绑定的函数会失落绑定对象,也就是说它会利用默认绑定,从而把 this 绑定到全局对象或者 undefined 上,取决于是否是严格模式。

const name = '李四';
function sayHi() {console.log(this.name)
}
const obj = {
    name: '张三',
    sayHi
}
let sayHello = obj.sayHi;
sayHello(); // 李四

尽管 sayHello 是 obj.sayHi 的一个援用,但实际上它援用的是 sayHi 函数自身,因而此时的 bar()其实是一个不带任何润饰的函数调用,因而利用了默认绑定

上面是一种更常见并且更出其不意的状况产生在传入回调函数:

const name = '李四';
function sayHi() {console.log(this.name)
}
function anotherSayHi(fn) {fn();
}
const obj = {
    name: '张三',
    sayHi
}
let sayHello = obj.sayHi;
anotherSayHi(obj.sayHi) // 李四

参数传递其实就是一种隐式赋值,所以和上一个例子一样。回调函数失落 this 绑定是十分常见的,调用回调函数的函数可能会批改 this

3、应用 call,apply,bind(传入什么是什么)— 显示绑定

const a = 1;
function fn2() {console.log(this.a)
}
const obj = {a: 2}
fn2.call(obj) // 2

通过 fn2.call(…),咱们能够在调用 fn2 时强制把它的 this 绑定到 obj 上。

硬绑定

const a = 1;
function fn2() {console.log(this.a);
}
const obj = {a: 2}
let fn3 = function() {fn2.call(obj);
}
fn3(); // 2
fn3.call(window) // 2

创立了 fn3 => 在它外部手动调用 fn2.call(obj),因而强制把 fn2 的 this 绑定到了 obj => 之后再调用 fn3 => 它总会手动在 obj 上调用 foo => 这种就是显式的强制绑定,也叫做硬绑定。

4、在 class 办法中调用(以后实例自身)— new 绑定

应用 new 来调用函数,或者说产生结构函数调用时,会主动执行上面的操作:
创立一个新的对象 => 这个新的对象会被执行【原型】连贯 => 这个新的对象会绑定到函数调用的 this => 如果函数没有返回其余对象,那么 new 表达式中的函数调用会主动返回这个新对象

const name = '张三';
class People {constructor(name) {
        this.name = name
        this.age = 20
    }
    sayHi() {console.log(this.name)
    }
}
const lisi = new People('李四')
lisi.sayHi() // 李四

二、优先级

通过下面的例子,咱们曾经晓得了 this 绑定的规定,须要做的就是找到函数的调用地位并判断利用哪条规定,然而某个调用地位能够利用多条规定该怎么办呢?那就须要晓得一下他们的优先级啦!

隐式绑定和显式绑定哪个优先级更高?

const a = 1;
function fn1() {console.log(this.a);
}
const obj1 = {
    a: 2,
    fn1
};
const obj2 = {
    a: 3,
    fn1
};
obj1.fn1(); // 2
obj2.fn1(); // 3

obj1.fn1.call(obj2); // 3
obj2.fn1.call(obj1); // 2

能够看到 显示绑定优先级更高

那么隐式绑定和 new 绑定哪个优先级更高?

function foo(something) {this.a = something;}
const obj1 = {foo};
const obj2 = {};
obj1.foo(2);
console.log(obj1.a); // 2
obj1.foo.call(obj2, 3)
console.log(obj2.a); // 3

const bar = new obj1.foo(4);
console.log(obj1.a) // 2
console.log(bar.a) // 4

能够看到new 绑定优先级更高
new 和 call/apply 无奈一起应用,因而无奈通过 new foo.call(obj1) 来间接进行测试。然而咱们能够应用硬绑定来测试它俩的优先级。

function foo(something) {this.a = something;}
const obj1 = {};
const bar = foo.bind(obj1);
bar(2);
console.log(obj1.a); // 2
const baz = new bar(3);
console.log(obj1.a); // 2
console.log(baz.a); // 3

bar 被硬绑定到 obj1 上,new 批改了硬绑定(到 obj1 的)调用 bar(…)中的 this。因为应用了 new 绑定,失去了名字为 baz 的新对象,且 baz.a 的值是 3

所以咱们能够通过优先级来判断应该利用上述哪条规定:

  1. new 绑定
  2. call,apply(显示绑定)
  3. 隐式绑定
  4. 默认绑定

三、非凡的 ES6 箭头函数

箭头函数并不会应用四条规范的绑定规定,而是依据以后的词法作用域来决定 this,箭头函数会继承外层函数调用的 this 绑定(无论 this 绑定到什么)。

const zhangsan = {
    name: '张三',
    sayHi() {
        // this 即以后对象
        console.log(this);
    },
    wait() {
        // this 即以后对象
        console.log(this);
        setTimeout(function () {
            // this === window
            // setTimeout 自身触发的这个函数执行,并不是 zhangsan.sayHi()形式
            console.log(this);
        });
    },
    waitAgain() {
        // this 即以后对象
        console.log(this);
        setTimeout(() => {
            // this 即以后对象
            // 箭头函数 this 永远取下级作用域 this
            console.log(this);
        });
    }
};
zhangsan.sayHi();
zhangsan.wait();
zhangsan.waitAgain();
正文完
 0