引子
var name = "Heternally";
var obj = {
name: "zl",
foo: function() {
console.log(this.name);
}
};
var foo = obj.foo;
obj.foo(); // "zl"
foo(); // "Heternally"
为什么结果会不一样,因为obj.foo()运行在obj的环境之下,所以this指针指向的是obj内部的作用域。而foo()运行在全局环境下,在非严格模式下this指向window,所以打印的是全局的name。
为什么设计this指针
当我们使用对象数据类型时,对象1的属性可能是一个函数。当对象1属性为函数时,会在堆中再建立一个对象2,然后将对象2的地址赋值给对象1的属性。但是JavaScript中函数是可以在不同环境内运行的,此时可以引用当前环境的其他变量。所以需要一个机制,能过获取函数当前运行环境。所以this是为了指向函数运行时所在的运行环境的。
this的绑定规则
总共是有下面5种:
- 默认绑定(严格/非严格模式)
- 隐式绑定
- 显式绑定
- new绑定
- ES6箭头函数绑定
1.1默认绑定(严格模式)
默认绑定不将全局对象window作为绑定,而会绑定到undefined
1.2默认绑定(非严格模式)
此时默认绑定全局对象
2.隐式绑定
当函数作为对象属性存在,通过对象属性执行函数,隐式绑定会将this绑定到对象上。倘若通过赋值操作后执行,则会应用默认绑定(严格undefined,非严格window)。
函数传参也是隐式绑定,此时环境变为该函数所在环境。
3.显式绑定
通过call、apply、bind三个函数进行显式绑定
call在执行绑定时做了什么:
将该函数设定为对象属性
指定该函数的this为对象,并进行传参
执行或删除函数
若call内传参为空,则根据默认绑定来判定this环境
foo.call(obj);//将foo函数this绑定obj对象
4.new绑定
new绑定做了什么
它创建(构造)了一个全新的对象
它会被执行[[Prototype]](也就是__proto__)链接
它使this指向新创建的对象
通过new创建的每个对象将最终被[[Prototype]]链接到这个函数的prototype对象上
如果函数没有返回对象类型Object(包含Functoin, Array, Date, RegExg, Error),那么new表达式中的函数调用将返回该对象引用
5.ES6箭头函数绑定
箭头函数调用时无法使用上面四种规则了,它和普通函数最不同的一点就是对于箭头函数的this指向,是根据它外层(函数/全局)作用域来决定。
规则优先级
1、new绑定
var obj = new Foo();
this绑定新的对象上
2、显示绑定
var obj = foo.call(bar);
this绑定到指定对象上,若指定对象为null/undefined或着没传,则使用默认绑定规则
3、隐式绑定
var obj = bar.foo();
this绑定到调用方法的对象上
4、默认绑定
foo();
this在严格模式下绑定到undefined
在非严格模式下绑定到全局对象