ECMAScript中作用域一般是基于词法作用域是静态的,但是也有一种动态的作用域就是this: this它不指向函数本事,也不指向函数的作用域
this是在运行的时候绑定的并不是在定义的时候绑定的,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方法既是函数的调用位置。
如果要判断一个运行中函数的this的绑定,就需要找到这个函数的直接调用位置。找到后可以运用顺序运用下面的四条规则来判断this的绑定对象
1:有new调用?绑定到新创建的阿对象。
2:由call或者apply或者bind 调用?绑定到指定对象。
3:由上下文对象调用?绑定到那个上下文对象。
4:默认: 在严格模式下绑定到undefined,否则绑定到全局对象。
箭头函数不会使用以上四条标准的绑定规则,而是根据当前的词法作用域来决定this,具体说,箭头函数会继承外层函数调用的this绑定
调用位置
通常来说,寻找调用位置就是寻找“函数被调用的位置”:最重要的是分析调用栈(就是为了到达当前执行位置所调用的所有函数),调用位置就在当前正在执行函数的前一个调用中。
<script type="text/javascript"> var a=0; function baz() { //当前的调用栈是 baz //因此当前调用位置是全局作用域 var a=1; console.log("baz"+this.a); bar(); //bar 的调用位置 } function bar() { //当前的调用栈是 baz--bar //因此当前调用位置在bar中 var a=2; console.log("bar"+this.a); foo(); //foo的调用位置 } function foo() { var a=3; console.log("foo"+this.a); } baz(); // baz的调用位置 </script>
利用工具查找
查找方法:可以在函数的第一行添加断点,运行代码的时候,调试器在那个位置停留的时候,会展示当前位置函数的调用列表,这就是调用栈。因此,当分析this绑定使用开发者工具调试得到调用栈,然后找到栈中的第二个元素,这就是真正的调用位置
绑定规则
找到了调用位置,就可以已经上面的四条规则来判断this的绑定对象。
-
默认绑定
独立函数调用,可以把这条规则看作是无法应用其它规则时的默认规则 : 单独调用函数并且函数没有运行在严格模式下,this绑定到全局对象。
看下面的代码
<script type="text/javascript"> function foo() { var a=5; console.log(this.a); } var a=2; foo(); //输出 2; </script>
明显this指向了全局对象。 因为函数foo()是直接使用不带任何修饰的函数引用调用的
如果调用的函数运行在严格模式下,this绑定到 undefinded
<script type="text/javascript"> function foo() { "use strict" var a=5; console.log(this.a); } var a=2; foo(); //访问不到 </script>
结果如下
但是在严格模式下调用是可以正常访问的
<script type="text/javascript">
function foo()
{
var a=5;
console.log(this.a);
}
"use strict" //严格模式下调用
var a=2;
foo(); // 输出2
</script>
-
隐式绑定
函数作为对象的属性,并通过这个属性调用函数
函数作为对象的属性被调用时,this指向该对象,对象属性引用链中只有一层或者说最后一层在调用位置中起作用。
但是当对象的函数属性被赋值个别的变量,再调用时,this对象就指向全局对象。
<script type="text/javascript"> function foo() { var a=5; console.log(this.a); } var obj= { foo:foo, a:2 } obj.foo(); //输出2 this指向obj var obj2= { a:7, obj:obj; } obj2.obj.foo(); //输出2 this 指向obj 而不是obj2 </script>
隐式丢失
当对象的函数属性被当作参数传递后调用,效果等同于默认绑定
<script type="text/javascript"> function foo() { var a=5; console.log(this.a); } var obj={ foo:foo, a:2 } var a="global"; var bar=obj.foo; // bar其实引用的就是 foo()函数 和obj 对象脱离了关系 bar(); //this 指向全局对象 输出global; </script>
bar其实引用的就是 foo()函数 和obj 对象脱离了关系,相当于不带任何修饰符的函数调用,所有运用了默认规则指向全局对象。
-
显示绑定
cally(),apply(),bind 都是改变函数内部this的指向
var obj = {
x: 81,
};
var foo = {
getX: function() {
return this.x;
}
}
console.log(foo.getX.bind(obj)()); //81
console.log(foo.getX.call(obj)); //81
console.log(foo.getX.apply(obj)); //81
三个输出的都是81,但是注意看使用 bind()
方法的,他后面多了对括号。
也就是说,区别是,当你希望改变上下文环境之后并非立即执行,而是回调执行的时候,使用 bind() 方法。而 apply/call 则会立即执行函数。
再总结一下:
apply
、 call
、bind
三者都是用来改变函数的this对象的指向的;apply
、 call
、bind
三者第一个参数都是this要指向的对象,也就是想指定的上下文;apply
、 call
、bind
三者都可以利用后续参数传参;bind
是返回对应函数,便于稍后调用;apply
、call
则是立即调用
当把null,undefind作为this绑定的对象传入 call,apply,bind的时候,会被忽略,实际应用是默认绑定规则
<script type="text/javascript"> function foo() { var a=3; console.log(this.a); } var a=100; foo.call(null); //输出100 </script>
-
new调用 :指向创建的对象
说明:javaScript 中没有构造函数,只有构造调用
使用new来调用函数,或者发生构造函数调用时,会自动指向下面的操作
1:创建一个全新的对象。
2:这个对象会被执行【prototype】连接
3:这个新对象会绑定到函数调用的this
4: 如果函数没有返回其它对象,那么new表达式中的函数会自动返回这个新对象。
即是:如果函数有返回对象,就返回对象,如果函数返回不是对象,或者没有定义返回值,就将新对象返回。
使用new调用函数实际执行的步骤
当返回值是对象的时候就返回函数返回值
可以看到使用new 创建的对象 this :指向创建的对象, 不适用this创建的对象 this指向全局对象。