• 注册
  • 前端综合 关注:4 内容:4

    js之this简单总结

  • 查看作者
  • 打赏作者
  • 拉黑名单
    • 1 什么是this

      1.1 this 的指向

      this 是 js 中的关键字,看起来很混乱,但其实很好理解,用网上通用的一句话来说就是: this 的指向在函数定义时确定不了,只有函数指向的时候才能确定 this 到底指向谁,也即是说this的卒中指向的是那个调用他的对象

      总结起来就六个字
      谁调用,指向谁

      例子1

      function foo() {
          var name = "我爱js";
          console.log("this", this); // this window
          console.log("name", this.name)// this undefined}foo()

      为什么这里的 this 是window对象,因为我们是在全局作用域中调用的,实际上是省略了 window.
      看如下代码的调用方式,跟上一个完全相同

      例子2

      function foo() {
          var name = "我爱js";
          console.log("this", this); // this window
          console.log("name", this.name)// this undefined}window.foo()

      效果跟上面是完全相同的

      例子3

      var foo = {
          name: "JavaScript",
          child: function () {
              console.log("this", this) // this {name: "JavaScript", child: ƒ}
              console.log("name", this.name) //name JavaScript
          }}foo.child()

      在这里调用者是 foo, 所以this指向的是 foo
      然而事实是这样吗,再看看上面的例子

      例子4

      var foo = {
          name: "JavaScript",
          child: function () {
              console.log("this", this) // this {name: "JavaScript", child: ƒ}
              console.log("name", this.name) //name JavaScript
          }}window.foo.child()

      这里最终调用者是window,但是this并没有指向window,这是怎么回事?难道是六字真言有问题?要弄懂这个问题,我们需要再看几个例子

      例子5

      var foo = {
          a: 1,
          b: {
              a: 2,
              fn: function() {
                  console.log(this.a) // 2
              } 
          }}foo.b.fn()

      而且这个例子中的this也同样没指向foo, 看到这里,可能会有疑惑,会推翻六字箴言,但是,如果再补充几句话,就会消除歧义

      1. 非严格模式下,如果一个函数中有this,并且这个this没有被上一级调用,那么这个this的指向就是window,具体参考例子1,例子2

      2. 如果一个函数中有this,这个函数被上一级对象调用,那么this就指向上一级对象,具体参考例子3

      3. 如果一个函数有this,假如调用这个函数的有多级对象,那么this指向的是调用它的上一级对象,具体参考例子5

      我们再来验证一下情况3
      例子 6

      var foo = {
          a: 1,
          b: {
              //a: 2,
              fn: function() {
                  console.log(this.a) // undefined
              } 
          }
      }
      
      foo.b.fn()

      此时,我们看到,输入的值是undefined,也就是说,这个时候this指向的是b的作用域,而b中没有定义a,所以输出的是undefined
      然而一切并不是依靠想象,总有情况例外,我们看下一个例子

      例子7

      var foo = {
          a: 1,
          b: {
              a: 2,
              fn: function() {
                  console.log(this) // window
                  console.log(this.a) // undefined
              } 
          }}var tm = foo.b.fntm()

      从这个例子中我们看到,this指向的是window,这是怎么回事?其实很好理解,还记得六字箴言吗?谁调用,指向谁,这一句var tm = foo.b.fn只是进行了赋值,把 fn赋值给了全局变量 tm ,然而最终的调用时机是在 window作用域调用的函数 tm(), 所以 this的指向是window对象。至此, this 的指向已经完毕

      1.2 this指向总结

      就六个字 谁调用,指向谁,另加几个条件

      1. 非严格模式下,如果一个函数中有this,并且这个this没有被上一级调用,那么这个this的指向就是window

      2. 如果一个函数中有this,这个函数被上一级对象调用,那么this就指向上一级对象

      3. 如果一个函数有this,假如调用这个函数的有多级对象,那么this指向的是调用它的上一级对象

      2 改变 this 的指向

      通常情况下,我们想使用其它环境变相下的状态,这就需要借助this来实现,常用改变this指向的有以下几种方式可以实现

      1. 构造函数

      2. call

      3. apply

      4. bind

      2.1 构造函数改变this的指向

      先看这个例子

      例子8

      function Foo () {
          this.name = 'Justin'}var f = new Foo()console.log(f.name) // Justin

      在使用 new 关键字调用构造函数时,会依次执行

      1. 首先创建一个新对象 f,并把构造函数的 prototype赋值给这个新对象的 __proto__

      2. 将这个新对象 f 赋值给this,并执行构造函数

      3. 如果函数返回了其它对象,那么 new 表达式中的函数调用会自动返回这个新对象, 否则忽略,这句话的意思是,如果函数没有 return一个对象, this代表的就是 new 出来的实例,否则,this指向的是返回的新对象

      假如函数中有 return, 来看看this的指向?
      看下面这个例子
      例子9

      function Foo () {
          this.name = 'Justin'
          return {}}var f = new Foo()console.log(f.name) // undefined

      在这里, 输出的是undefined,也就是说,this根本没有指向 f, 那么this到底指向哪里了呢
      其实,如果一个返回值是对象,那么this指向的就是返回的对象, 如果反回的不是对象,那么指向的还是指向函数的实例, null除外

      例子10
      利用下面几个例子验证

      function Foo () {
          this.name = 'Justin'
          return function(){}}var f = new Foo()console.log(f.name) // undefined

      例子11

      function Foo () {
          this.name = 'Justin'
          return 1}var f = new Foo()console.log(f.name) // Justin

      例子12

      function Foo () {
          this.name = 'Justin'
          return undefined}var f = new Foo()console.log(f.name) // Justin

      例子13

      function Foo () {
          this.name = 'Justin'
          return null}var f = new Foo()console.log(f.name) // Justin

      以上跟第三点总结相符合,至此,构造函数改变this指向解析完毕

      2.2 通过 call, apply, bind 改变 this 的指向

      例子13

      var w = "流星火雨"var obj = {
          name: "黄忠",
          w: this.w,
          state: function(name, ow){
              console.log(`${this.name} 的 w 技能是 ${this.w}`)
          }}var skills = {
          name: '马岱',
          w: "紫电箭雨"}
          obj.state() // 忠 的 w 技能是 undefined
          obj.state.call(skills) //马岱 的 w 技能是 紫电箭雨
          obj.state.apply(skills) //马岱 的 w 技能是 紫电箭雨
          obj.state.bind(skills) //马岱 的 w 技能是 紫电箭雨

      从上面看出来, call, apply, bind把this的指向改变为call, apply, bind所传入的第一个参数, 除此之外,他们是没有什么区别的

      2.3 call, apply, bind的区别

      先看一个例子

      var w = "流星火雨"var obj = {
          name: "黄忠",
          w: this.w,
          state: function(HeroType, HP){
              console.log(`${this.name} 的 w 技能是 ${this.w}, 他是一个 ${HeroType}, 他的血量是${HP}`)
          }}var skills = {
          name: '马岱',
          w: "紫电箭雨"}
          obj.state() // 忠 的 w 技能是 undefined
          obj.state.call(skills, "法师", 100) //马岱 的 w 技能是 紫电箭雨
          obj.state.apply(skills, ["法师", 100]) //马岱 的 w 技能是 紫电箭雨
          obj.state.bind(skills, "法师", 100)() //马岱 的 w 技能是 紫电箭雨

      不同的是, apply第二个参数接受的是一个数组, call 直接按参数排列就可以了,而 bind 跟call相似,只不过后面多了个 ()

      mark

      回复
      你需要登录,才能进行发帖操作
    • 帖子间隔 侧栏位置: