zoukankan      html  css  js  c++  java
  • 720 JavaScript函数的this指向

    为什么需要this?


    this指向什么呢?


    this到底指向什么呢?


    规则一:默认绑定


    规则二:隐式绑定


    规则三:显示绑定


    call、apply、bind


    内置函数的绑定思考


    new绑定


    规则优先级


    this规则之外 – 忽略显示绑定


    this规则之外 - 间接函数引用


    箭头函数 arrow function


    箭头函数的编写优化


    this规则之外 – ES6箭头函数


    ES6箭头函数this


    01_this的作用.js

    // 从某些角度来说, 开发中如果没有this, 很多的问题我们也是有解决方案
    // 但是没有this, 会让我们编写代码变得非常的不方便
    var obj100 = {
      name: 'why',
      eating: function () {
        console.log(this.name + '在吃东西')
      },
      running: function () {
        console.log(this.name + '在跑步')
      },
      studying: function () {
        console.log(this.name + '在学习')
      }
    }
    
    var info = {
      name: 'why',
      eating: function () {
        console.log(this.name + '在吃东西')
      },
      running: function () {
        console.log(this.name + '在跑步')
      },
      studying: function () {
        console.log(this.name + '在学习')
      }
    }
    
    var person = {
      name: 'kobe',
      eating: function () {
        console.log(this.name + '在吃东西')
      },
      running: function () {
        console.log(this.name + '在跑步')
      },
      studying: function () {
        console.log(this.name + '在学习')
      }
    }
    
    obj100.eating()
    obj100.running()
    obj100.studying()
    

    02_this在全局作用域指向什么.js

    // 在大多数情况下, this都是出现在函数中
    // 在全局作用域下
    // 浏览器: window(globalObject)
    // Node环境: {}
    console.log(this)
    // console.log(window)
    

    03_同一个函数的this的不同.js

    // this指向什么, 跟函数所处的位置是没有关系的
    // 跟函数被调用的方式是有关系
    
    function foo() {
      console.log(this)
    }
    
    // 1.直接调用这个函数
    foo()
    
    // 2.创建一个对象, 对象中的函数指向foo
    var obj = {
      name: 'why',
      foo: foo
    }
    
    obj.foo()
    
    // 3.apply调用
    foo.apply('abc')
    

    04_绑定规则一-默认绑定.js

    // 默认绑定: 独立函数调用
    // 1.案例一:
    function foo() {
      console.log(this)
    }
    
    foo()
    
    // 2.案例二:
    function foo1() {
      console.log(this)
    }
    
    function foo2() {
      console.log(this)
      foo1()
    }
    
    function foo3() {
      console.log(this)
      foo2()
    }
    
    foo3()
    
    // 3.案例三:
    var obj = {
      name: 'why',
      foo: function () {
        console.log(this)
      }
    }
    
    var bar = obj.foo
    bar() // window
    
    // 4.案例四:
    function foo() {
      console.log(this)
    }
    
    var obj = {
      name: 'why',
      foo: foo
    }
    
    var bar = obj.foo
    bar() // window
    
    // 5.案例五:
    function foo() {
      function bar() {
        console.log(this)
      }
      return bar
    }
    
    var fn = foo()
    fn() // window
    
    var obj = {
      name: 'why',
      eating: fn
    }
    
    obj.eating() // 隐式绑定,{name: "why", eating: ƒ}
    

    05_绑定规则二-隐式绑定.js

    // 隐式绑定: object.fn()
    // object对象会被js引擎绑定到fn函数的中this里面
    
    var name = '哈哈哈'
    
    function foo() {
      console.log(this)
    }
    
    // 独立函数调用
    foo()
    
    // 1.案例一:
    var obj = {
      name: 'why',
      foo: foo
    }
    
    obj.foo() // obj对象
    
    // 2.案例二:
    var obj = {
      name: 'why',
      eating: function () {
        console.log(this.name + '在吃东西')
      },
      running: function () {
        console.log(obj.name + '在跑步')
      }
    }
    
    obj.eating()
    obj.running()
    
    var fn = obj.eating
    fn() // 哈哈哈在吃东西
    
    // 3.案例三:
    var obj1 = {
      name: 'obj1',
      foo: function () {
        console.log(this)
      }
    }
    
    var obj2 = {
      name: 'obj2',
      bar: obj1.foo
    }
    
    obj2.bar() // obj2对象
    

    06_绑定规则三-显示绑定-apply-call.js

    function foo() {
      console.log('函数被调用了', this)
    }
    
    // 1.foo直接调用和call/apply调用的不同在于this绑定的不同
    // foo直接调用指向的是全局对象(window)
    foo()
    
    var obj = {
      name: 'obj'
    }
    
    // call/apply是可以指定this的绑定对象
    foo.call(obj)
    foo.apply(obj)
    foo.apply('aaaa')
    
    // 2.call和apply有什么区别?
    function sum(num1, num2, num3) {
      console.log(num1 + num2 + num3, this)
    }
    
    sum.call('call', 20, 30, 40)
    sum.apply('apply', [20, 30, 40])
    
    // 3.call和apply在执行函数时, 是可以明确的绑定this, 这个绑定规则称之为显示绑定
    

    07_绑定规则三-显示绑定-bind.js

    function foo() {
      console.log(this)
    }
    
    // foo.call("aaa")
    // foo.call("aaa")
    // foo.call("aaa")
    // foo.call("aaa")
    
    // 默认绑定和显示绑定bind冲突: 优先级(显示绑定)
    
    var newFoo = foo.bind('aaa')
    
    newFoo()
    newFoo()
    newFoo()
    
    var bar = foo
    console.log(bar === foo)
    console.log(newFoo === foo)
    

    08_绑定规则四-new绑定.js

    // 我们通过一个new关键字调用一个函数时(构造器), 这个时候this是在调用这个构造器时创建出来的对象
    // this = 创建出来的对象
    // 这个绑定过程就是new 绑定
    
    function Person(name, age) {
      this.name = name
      this.age = age
    }
    
    var p1 = new Person('why', 18)
    console.log(p1.name, p1.age)
    
    var p2 = new Person('kobe', 30)
    console.log(p2.name, p2.age)
    
    var obj = {
      foo: function () {
        console.log(this)
      }
    }
    

    01_一些函数的this分析.js

    // 1.setTimeout
    function hySetTimeout(fn, duration) {
      fn.call("abc")
    }
    
    hySetTimeout(function() {
      console.log(this) // window
    }, 3000)
    
    setTimeout(function() {
      console.log(this) // window
    }, 2000)
    
    // 2.监听点击
    const boxDiv = document.querySelector('.box')
    boxDiv.onclick = function () {
      console.log(this)
    }
    
    boxDiv.addEventListener('click', function () {
      console.log(this)
    })
    
    boxDiv.addEventListener('click', function () {
      console.log(this)
    })
    
    // 3.数组.forEach/map/filter/find
    var names = ['abc', 'cba', 'nba']
    
    names.forEach(function (item) {
      console.log(item, this)
      console.log(this)
    }, 'abc')
    
    console.log('---------------------')
    
    names.map(function (item) {
      console.log(item, this)
      console.log(this)
    }, 'cba')
    

    02_优先级-显示高于隐式绑定.js

    var obj = {
      name: 'obj',
      foo: function () {
        console.log(this)
      }
    }
    
    obj.foo()
    
    // 1.call/apply的显示绑定高于隐式绑定
    obj.foo.apply('abc')
    obj.foo.call('abc')
    
    // 2.bind的优先级高于隐式绑定
    var bar = obj.foo.bind('cba')
    bar()
    
    // 3.更明显的比较
    function foo() {
      console.log(this)
    }
    
    var obj = {
      name: 'obj',
      foo: foo.bind('aaa')
    }
    
    obj.foo()
    

    03_优先级-new高于隐式绑定.js

    var obj = {
      name: "obj",
      foo: function() {
        console.log(this)
      }
    }
    
    // new的优先级高于隐式绑定
    var f = new obj.foo()
    

    04_优先级-new高于显示绑定.js

    // 结论: new关键字不能和apply/call一起来使用 【因为apply、call是直接执行函数,而bind是返回一个新的函数。】
    
    // new的优先级高于bind
    function foo(name, age) {
      console.log(this)
      this.name = name
      this.age = age
    }
    
    var bar = foo.bind('aaa')
    var obj = new bar(111, 222)
    
    // new绑定 > 显示绑定(apply/call/bind) > 隐式绑定(obj.foo()) > 默认绑定(独立函数调用)
    

    05_特殊绑定-忽略显示绑定.js

    function foo() {
      console.log(this)
    }
    
    foo.apply('abc')
    foo.apply({})
    
    // apply/call/bind: 当传入null/undefined时, 自动将this绑定成全局对象
    foo.apply(null)
    foo.apply(undefined)
    
    var bar = foo.bind(null)
    bar()
    

    06_特殊绑定-间接函数引用.js

    // 争论: 代码规范 ;
    
    var obj1 = {
      name: 'obj1',
      foo: function () {
        console.log(this)
        console.log(this.name)
      }
    }
    
    var obj2 = {
      name: 'obj2'
    }
    
    obj2.bar = obj1.foo
    obj2.bar()
    ;(obj2.bar = obj1.foo)()
    

    07_箭头函数的使用解析.js

    // 1.编写箭头函数
    // 1> (): 参数
    // 2> =>: 箭头
    // 3> {}: 函数的执行体
    var foo = (num1, num2, num3) => {
      console.log(num1, num2, num3)
      var result = num1 + num2 + num3
      console.log(result)
    }
    
    function bar(num1, num2, num3) {}
    
    // 高阶函数在使用时, 也可以传入箭头函数
    var nums = [10, 20, 45, 78]
    nums.forEach((item, index, arr) => {})
    
    // 箭头函数有一些常见的简写:
    // 简写一: 如果参数只有一个, ()可以省略
    nums.forEach(item => {
      console.log(item)
    })
    
    // 简写二: 如果函数执行体只有一行代码, 那么{}也可以省略
    // 强调: 并且它会默认将这行代码的执行结果作为返回值
    nums.forEach(item => console.log(item))
    
    var newNums = nums.filter(item => item % 2 === 0)
    console.log(newNums)
    
    // filter/map/reduce
    var result = nums
      .filter(item => item % 2 === 0)
      .map(item => item * 100)
      .reduce((preValue, item) => preValue + item)
    
    console.log(result)
    
    // 简写三: 如果一个箭头函数, 只有一行代码, 并且返回一个对象, 这个时候如何编写简写?
    // var bar = () => {
    //   return { name: "why", age: 18 }
    // }
    
    var bar = () => ({ name: 'why', age: 18 })
    

    08_箭头函数的this获取.js

    // 1.测试箭头函数中this指向
    var name = 'why'
    
    var foo = () => {
      console.log(this)
    }
    
    foo() // window
    
    var obj = { foo: foo }
    obj.foo() // window
    foo.call('abc') // window
    
    // 2.应用场景
    var obj = {
      data: [],
      getData: function () {
        // 发送网络请求, 将结果放到上面data属性中
        // 在箭头函数之前的解决方案
        // var _this = this
        // setTimeout(function() {
        //   var result = ["abc", "cba", "nba"]
        //   _this.data = result
        // }, 2000);
        // 箭头函数之后
        setTimeout(() => {
          var result = ['abc', 'cba', 'nba']
          this.data = result
        }, 2000)
      }
    }
    
    obj.getData()
    

    09_this练习题一.js

    var name = 'window'
    
    var person = {
      name: 'person',
      sayName: function () {
        console.log(this.name)
      }
    }
    
    function sayName() {
      var sss = person.sayName
      sss() // window: 独立函数调用
      person.sayName() // person: 隐式调用
      person.sayName() // person: 隐式调用
      ;(b = person.sayName)() // window: 赋值表达式(独立函数调用)
    }
    
    sayName()
    

    10_this练习题二.js

    var name = 'window'
    
    var person1 = {
      name: 'person1',
      foo1: function () {
        console.log(this.name)
      },
      foo2: () => console.log(this.name),
      foo3: function () {
        return function () {
          console.log(this.name)
        }
      },
      foo4: function () {
        return () => {
          console.log(this.name)
        }
      }
    }
    
    var person2 = { name: 'person2' }
    
    person1.foo1() // person1(隐式绑定)
    person1.foo1.call(person2) // person2(显示绑定优先级大于隐式绑定)
    
    person1.foo2() // window(不绑定作用域, 上层作用域是全局)
    person1.foo2.call(person2) // window
    
    person1.foo3()() // window(独立函数调用)
    person1.foo3.call(person2)() // window(独立函数调用)
    person1.foo3().call(person2) // person2(最终调用返回函数式, 使用的是显示绑定)
    
    person1.foo4()() // person1(箭头函数不绑定this, 上层作用域this是person1)
    person1.foo4.call(person2)() // person2(上层作用域被显示的绑定了一个person2)
    person1.foo4().call(person2) // person1(上层找到person1)
    

    11_this练习题三.js

    var name = 'window'
    
    function Person(name) {
      this.name = name
      ;(this.foo1 = function () {
        console.log(this.name)
      }),
        (this.foo2 = () => console.log(this.name)),
        (this.foo3 = function () {
          return function () {
            console.log(this.name)
          }
        }),
        (this.foo4 = function () {
          return () => {
            console.log(this.name)
          }
        })
    }
    
    var person1 = new Person('person1')
    var person2 = new Person('person2')
    
    person1.foo1() // person1
    person1.foo1.call(person2) // person2(显示高于隐式绑定)
    
    person1.foo2() // person1 (上层作用域中的this是person1)
    person1.foo2.call(person2) // person1 (上层作用域中的this是person1)
    
    person1.foo3()() // window(独立函数调用)
    person1.foo3.call(person2)() // window
    person1.foo3().call(person2) // person2
    
    person1.foo4()() // person1
    person1.foo4.call(person2)() // person2
    person1.foo4().call(person2) // person1
    
    var obj = {
      name: 'obj',
      foo: function () {}
    }
    

    12_this练习题四.js

    var name = 'window'
    
    function Person (name) {
      this.name = name
      this.obj = {
        name: 'obj',
        foo1: function () {
          return function () {
            console.log(this.name)
          }
        },
        foo2: function () {
          return () => {
            console.log(this.name)
          }
        }
      }
    }
    
    var person1 = new Person('person1')
    var person2 = new Person('person2')
    
    person1.obj.foo1()() // window
    person1.obj.foo1.call(person2)() // window
    person1.obj.foo1().call(person2) // person2
    
    person1.obj.foo2()() // obj
    person1.obj.foo2.call(person2)() // person2
    person1.obj.foo2().call(person2) // obj
    
    
    // 
    
    // 上层作用域的理解
    // var obj = {
    //   name: "obj",
    //   foo: function() {
    //     // 上层作用域是全局
    //   }
    // }
    
    // function Student() {
    //   this.foo = function() {
    
    //   }
    // }
    

  • 相关阅读:
    简单实用的Asp.net生成静态页方法
    第八章 、数据存储 Stroe
    第七章 、使用表格控件
    第九章 、 TreePanel
    mysql 安装时的第三第四步出错解决方法
    asp.net 水晶报表 VS2008 10.5版本
    关于log4net的使用
    SQL2000时安装挂起的解决方法
    linux下安装Sublime Text3并将它的快捷方式放进启动器中和卸载Sublime
    Linux下安装配置jdk
  • 原文地址:https://www.cnblogs.com/jianjie/p/15564169.html
Copyright © 2011-2022 走看看