上下文环境
在了解this应用场景之前需要了解什么是上下文环境.可以阅读该文章this和执行上下文环境的理解
关于this的应用场景可以看这里,因为这个知识点的篇幅非常的长,参考至this的应用场景总结
如图1所示:this探讨,关于this,首先需要知道函数中的this只有在调用后才能知道它的值是怎么样的,而不是常识般认为this指向对象本身,或者指向对象的作用域.因为函数调用时会生成一个新的执行上下文环境. 里面会涉及到LSH可以参考此文章LSH查询
<script>
var n = 'hello world !';
function example() {
this.n = 0;
}
let vm = example()
console.log(n);
// -> 结果为 0
</script>
这是必考对面试知识点,在看到这类题,需要做对事情就是预解析放在第一步,然后再去执行操作.
this的决定因素
实际中,this是 上下文执行环境中的一个属性,this的指向也不是如常识一般指向谁,而是依据调用栈和执行位置决定的(或者说执行环境),并且 this 是在运行时绑定的,并不是在编写时绑定.所以this指向什么完全取决于函数在哪里被调用. 为了更好理解看图2所示:
<script>
// 书中的一段话.
function foo() {
var a = 2;
this.bar();
}
function bar() {console.log(this.a);}
foo(); // undefined
/*
预解析
function foo(){
this.bar;
var a;
a = 2;
this.bar();
}
...
foo()
1. 对foo函数进行声明提升,执行foo
2. 对a和bar进行变量提升,bar在前a在后,因为bar是函数,调入栈中,在队列中第一执行
3. 搜索引擎通过LSH查询,找到a,a=2进行赋值操作.
4. this指向谁,根据调用栈: 全局 -> foo();调用位置是: 全局, 判断,此时this指向全局,调用bar()
5. 执行bar(),依据调用栈: 全局 -> bar(), 调用位置:全局,判断,此时this指向全局.
执行this.a 引擎通过LSH查询
在宽松模式下,全局环境没有找到a变量,所以会创建一个a,而a没有值. 此时undefined
(严格模式下,全局执行环境不会创建a变量,所以会抛出错误)
6. 执行console.log(),因为this执行环境中a没有赋值,所以undefined.
7. 那么思考,a在哪里呢,a在foo()的作用域中,而此时bar调用a明显跨域,依据闭包原理,bar是访问不到foo内部的a,
如果要访问a,则需要显示传入参数,调用a的作用域.
*/
</script>
因为在该this和执行上下文环境的理解文章中有说到判断this. 所以这边就添加过来.
注意:书写函数时,应尽量书写具名函数,以方便需要用到自身的时候进行回调,如果是匿名函数,则会因为没有指向函数对象的词法标识符,而无法 找到自身,从而无法调用
this指向是可以通过固定this来修复的,这里称为显示绑定/硬绑定, 例如call,apply,bind,new, 然后说在他们的优先级以及显示绑定后的this指向.
判断this
根据优先级来判断函数在某个调用位置应用的是哪条规则,按照下面顺序进行,如图3所示:判断this
1. 函数是否在new中调用(new绑定)? 如果是的话,this绑定的是新创建的对象
var bar = new foo()
2. 函数是否通过call,apply(显式绑定)或硬绑定调用? 如果是,this绑定是指定的对象.
var bar = foo.call(obj2)
3. 函数是否在某个上下文对象中调用(隐式绑定)? 如果是,this绑定的是那个上下文对象.
var bar = obj1.foo()
4. 如果不是的话,使用默认绑定,如果在严格模式下,就绑定到undefined,否则全局对象.
var bar = foo()
就是这样. 对于正常的函数调用来说,理解了这些就明白this的绑定原理.
但也有例外, 这个例外以后再补充,也是在该文章.
this的应用场景
普通函数或全局函数调用
如图4所示: 普通函数或全局函数调用后的this
<script>
var n = 'hello world'
function example(){
this.n = 0;
}
example() // 因为调用后this指向全局,所以n重新将n赋值为0
console.log(n);
// -> 0
</script>
严格模式下,未指定环境对象而调用函数,this的值是undefined。
构造函数调用
如果直接调用构造函数,那么效果和图4一样,但是使用new关键字,此时this会指向该新对象. 如图5所示:构造函数new调用this变化
<script>
function Foo(){
this.x = 10;
console.log(this);
// -> Foo {x: 10}
}
var foo = new Foo()
console.log(foo.x);
// -> 10
</script>
作为对象的方法调用
如图5所示, this作为对象方法调用可以看到,this指向该对象.
<script>
var obj = {
x : 10,
foo:function(){
console.log(this);
// -> {x: 10, foo: ƒ}
console.log(this.x);
// -> 10
}
}
obj.foo()
</script>
如果this不作为对象的方法调用,结果会是window,如图6所示.
<script>
var obj = {
x : 10,
foo:function(){
console.log(this);
// -> {x: 10, foo: ƒ}
console.log(this.x);
// -> 10
}
}
let fn = obj.foo
fn()
// -> Window
</script>