你不知道的javascript--this绑定

原创
2021/11/25 20:19
阅读数 155
AI总结
  • 每个函数的this是在调用时被绑定的,完全取决于函数的调用位置

this绑定的规则

默认绑定,独立函数调用

函数默认绑定window

var a = 10
function foo () {
    var a = 20
    console.log(a) // 20
    console.log(this.a) // 10
    function boo () {
        console.log(this.a) // 10
    }
    boo()
}
foo()

隐式绑定

函数调用的位置是否有上下文对象,或者说是否被某个对象拥有或者包含,见下代码

function foo () {
    console.log(this.a)
}
var name = 'hui'
var obj = {
    name: 'zhan'
    foo: foo
}
obj.foo()  // 打印'zhan',由于foo调用的位置,foo是被obj这个对象所调用,也可以是被obj所包含,所以this指向就指向obj这个调用上下文

var bar = obj.foo
bar() // 打印'hui', 此时foo函数的引用赋值给bar变量,就相当于window.bar(),所以此时this指向window

setTimeout(foo, 0) //  打印'hui', foo调用的环境是window

硬绑定

  1. call、apply、bind bind给函数绑定了this之后,就无法改变bind返回的函数的this指向
    function foo (...ary) {
    this.a = ary.pop() // 取最后一个参数
    }
    var bar = {
        a: 1
    }
    var m = {
        a: 10
    }
    var fn = foo.bind(bar, 2) // fn函数的this已经指向bar,无法在改了
    m.fn = fn
    m.fn() // m中a依旧不变
    fn.call(m, 20) //  m中a依旧不变, fn内部的this无法改变
    
  2. API调用上下文,例如forEach,提供一个可选参数,指定回调函数this的指向
let obj = {
    name: 'zh'
}
[1,2,3].forEach(function(p) {
    console.log(this.name) // this指向obj
},obj)

new绑定

实际上并不存在所谓的“构造函数”,只是用new对函数的“构造调用”。 下面首先了解下用new调用一个函数的过程

  1. 创建一个对象
  2. 将该对象的__proto__指向函数的prototype
  3. 将函数的this指向该对象,并且执行该函数
  4. 如果函数的返回值是引用类型数据且非null,则new调用的结果就是该引用类型数据,如果是非引用类型数据或者是null,则new调用的结果就是该对象

在上述的第三步中,改变了函数this的指向

上面4种this绑定的优先级如何呢

  1. 默认绑定优先级最低
  2. new绑定比隐式绑定优先级高, 见下面代码
function foo (a) {
    this.a = a
}
var bar = {
    a: 1,
    foo: foo
}
var obj = new bar.foo(2)
console.log(obj.a) // 2
console.log(bar.a) // 1
  1. new绑定比硬绑定优先级高,但是new 无法和call、apply一起使用, 可以通过bind测试下
function foo (...ary) {
    this.a = ary.pop() // 取最后一个参数
}
var bar = {
    a: 1
}
var m = {
    a: 10
}
var fn = foo.bind(bar, 2)
m.fn = fn
m.fn()
var obj = new fn(3)
console.log(obj.a) // 3
console.log(bar.a) // 1

之所以要在new中使用硬绑定函数,主要目的是预先设置函数的一些参数,这样在使用new进行初始化时就可以只传入其余的参数

  1. 硬绑定比隐式绑定高

绑定的例外

  1. 硬绑定apply、call、bindthis指向null、undefined,则函数内部的this指向就按照默认的方式指定

软绑定

上面讲了bind执行后,返回的函数的this就无法改变,有没有一种可以软绑定呢,使返回的函数也是可以被重新改变其this指向,下面见代码:

if(!Function.prototype.sofeBind) {
    Function.prototype.sofeBind = function (obj, ...ary){
        var fn = this
        var bindFn = function (...params) {
            var that = this === window ? obj : this  // 如果返回函数this绑定到window上,则改变this指向obj,否则则不会修改this指向
            console.log(that)
            return fn.apply(that, [...ary, ...params])
        }
        return bindFn
    }
}
// 见使用
var bar = {
    a: 1
}
var mak = {
    a: 2
}
function foo(a) {
    this.a = a
}
var fn = foo.sofeBind(bar)
fn(3)
console.log(bar.a) // 3
mak.foo = fn
mak.foo(4)  // **看这里,这里改变fn的this的指向,不像bind无法改变返回函数的this的指向**
console.log(mak.a) // 4

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
0 收藏
0
分享
AI总结
返回顶部
顶部