this的绑定规则有:

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

用一句话总结this的指向规则:
this的指向,是在执行函数时根据执行上下文所动态决定的
(调用函数会创建新的属于函数自身的上下文,执行上下文的调用创建阶段会决定this的指向)

一般来说,this 绑定的优先级: new > 显示绑定 > 隐式绑定 > 默认绑定。

1. 默认绑定

var a = 'global'
function fn1() {
     console.log(this)
 }
 fn1();   //global

函数在全局环境调用执行时,this指向全局对象,如果使用严格模式,this绑定到undefined

2. 隐式绑定

var a = 'globalA';
var obj = {
  a: 'objA',
  test
}
function test () {
   console.log(this.a)
}
obj.test();  // objA

函数作为对象的方法被调用时,this指向方法运行时所在的当前对象,即obj。

var obj2 = {
    a: 42,
    foo: foo
};
var obj1 = {
    a: 2,
    obj2: obj2
};
function test() {
    console.log(this.a);
}
obj1.obj2.foo(); // 42

如果是多重对象的调用,this 也还是绑定在上一级对象上,即obj2上。
对象属性引用链中只有最后一层会影响调用位置。

2.1.1 隐式丢失
var a = 'global'
const zhangsan = {
      name: "张三",
      sayHi () {
          console.log(this);
      },
      wait() {
         function foo1() {
			console.log(this.a);
		}
		foo1();
      }
};
zhangsan.wait();    //global

这里的函数调用比上面的程序多了一层函数的嵌套,绑定的 this 就发生了变化,变成了 winodw 全局对象
解决办法:

wait() {
 	    var that = this
         function foo1() {
			console.log(that.a);
		}
		foo1();
      }

或者使用箭头函数

wait() {
         const foo1 = () => {
			console.log(this.a);
		}
		foo1();
      }

箭头函数内的this取上级作用域(waitAgain方法)的this,即waitAgain方法运行时所在的当前对象张三。
箭头函数中的this是在定义函数的时候绑定,而不是在执行函数的时候绑定。this指向箭头函数上级作用域指向的this,this是继承自父执行上下文!!!
箭头函数面试题:

var name = 'window';
const person1 = {
  name: 'person1',
  sayName: () => {
    console.log(this.name);
  }
}
person1.sayName();//window
var b = '0'
let obj = {
  b: 1,
  a () {
    var a = '2'
    return () => {
      return () => {
        console.log(this.b) // 1
      }
    }
  }
}
obj.a()()()
// this指向的是obj
// {a: ƒ}
var name = 'a'
let obj = {
  a () {
    console.log(this)
  },
  name: 'jack',
  showName: this.name // 箭头函数
}
obj.a()  // obj
console.log(obj.showName)  // a

let a = obj.a
a() // window

3. 显示绑定

使用call,apply,bind改变函数的调用对象,第一个参数就是改变后的调用这个函数的对象

function fn1() {
      console.log(this)
}
fn1.call({x:100});  //{x: 100}

使用call,apply,bind方法调用时,第一个参数就是this指向的对象{x:100}
call,apply,bind的区别
call、apply、bind的作用是改变函数运行时this的指向,所以它们的第一个参数是要绑定给this的值,区别在于:

fn.call({x:100},p1,p2,p3)  //call后面的参数是一个参数列表,一个一个传进去的
fn.apply({x:100},argument) //apply里面的第二个参数是一个参数数组
fn.bind({x:100},p1,p2,p3)  //bind和call相似,从第二个参数开始是接收的参数列表。区别在于bind方法返回值是函数,没有立即执行。

4. new绑定

var x = "global!";
function obj(){
  this.x = "obj!";
}
var o = new obj();
console.log(o.x);   //obj!

函数通过new调用后,会返回一个新对象,并将新对象会绑定到函数调用的 this上
使用 new 调用函数,会自动执行下面的操作:
1.创建一个新对象。
2. 将新对象绑定到函数调用的 this上。这个新对象会被执行 [[prototype]] 连接。对象.proto = 构造函数.prototype
3. 执行构造函数
4. 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。

面试题
构造函数中返回一个对象对结果有什么影响???

function Super (a) {  
   this.a = a;  
   return 123;  
}  
Super.prototype.sayHello = function() {  
   alert('hello world');  
} 

function Super_ (a) {  
   this.a = a;  
   return {a: 2};  
}  
Super_.prototype.sayHello = function() {  
   alert('hello world');  
}  

const super1 = new Super(3)
const super2 = new Super_(3)
console.log(super1)    // Super { a: 3 }
console.log(super2) // { a: 2 }

在构造函数中 return 基本类型 不会影响构造函数的值, 而 return 对象类型 则会替代构造函数返回该对象。