js的this绑定规则以及箭头函数

目录

  • 调用位置
  • 默认绑定
  • 隐式绑定
    • 隐式丢失
  • 显式绑定
    • call
    • apply
    • bind
  • new绑定
  • 装箱
  • 绑定优先级
  • this规则之外
    • 忽略显式绑定
    • 间接函数引用
  • 箭头函数

调用位置

从字面意思上来理解,this似乎是指向自己的
然而在JavaScript中,this并不是绑定到自身
可以看这一个例子

        function foo(num) {
            console.log("num:" + num)
            this.count++
        }
        foo.count = 0
        for (var i = 0; i < 10; i++) {
            if (i > 5) {
                foo(i)
            }
        }
        console.log(foo.count)

结果
js的this绑定规则以及箭头函数_第1张图片

显然从字面意思来理解this指向自身是错误的认知
事实上this并不是编写时绑定的,而是运行时绑定
我们判断this会绑定到什么首先就要分析它的调用位置

        function foo() {
            bar()//此时的调用栈为:全局--》foo--》bar
            function bar() {
                baz()//此时的调用栈为:全局--》foo--》bar--》baz
                function baz() {

                }
            }
        }
        foo()//此时的调用栈为:全局--》foo

在寻找到它的调用位置之后我们就需要判断这里的this适用于以下四条规则中的哪一条

默认绑定

独立调用函数时适用此条,也可以看成当其他三条规则都不适用时适用这条规则
独立函数调用可以看成函数没有被绑定到某个对象上进行调用
例如下面这个例子

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

结果
结果

此时的this绑定了全局对象window
值得注意的是,在严格模式下,这里的this会绑定到undefined

隐式绑定

另一种比较常见的绑定方式为通过某个对象调用

        function foo() {
            console.log(this)
        }
        var obj = {
            name: "obj",
            foo: foo
        }
        obj.foo()

结果
结果
此时的this绑定到了obj对象上了

隐式丢失

被隐式绑定的this有时也会应用默认绑定规则
例如下面这段代码

        function foo() {
            console.log(this)
        }
        var obj = {
            name: "obj",
            foo: foo
        }
        var bar = obj.foo
        bar()

结果
可以看到this绑定到了window上,这就是隐式丢失

显式绑定

在上面的代码中我们可以看到隐式绑定也会有可能出现隐式丢失的现象,为了确保我们的this能正确的绑定到我们想要的对象上可以使用显式绑定
显式绑定可以通过传递参数的形式来把this绑定到参数
显示绑定有具体三种方式

call

call方法需要传入一个参数来作为this的绑定对象,如果函数需要参数则在后面用逗号隔开

        function foo(name, age) {
            console.log(this)
            console.log(name + " " + age)
        }
        var obj = {
            name: "obj",
        }
        foo.call(obj, "张三", 18)

结果
结果
这里可以看到this绑定到了obj上了
注意:call方法是立即执行

apply

apply方式与call方法相似,但传递参数的方式不同

        function foo(name, age) {
            console.log(this)
            console.log(name + " " + age)
        }
        var obj = {
            name: "obj",
        }
        foo.apply(obj, ["张三", 18])

结果
结果
这里可以看到this绑定到了obj上了
注意:apply方法也是立即执行的

bind

无论是apply绑定或者是call绑定,都不能完全解决this绑定丢失问题
当将函数作为参数传递给其他函数之后,很难知道其他函数会怎么调用这个函数
函数的this指向此时也不受你控制
为了解决这个问题,我们可以使用bind方法

        function foo() {
            console.log(this)
        }
        function bar(fn) {
            fn.call(obj)
        }
        var obj = {
            name: "obj",
        }
        bar(foo.bind(window))

结果
结果
bind方法不会立即执行函数,而是会返回一个新函数
新函数的this将会指向你所指定的对象
以后this的指向也始终会是预期

new绑定

当我们使用new关键字时,会执行以下几件事情

  1. 创建一个空对象
  2. 将_空对象_的this绑定到这个空对象
  3. 执行函数体里的代码
        function foo() {
            console.log(this)
            this.a = 2
        }
        var obj = {
            name: "obj",
        }
        var bar = new foo()
        console.log(bar.a)

结果
结果

可以看到此时的this已经绑定到了foo

装箱

无论是call还是apply还是bind,当我们传入一个原始值(如数字字符串)时会发生什么呢

        function foo() {
            console.log(this)
            this.a = 2
        }
        var obj = {
            name: "obj",
        }
        foo.call(123)
        foo.apply("123")

结果
结果
得到的结论就是如果你传入了一个原始值来当作this的绑定对象,这个原始值会被转换成它的对象形式
new Number()new String()等等
这通常被称为装箱

绑定优先级

当一个函数涉及了多条规则,规则与规则之间则会按照自己的优先级生效

  1. 毋庸置疑的是默认绑定优先级最低,是其他规则都不适用情况下的兜底条款

  2. 显示绑定优先级高于隐式绑定

        function foo() {
            console.log(this.name)
            this.a = 2
        }
        var obj = {
            name: "obj",
            foo: foo
        }
        var obj2 = {
            name: "obj2",
            foo: foo
        }
        obj.foo.call(obj2)
    

    结果
    结果

  3. new绑定比隐式绑定优先级高

        function foo() {
            this.a = 2
        }
        var obj = {
            name: "obj",
            a: 4,
            foo: foo
        }
        var obj2 = new obj.foo()
        console.log(obj2.a)
    

    结果
    结果

  4. new绑定无法与call/apply方法一起使用,但我们可以通过与bind方法比较来得到结果

        function foo() {
            console.log(this)
        }
        var obj = {
            name: "obj",
        }
        var obj2 = foo.bind(obj)
        new obj2
    

    结果
    结果
    我们可以知道new绑定优先级高于显式绑定

this规则之外

在现实使用中,我们总有些语法超出了规则之外

忽略显式绑定

当传入的参数是null或者为undefined时,这个显式绑定忽略,使用默认绑定

        function foo() {
            console.log(this)
        }
        foo.call(null)
        foo.call(undefined)

结果
结果

间接函数引用

创建一个函数的间接引用,这种情况适用默认规则

        function foo() {
            console.log(this)
        }
        var obj = {
            foo: foo
        }
        var obj2 = {
            name: "obj2"
        };
        (obj2.foo = obj.foo)()

结果
结果

箭头函数

ES6中提出了一种新函数,他的名字叫箭头函数
箭头函数中,其实并没有this
所以并不适用上面提到的四条规则
箭头函数中的this是由其外部作用域来决定的
当箭头函数中遇到this时,箭头函数便会去它的外层作用域寻找

        var obj = {
            bar: function () {
                var foo = () => {
                    console.log(this)
                }
                return foo
            }
        }
        var baz = obj.bar()
        baz()

结果
结果

你可能感兴趣的:(网页,前端,javascript)