JS中this指向问题

125 阅读6分钟

  1.  函数在调用时js会默认给this绑定一个值
  2. this的绑定和定义的位置无关
  3. this的绑定和调用方式以及调用的位置有关
  4. this是在运行时被绑定的

JavaScript中this指向分类:

  1. 默认绑定
  2. 隐式绑定
  3. new绑定
  4. 显式绑定

一.默认绑定

1. 函数直接调用,this指向window

function foo() {
    console.log("foo", this);
}

 foo() //window

转存失败,建议直接上传图片文件

2.对象中的函数 单独调用 this指向window

 const obj = {
    say() {
    console.log("say", this);
  }
}
 const a = obj.say;
 a() //window

转存失败,建议直接上传图片文件

3.高阶函数

const obj = {
   say() {
   console.log("say", this);
   }
}     
function bar(fn) {
   fn()
}

bar(obj.say)//window

转存失败,建议直接上传图片文件

4.在严格模式下,this指向undefined

二.隐式绑定

通过对象的引用this执行调用的对象

    function foo() {
      console.log("foo函数:", this)
    }

    var obj = {
      bar: foo
    }

    obj.bar() // foo函数: {bar: ƒ}

转存失败,建议直接上传图片文件

三.new构造函数绑定

  1. 创建新的空对象
  2. 将this指向这个空对象
  3. 执行函数体中的代码
  4. 没有显示返回非空对象时, 默认返回这个对象
    function foo() {
      this.name = "why"
      console.log("foo函数:", this)
    }

    new foo() // foo函数: foo {name: "why"}

转存失败,建议直接上传图片文件

四.显式绑定

  1. call
  2. bind
  3. apply

三者区别:1.传参区别 bind call 传参后续的参数以多参数的形式传递 apply传参通过数组 2.call apply绑定后立即执行函数 bind执行后会形成一个新的函数 需要再次执行函数

   // call 参数 参数一:绑定的对象 参数二: xxx,xxx 后续的参数以多参数的形式传递
    // apply 参数 参数一:绑定的对象 参数二:[xxx,xxx]

    // call/apply
    function foo(name, age, height) {
      console.log("foo函数被调用:", this)
      console.log("打印参数:", name, age, height)
    }

    // ()调用
    // foo("why", 18, 1.88)

    // apply
    // 第一个参数: 绑定this
    // 第二个参数: 传入额外的实参, 以数组的形式
    // foo.apply("apply", ["kobe", 30, 1.98])

    // call
    // 第一个参数: 绑定this
    // 参数列表: 后续的参数以多参数的形式传递, 会作为实参
    foo.call("call", "james", 25, 2.05)




    // bind 参数 参数一:绑定的对象 参数二:【xxx,xxx】
    // bind 会返回一个新的函数
    function foo(name, age, height, address) {
      console.log("foo:", this)
      console.log("参数:", name, age, height, address)
    }

    var obj = { name: "why" }

    // 需求: 调用foo时, 总是绑定到obj对象身上(不希望obj对象身上有函数)
    // 1.bind函数的基本使用
    // var bar = foo.bind(obj)
    // bar() // this -> obj

    // 2.bind函数的其他参数(了解)
    var bar = foo.bind(obj, "kobe", 18, 1.88)
    bar("james")

转存失败,建议直接上传图片文件

五.this的优先级

  1. 显式绑定绑定的优先级高于隐式绑定
  2. bind高于默认绑定
  3. new绑定优先级高于隐式绑定
  4. new不可以和apply/call一起使用
  5. new优先级高于bind
  6. bind优先级高于apply/call
    // function foo() {
    //   console.log("foo:", this)
    // }

    // 比较优先级:

    // 1.显式绑定绑定的优先级高于隐式绑定
    // 1.1.测试一:apply高于默认绑定
     var obj = { foo: foo }
     obj.foo.apply("abc")
     obj.foo.call("abc")

    // 1.2.测试二:bind高于默认绑定
     var bar = foo.bind("aaa")
     var obj = {
       name: "why",
       baz: bar
     }
     obj.baz()


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


    // 3.new/显式
    // 3.1. new不可以和apply/call一起使用

    // 3.2. new优先级高于bind
     function foo() {
       console.log("foo:", this)
     }
     var bindFn = foo.bind("aaa")
     new bindFn()


    // 4.bind/apply优先级
    // bind优先级高于apply/call
    function foo() {
      console.log("foo:", this)
    }
    var bindFn = foo.bind("aaa")
    bindFn.call("bbb")

转存失败,建议直接上传图片文件

六.this绑定之外的情况

        // 1.情况一: 显式绑定null/undefined, 那么使用的规则是默认绑定
        function foo() {
            console.log("foo:", this)
        }

        foo.apply("abc")
        foo.apply(null)
        foo.apply(undefined)


        // 2.情况二: 间接函数引用 
        var obj1 = {
            name: "obj1",
            foo: function () {
                console.log("foo:", this)
            }
        }
        var obj2 = {
            name: "obj2"
        };

        // {}[]()

        // obj2.foo = obj1.foo
        // obj2.foo()
        (obj2.foo = obj1.foo)() // obj2.foo = obj1.foo 返回的是一个函数,然后立即执行 执行的时候是默认绑定 所以this指向window

转存失败,建议直接上传图片文件

七.箭头函数中的this

箭头函数中没有this 显式绑定this也绑不上去 箭头函数的this指向只会向上一级作用域找

       // 1.普通函数中是有this的标识符
        function foo() {
            console.log("foo", this)
        }

        foo()
        foo.apply("aaa")

        // 2.箭头函数中, 压根没有this
        var bar = () => {
            console.log("bar:", this)
        }
        bar()
        // 通过apply调用时, 也是没有this
        bar.apply("aaaa")

        console.log("全局this:", this)关于JSthis指向问题 
        var message = "global message"

        // 3.this的查找规则
        var obj = {
            name: "obj",
            foo: () => {
                var bar = () => {
                    console.log("bar:", this)
                }
                return bar

            }
        }
        var fn = obj.foo()
        fn.apply("bbb")

转存失败,建议直接上传图片文件

八.关于this的面试题

面试题1

var name = 'window';

var person = {
    name: 'person',
    sayName: function () {
        console.log(this.name);
    },
};

function sayName() {
    var sss = person.sayName;

    sss(); // 绑定: 默认绑定, window -> window

    person.sayName(); // 绑定: 隐式绑定, person -> person

    person.sayName(); // 绑定: 隐式绑定, person -> person

    (b = person.sayName)(); // 术语: 间接函数引用, window -> window
}

sayName();

转存失败,建议直接上传图片文件

面试题2

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 () {
    // console.log(this) // 第一个表达式this -> person1
    // console.log(this) // 第二个表达式this -> person2
    // console.log(this) // 第三个表达式this -> person1
    
    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
person1.foo4.call(person2)(); // person2
person1.foo4().call(person2); // person1

转存失败,建议直接上传图片文件

面试题3

var name = 'window'

/*
  1.创建一个空的对象
  2.将这个空的对象赋值给this
  3.执行函数体中代码
  4.将这个新的对象默认返回
*/
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)
    }
  }
}

// person1/person都是对象(实例instance)
var person1 = new Person('person1')
var person2 = new Person('person2')


// 面试题目:
person1.foo1() // 隐式绑定: person1
person1.foo1.call(person2) // 显式绑定: person2

person1.foo2() // 上层作用域查找: person1
person1.foo2.call(person2) // 上层作用域查找: 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(隐式绑定)

转存失败,建议直接上传图片文件

面试题4

var name = 'window'

/*
  1.创建一个空的对象
  2.将这个空的对象赋值给this
  3.执行函数体中代码
  4.将这个新的对象默认返回
*/
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(隐式绑定)