#我的21天#《你不知道的javascript》- D8&D9

前端之家收集整理的这篇文章主要介绍了#我的21天#《你不知道的javascript》- D8&D9前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

this全面解析

调用位置

在理解this之前,我们需要理解函数调用位置,分析调用栈(就是为了到达当前执行位置所调用的所有函数),调用位置就在当前正在执行的函数的前一个调用中。

调用栈是:baz
     // 因此当前调用位置是全局作用域
     console.log( "baz" );
     bar( ); // <— bar的调用位置
}
function bar( ) {
     // 当前调用栈baz -> bar
     // 因此当前调用位置在baz中
    console.log( "bar" );
     foo( ); // <— foo的调用位置
}
function foo( ) {
     //当前调用栈 baz -> bar -> foo
     // 当前调用位置在bar中
      console.log( "foo" );
 }
baz( ); // <— baz的调用位置

注意我们是如何(从调用栈中)分析出真正的调用位置的,因为它决定了this的绑定。

绑定位置

你必须找到调用位置,然后判断需要应用下面四条规则中的哪一条。我们首先会分别解释这四条规则,然后解释多条规则都可用时它们的优先级如何排列。

默认绑定

在全局作用域下声明的变量就是全局对象的一个属性,这不是通过赋值得来的,而是它们本身就是一回事,就像一个硬币的两面性那样。在上面的foo调用时,this指向默认的绑定,也就是全局对象。
那么如何判断这里的确是应用了默认绑定呢?可以通过分析调用位置来看看foo是如何调用的。foo是直接食用不带任何修饰的函数引用进行调用的,因此只能使用默认绑定,无法应用其他规则。在严格模式下,全局对象将无法使用默认绑定,因此this会绑定到undefined

注意这里有一个微妙但是非常重要的细节

隐式绑定

  • 对象属性引用链中只有最顶层或者说最后一层会影响调用位置,就像a.b.c.d.foo( ) 此事这个函数中的this被绑定到a.b.c.d这个对象上

  • 隐式丢失,类似于bar = a.foo( )而后以bar( )调用this会默认绑定到全局作用域 (假定bar当前是全局对象的属性

更微妙、更常见的情况发生在传入回调函数

函数的调用位置
}
var obj = {
     a: 1,foo: foo
};
var a = “frome globa”; // 全局变量属性a
doFoo(obj.foo); // 对于参数传入的隐式赋值 此时参数fn指向foo

我们可以看到,参数传递就是一种隐式赋值,其中发生的doFoo(obj.foo)函数调用,对于参数发生的赋值语句就是fn = obj.foo,参数中的fn调用位置使用的是this的默认绑定值,也就是this
其实,不仅是我们自己定义的函数,就连一些全局属性函数),当我们传入函数引用作为实参的时候,都会发生实参到行参的赋值,这个时机如果不是在严格模式下,函数调用话,this值都会默认绑定到全局对象(在浏览器中就是window对象)。

显示绑定

在分析隐式绑定时,我们必须在一个对象内部包含一个指向函数属性,并通过这个属性间接引用函数,从而把this间接(隐式)绑定到这个对象上。
终于提到了callapply,当我们不想通过隐式绑定那样来把this绑定到对象上的时候,我们需要使用callapply
如果你传入了一个原始值(字符串类型、布尔类型或者数字类型)来当作this绑定的对象,这个原始值会被转换成它的对象形式(new String( ) new Boolean( ) new number( )),这通常被称为装箱。

硬绑定

思考下面的代码

修改它的this

上面的代码中,我们在bar函数显示的绑定了foothis,这种绑定是不可以被修改的,因此我们称为硬绑定。
典型的硬绑定应用场景就是bind的使用

API调用的上下文

实际上并不存在所谓的“构造函数”,只有对于函数的“构造调用”。使用new调用函数,会自动执行下面的操作:

  • 创建一个全新的对象

  • 这个对象会被执行[[原型]]连接

  • 这个新对象会绑定到函数调用this

  • 如果函数没有返回其他对象,那么new表达式中的函数调用自动返回这个新对象。

优先级

this的优先级从高到低依次为:

  • 函数是否在new调用?如果是的话this绑定的是新创建的对象

  • 函数是否通过call、apply(显示绑定)?如果是的话,this绑定的事指定的对象

  • 函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this绑定的是那个上下文对象。

  • 如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定到全局对象。

但是,以上的规则只是符合大多数情况而言的,凡事都有例外:

  • 当把null或则undefined作为this的绑定对象传入callapply中,这些值在调用的时候会被忽略,实际应用的是默认绑定规则。

  • 对于函数的间接引用,在这种情况下,调用这个函数会应用默认绑定规则。

  • 软绑定会对指定的函数进行封装,首先检查调用时的this,如果this绑定到全局对象或者undefined,那就班制定的默认对象绑定到this,否则不会修改thisreturn fn.apply(!this : this === (window || golbal) ? obj : this)

this 词法

在ES6中有一种无法使用这些规则的特殊函数类型:箭头函数
箭头函数并不是使用function关键字定义的,而是使用被称为“胖箭头”的操作符=>定义的,箭头函数不使用this的四种标准规则,而是根据外层(函数或者全局)作用域来决定this

 {
         console.log(this.name); // 这里的this继承自外层的foo函数
     }
}
var a = {
     name: "John"
};
var b = {
     name: "Tom"
};
var bar = foo.call(a); // 将foo的this绑定到a
var.call(b); // John  返回的箭头函数不会应用call强制绑定

通过上面的代码我们可以很清楚直观的了解箭头函数绑定this的特殊性。
除了箭头函数之外还有一种方法显示的否定this机制,那就是通过在需要绑定的外层声明一个变量并显示的传入this,var self = this;

小结

为了判断一个运行中函数this绑定,需要找到这个函数的直接调用位子,然后通过上面介绍的4中绑定规则来依次进行判断,但是在这里要注意在ES6中的箭头函数并不使用这4种绑定规则。

猜你在找的JavaScript相关文章