晴明的博客园 GitHub      CodePen      CodeWars     

[js] this (2)

call、apply 、函数执行的本质

严格模式下,fn 里的 this 就是 call 的第一个参数,也就是 undefined。

      "use strict"
      function fn(a, b) {
        console.log(this)
      }
      fn(1, 2)//undefined
      //等价于
      fn.call(undefined, 1, 2)//undefined
      fn.apply(undefined, [1, 2])//undefined

      //window
      window.fn(1, 2)//window;

在非严格模式下, call 传递的第一个参数如果是 undefined 或者 null, 那 this 会自动替换为 Window 对象

      function fn(a, b) {
        console.log(this)
      }
      fn(1, 2)//window
      //等价于
      fn.call(undefined, 1, 2)//window
      fn.apply(undefined, [1, 2])//window

more

xx.yy可以理解为yy的this绑定在了xx

      var obj = {
        fn: function (a, b) {
          console.log(this)
        },
        child: {
          fn2: function () {
            console.log(this)
          }
        }
      }
      obj.fn(1, 2)//obj
      //等价于
      obj.fn.call(obj, 1, 2)         // 所以 this 是 obj
      obj.fn.apply(obj, [1, 2])//obj

      obj.child.fn2()//obj.child  上一级?
      //等价于
      obj.child.fn2.call(obj.child)    // 所以 this 是 obj.child
      let name = 'hello';
      let people = {
        name: 'world',
        sayName: function () {
          console.log(this.name)
        }
      }
      let sayAgain = people.sayName;
      function sayName() {
        console.log(this)
        console.log(this.name)
      }

      sayName()//hello   可不可以理解为window.sayName
      //解析:相当于 sayName.call(undefined) 
      //因为是非严格模式,所以 this 被替换成 Window

      people.sayName()//world
      //解析: 相当于 `people.sayName.call(people)` 

      sayAgain()//hello
      //解析: 相当于 `sayAgain.call(undefined)` 
      //this是在运行时进行绑定的,并不是在编写时绑定的
      //因为是非严格模式,所以 this 被替换成 Window
      var arr = []
      for (var i = 0; i < 3; i++) {
        arr[i] = function () { console.log(this) }
      }
      var fn = arr[0]
      arr[0]()//arr
      /*
      解析: 因为函数是个特殊的对象,
      所以 arr 相当于 { '0': function(){}, '1': function(){}, '2': function(){}, length:3}
      arr[0]相当于 `arr['0']` 相当于 `arr.0` (当然这种写法不符合规范),
      所以 arr[0]等价于 arr.0.call(arr), this就是 arr
      */

      fn()//window
      /*
      解析: 相当于 `fn.call(undefined)`, 所以 fn 里面的 this 是 Window
      */

实际点的例子

      var a = {
        name: 'js',
        log: function () {
          //所以这里的
          console.log(this)//{name: "js", log: ƒ}
          function setName() {
            this.name = 'javaScript'
            console.log(this) //window
          }
          setName()//而这里相当于window.setName() 或者 setName.call(undefined) ?
        }
      }
      a.log()  //log的this绑定到了a

bind

bind 的作用和 call 与 apply 类似,区别在于使用上,bind 的执行的结果返回的是绑定了一个对象的新函数.
这也就是为什么对函数里传参时,要使用bind而不是call/apply了,
call实际上在绑定的同时执行了,而bind才是返回绑定后的函数。

      var obj = { name: 'qm' }
      function sayName() {
        console.log(this.name)
      }
      // 这里 fn 还是一个函数,功能和 sayName 一模一样,
      //区别只在于它里面的 this 是 obj
      var fn = sayName.bind(obj)
      fn() // qm
      var fn2 = sayName.call(obj);
      //fn2()//error
      var app = {
        container: document.querySelector('body'),
        bind: function () {
          //点击的时候会执行 sayHello,
          //sayHello 里面的 this 代表 body 对象
          this.container.addEventListener('click', this.sayHello)

          //点击的时候会执行 sayHello,
          //sayHello 里面的 this 代表 app 对象
          this.container.addEventListener('click', this.sayHello.bind(this))
        },
        sayHello: function () {
          console.log(this)
        }
      }
      app.bind()

new

this是在运行时进行绑定的,并不是在编写时绑定的

      function foo() {
        console.log(this.a)
      }
      var a = 2
      foo()//2 这里的this指向window

      var obj = {
        a: 3,
        foo: foo
      }
      obj.foo() //3 这里的this指向obj

      //这里类似把foo.bind(c)了??
      var c = new foo()//undefined
      c.a = 4
      console.log(c.a)//4

箭头函数

箭头函数其实是没有 this 的,其取决于所在的环境下的 this。
也可以理解为:这个函数中的 this 只取决于他外面的第一个不是箭头函数的函数的 this。
this 一旦绑定了上下文,就不会被任何代码改变(如bind的使用)。
感觉箭头函数很像是被bind后生成的函数。

      let app = {
        fn1: function (a) {
          console.log(this)
        },
        fn2(a) {
          console.log(this)
        },
        fn3: (a) => {
          console.log(this)
        }
      }
      app.fn1();//app
      app.fn2();//app
      app.fn3();//window

      app.fn2.call(app)
      app.fn3.call(window)
      function a() {
        return () => {
          return () => {
            console.log(this)//window
          }
        }
      }
      console.log(a()()()) //return undefined?
      var app = {
        init() {
          //这一层的this 是app
          var menu = {
            init: () => {
              console.log(this) //app
            },
            bind() {
              console.log(this)//menu
            }
          }
          menu.init()
          /*相当于  menu.init.call(menu 所在的环境下的 this)  , 所以 init 里面的 this 也就是 app。
          */
          menu.bind()
          /*相当于 menu.bind.call(menu),也就是 menu,所以 bind 里面的 this 就是 menu
          */
        }
      }
      app.init()
      var app = {
        init: () => {
          //这一层的this 是window
          var menu = {
            init: () => {
              console.log(this) //window
            },
            bind() {
              console.log(this)//menu
            }
          }
          menu.init()
          /*相当于  menu.init.call(menu 所在的环境下的 this)  , 所以 init 里面的 this 也就是 app。
          */
          menu.bind()
          /*相当于 menu.bind.call(menu),也就是 menu,所以 bind 里面的 this 就是 menu
          */
        }
      }
      app.init()
var app = {
        fn1() {
          setTimeout(function () {
            console.log(this)
          }, 10)
          //过10ms 后执行
          //fn.call(undefined) ,所以输出 Window
        },
        fn2() {
          setTimeout(() => {
            console.log(this)
          }, 20)
          //过20ms 执行箭头函数
          //箭头函数里面没资格有 自己的 this,借用 setTimeout 外面的 this,也就是 app
        },
        fn3() {
          setTimeout((function () {
            console.log(this)
          }).bind(this), 30)
          // 创建了一个新函数,这个新函数里面绑定了 外面的this,也就是 app
          // 20 ms 后执行新函数,输出 this,也就是刚刚绑定的 app    
        },
        fn4: () => {
          setTimeout(() => {
            console.log(this)
          }, 40)
          //过40ms 执行箭头函数
          //箭头函数里面没资格有 this,用 setTimeout 外面的 this
          //setTimeout 所在的 fn4也是箭头函数,没资格拥有自己的 this,借用外面的 this ,
          //app的this是 Window     
        }
      }
      app.fn1()//window
      app.fn2()//app
      app.fn3()//app
      app.fn4()//window

posted @ 2018-05-02 21:49  晴明桑  阅读(135)  评论(0编辑  收藏  举报