美文网首页
别再被JS中的this问倒了

别再被JS中的this问倒了

作者: 7天苹果 | 来源:发表于2017-07-27 19:31 被阅读36次
隐式绑定

关于this,一般来说,谁调用了方法,该方法的this就指向谁,如:

function foo(){
    console.log(this.a)
}

var a = 3;

var obj = {
    a: 2,
    foo: foo
};

obj.foo(); // 输出2,因为是obj调用的foo,所以foo的this指向了obj,而obj.a = 2

如果存在多次调用,对象属性引用链只有上一层或者说最后一层在调用位置中起作用,如:

function foo() {
    console.log( this.a )
}

var obj2 = { 
    a: 42,
    foo: foo
}

var obj1 = {
    a: 2,
    obj2: obj2
}

obj1.obj2.foo(); // 42
隐式丢失

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

function foo() {
    console.log( this.a )
}

var obj1 = {
    a: 2,
    foo: foo
}

var bar = obj1.foo; // 函数别名!

var a = "oops, global"; // a是全局对象的属性

bar(); // "oops, global"

虽然bar是obj.foo的一个引用,但是实际上,它引用的是foo函数本身,因此此时的bar()其实是一个不带任何修饰的函数调用,因此应用了默认绑定

一个更微妙、更常见并且更出乎意料的情况发生在传入回调函数时:

function foo() {
    console.log( this.a )
}

function doFoo( fn ){
    // fn 其实引用的是 foo
    fn(); // <-- 调用位置!
}

var obj = {
    a: 2,
    foo: foo
}

var a = "oops, global"; // a是全局对象的属性

doFoo( obj.foo ); // "oops, global"

参数传递其实就是一种隐式赋值,因此我们传入函数时也会被隐式赋值,所以结果和上一个例子一样,如果把函数传入语言内置的函数而不是传入自己声明的函数(如setTimeout等),结果也是一样的

显式绑定

简单的说,就是指定this,如:call、apply、bind、new绑定等。

function foo( something ) {
    console.log( this.a, something)
    return this.a + something
}

var obj = {
    a: 2
}

var bar = function() {
    return foo.apply( obj, arguments)
}

var b = bar(3); // 2 3
console.log(b); // 5

同样,本例也可以使用bind:

function foo( something ) {
    console.log( this.a, something)
    return this.a + something
}

var obj = {
    a: 2
}

var bar = foo.bind(obj)

var b = bar(3); // 2 3
console.log(b); // 5
new绑定

在传统面向类的语言中,使用new初始化类的时候会调用类中的构造函数,但是JS中new的机制实际上和面向类和语言完全不同。

使用new来调用函数,或者说发生构造函数调用时,会自动执行下面的操作:

  • 创建(或者说构造)一个全新的对象
  • 这个新对象会被执行[[Prototype]]连接
  • 这个新对象会绑定到函数调用的this
  • 如果函数没有返回其他对象,那么new表达式中的函数会自动返回这个新对象

如:

function foo(a){
    this.a = a
}

var bar = new foo(2);
console.log(bar.a); // 2

使用new来调用foo(...)时,我们会构造一个新对象并把它绑定到foo(...)调用中的this上。new是最后一种可以影响函数调用时this绑定行为的方法,我们称之为new绑定。

this的优先级

毫无疑问,默认绑定的优先级是四条规则中最低的,所以我们可以先不考虑它。

隐式绑定和显式绑定哪个优先级更高?我们来测试一下:

function foo(a){
    console.log(this.a)
}

var obj1 = {
    a: 2,
    foo: foo
}

var obj2 = {
    a: 3,
    foo: foo
}

obj1.foo(); // 2
obj2.foo(); // 3

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

可以看到,显式绑定优先级更高,也就是说在判断时应当先考虑是否可以存在显式绑定。

现在我们要搞清楚new绑定和隐式绑定的优先级谁高谁低 :

function foo(something){
    this.a = something
}

var obj1 = {
    foo: foo
}

var obj2 = {}

obj1.foo(2); 
console.log(obj1.a); // 2

obj1.foo.call(obj2,3);
console.log(obj2.a); // 3

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

可以看到new绑定比隐式绑定优先级高。但是new绑定和显式绑定谁的优先级更高呢?

function foo(something){
    this.a = something
}

var obj1 = {}

var bar = foo.bind(obj1);
bar(2);
console.log(obj1.a); // 2

var baz = new bar(3);
console.log(obj1.a); // 2
console.log(baz.a); // 3

可以看到,new绑定修改了硬绑定中的this,所以new绑定的优先级比显式绑定更高。

This在箭头函数中的应用

箭头函数不使用this的四种标准规则,而是根据外层(函数或者全局)作用域来决定this。

我们来看一下箭头函数的词法作用域:

function foo() {
    // 返回一个箭头函数
    return (a) => {
        // this继承自foo()
        console.log(this.a)
    };
}

var obj1 = {
    a: 2
};

var obj2 = {
    a: 3
};

var bar = foo.call(obj1);
bar.call(obj2); // 2, 不是3!

foo()内部创建的箭头函数会捕获调用时foo()的this。由于foo()的this绑定到obj1,bar(引用箭头函数)的this也会绑定到obj1,箭头函数的绑定无法被修改。(new也不行!)

总结

如果要判断一个运行中的函数的this绑定,就需要找到这个函数的直接调用位置。找到之后就可以顺序应用下面这四条规则来判断this的绑定对象。

  • 由new调用?绑定到新创建的对象。
  • 由call或者apply(或者bind)调用?绑定到指定的对象。
  • 由上下文对象调用?绑定到那个上下文对象。
  • 默认:在严格模式下绑定到undefined,否则绑定到全局对象。

相关文章

  • 别再被JS中的this问倒了

    隐式绑定 关于this,一般来说,谁调用了方法,该方法的this就指向谁,如: 如果存在多次调用,对象属性引用链只...

  • vue运行启动命令出现警告解决

    运行时出现这种问题结解决 出现此错误是因为postcss.config.js文件中 browsers已经不再被使用...

  • js中call,apply和bind方法的区别和使用场景

    在js中,所有的函数再被调用的时候都会默认传入两个参数,一个是this,还有一个是arguments。在默认情况下...

  • 姑娘,别颠倒了爱情

    其实想写这篇文章已经很久了,只是迟迟都没下笔,因为我怕我讲不清我要说的是什么,就算现在写出来,我依旧觉得我会讲不清...

  • 学习笔记--js垃圾回收

    js中的垃圾都是自动的,我们不需要手动回收垃圾;通常认为对象不再被引用时是垃圾,对象从根上(全局对象)向下一级一级...

  • 数组方法大全

    函数中,js所传递的参数可以用arguments[i](注意别漏了s)来取,在argumengts[]数组中的顺...

  • 内置对象

    js的内置对象 内置对的有11个,Global对象不列入内置对象。 js的数据类型 问:js中数据类型有哪些?st...

  • 问别

    素语盈盈暗香浓,长夜几多欢聚,不与君同。未央夜,长生宫,相誓比翼终成空。婵娟尤在,绿绮弦冰。 笙歌落尽花月重,最易...

  • 问别

    问君愁思几许,一穿烟雨 ,几多黄昏 ,杏花树下几折柳。

  • 别 问

    别 问 文/中流击水 别问我为什么前来送行 瘟疫肆虐时你们不怕牺牲 防护服上写下赤胆与忠诚 别问我为什么眼睛湿润 ...

网友评论

      本文标题:别再被JS中的this问倒了

      本文链接:https://www.haomeiwen.com/subject/jopzkxtx.html