浅谈this关键字

209 阅读4分钟

this理解

  • this是指包含它的函数作为方法被调用时所属的对象

一、this的指向大致可以分为以下几种

注:this指向什么跟函数所处位置没有关系,跟函数被调用的方式有关系

  1. 默认绑定:作为普通函数独立调用(在这种模式下,this代表全局对象Global。)
function foo(){
    console.log(this);
}

foo()  //window
var obj = {
    foo: function(){
        console.log(this);
    }
}
var bar = obj.foo
bar()  //window
  1. 隐式绑定:作为对象的属性来调用(这时this就指这个上级对象)
function foo(){
    console.log(this);
}
var obj = {
    foo : foo
}
obj.foo();  //obj对象
var obj = {
    foo: function(){
        console.log(this);
    }
}
var obj2 = {
    name: '这是obj2',
    bar: obj.foo
}

obj2.bar()  //obj2对象
  1. 作为构造函数来调用(这时this指向Persion实例对象)
function Persion(){
    this.name = "Jackie"
    this.age = 21
    this.show = function(){
        console.log(this);  //Persion实例对象
    }
}
var p = new Persion();
p.show();  //在show方法中方法this,指向了p对象实例。
  1. 显示绑定:通过call与apply改变this的指向

注:call和apply传入参数方式不同

function sum(num1, num2, num3){
    console.log(num1 + num2 + num3, this);
}
var obj = {
    name: '这是obj'
}
sum.call(obj,20,50,70)  //this指向obj对象
sum.apply(obj,[20,50,70])  //this指向obj对象
  • 显示绑定:bind

默认绑定和显示绑定bind冲突:优先级最高(显示绑定)

function aaa(){
    console.log(this);
}
var obj = {
    name: '这是obj'
}
var newa = aaa.bind(obj)
newa()  //此时显示绑定和默认绑定冲突,当前this指向obj对象,而不是默认绑定的window对象

二、this绑定优先级

  1. 默认绑定的优先级最低 foo()
  2. 显示绑定优先级高于隐式绑定 foo.call(obj) > obj.foo()
//call/apply优先级高于隐式绑定
var obj1 = {
    name: 'obj1',
    foo: function(){
        console.log(this);
    }
}

var obj2 = {
    name: 'obj2'
}

obj1.foo.call(obj2);  //this指向obj2对象
//bind优先级高于隐式绑定
function foo(){
    console.log(this);
}

var obj = {
    name: 'obj',
    foo: foo.bind("abc")
}

obj.foo();  //this指向字符串abc
  1. new绑定优先级高于隐式绑定
var obj = {
    name: 'obj',
    foo: function(){
        console.log(this);
    }
}

var f = new obj.foo()  //this指向foo函数
  1. new绑定优先级高于bind显示绑定
var obj = {
    name: 'obj对象',
    foo: function foo(){
        console.log(this);
    }
}
var a = obj.foo.bind("aaa")
var bar = new a()  //this指向foo函数

结论:new绑定 > 显示绑定(call/apply/bind) > 隐式绑定(obj.foo()) > 默认绑定(独立函数调用)

附加:当apply/call/bind传入undefined/null时,自动将this绑定成全局对象

var obj = {
    name: 'obj对象',
    foo: function foo(){
        console.log(this);
    }
}
obj.foo.call(null)  //window
obj.foo.apply(undefined)  //window
var bar = obj.foo.bind(null)
bar()  //window

三、ES6箭头函数(箭头函数不在前四个绑定规则生效)

  • 当在setTimeout中传入的参数为函数时,函数内部的this才会指向window对象。
  • 箭头函数本身不绑定this,当箭头函数中使用this时,会去找该函数的上层作用域的this
var obj = {
    data: [],
    getData: function(){
        console.log(this);  //obj对象
        setTimeout(function(){
            console.log(this);  //window
        })
        setTimeout(console.log(this))   //obj对象
        setTimeout(()=>console.log(this))   //obj对象
    }
}

obj.getData()   //obj对象、obj对象、window、obj对象

四、关于this面试题

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();
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(箭头函数不绑定this,则找该函数的上层作用域全局)
person1.foo2.call(person2); //window(依然是找上层作用域)

person1.foo3()();  //window(默认绑定->独立函数调用)
person1.foo3.call(person2)();  //window(默认绑定->独立函数调用)先执行person1.foo3.call(person2),再进行函数独立调用
person1.foo3().call(person2);  //person2(显示绑定)

person1.foo4()();  //person1,箭头函数本身没有this,此时this指向上层作用域的this,又因为上层是隐式调用,所以此时this指向person1
person1.foo4.call(person2)();  //person2(将person1.foo4的作用域this绑定到person2作用域)
person1.foo4().call(person2);  //person1(先执行person1.foo4(),箭头函数不绑定this所以即便call(person2),还是会去找上层作用域this)
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(隐式绑定)
person1.foo2.call(person2)  //person1(箭头函数本身没有this,即便call(person2)还是去找上层作用域this)

person1.foo3()()  //window(独立函数调用)
person1.foo3.call(person2)()  //window(独立函数调用,相当于先执行person1.foo3.call(person2),后再独立调用这个函数)
person1.foo3().call(person2)  //person2(显示绑定)

person1.foo4()()  //person1(默认绑定->独立函数调用)
person1.foo4.call(person2)()  //person2(隐式绑定)
person1.foo4().call(person2)  //person1(箭头函数this指向上层作用域)
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(箭头函数不绑定this,该箭头函数上层作用域this.name为obj)
person1.obj.foo2.call(person2)()  //person2(隐式绑定)
person1.obj.foo2().call(person2)  //obj(箭头函数不绑定this,该箭头函数上层作用域this.name为obj)