执行上下文的分类
- 全局执行上下文
- 函数执行上下文
- eval 执行上下文
执行上下文的组成
变量环境:
存放由 var 、function 声明的变量和函数
- 提升的变量
- 提升后的函数定义
词法环境:
存放 let const 声明的块级作用域中的变量
外部环境:
作用域链中上层环境,由词法作用域决定
this 对象:
和当前执行上下文绑定,每个执行上下文都有一个 this
- 全局执行上下文 this -- 指向 window 对象
/* 全局执行上下文中的 this -- Window 对象 */
console.log(this);
/* 默认情况下的函数执行上下文中的 this -- Window 对象*/
function foo(){
console.log(this);
}
foo();- 函数执行上下文 this,函数执行上下文中的 this 值取决于函数的调用方式
- 在全局环境中调用一个函数,函数内部的 this 指向的是全局变量 window
- 通过一个对象来调用内部方法,该方法的执行上下文中的 this 指向对象本身
- eval 执行上下文 this
如何设置函数执行上下文中的 this 值?
使用函数的 call、apply、bind 方法
共同点:
apply、call、bind 三者都是用来改变函数this对象的指向。
apply、call、bind 三者第一个参数都是this要指向的对象,也就是指定的上下文。
apply、call、bind 三者都可以利用后续参数传参。
不同点:
bind是返回对应的函数,便于稍后调用。
apply、call 则是立即调用。
/* 1、使用函数的 call、apply、bind 方法 */
var bar = {
name:'我是 bar 中的name'
}
function foo2(){
console.log(this.name);
}
foo2.call(bar);/* 立即调用 */
foo2.apply(bar);/* 也是立即调用 */
foo2.bind(bar)();/* 返回值是一个改变了 this 值的函数,需要手动调用 */使用对象调用方法
/* 2、使用对象调用方法 */
let bar2 = {
name:'我是 bar2 中的name',
getNameFn:function(){
console.log(this.name);
}
}
bar2.getNameFn();//我是 bar2 中的name
bar2.getNameFn.apply(this);//我是 foo2 中的name
var foo3 = bar2.getNameFn;/* 把对象方法赋值给全局变量 */
foo3();//我是 foo2 中的name -- 在全局调用了,等价于 bar2.getNameFn.apply(this);使用 new 关键字实例化一个构造函数
/* 3、使用 new 一个构造函数来设置 */
function CreateObj(){
console.log(this);/* 这里的 this 就是实例化后的对象 */
this.name = "我是 CreateObj 中的 name";
}
var myObj = new CreateObj();
console.log(myObj);
/*
这里 new 关键字做了如下几件事:
1、创建一个临时的空对象 tempObj
2、调用 CreateObj.call(tempObj)
3、执行 CreateObj 函数,此时 CreateObj 函数执行上下文中的 this 就是前面绑定的 tempObj
4、返回 tempObj 对象
*/this 的缺陷和应对方案
嵌套函数中的 this 不会从外层函数中继承
/* 1、嵌套函数中,内部函数不会从外层函数中继承 this */
var bar3 = {
name : "我是 bar3 中的 name",
getName: function(){
console.log(this.name);//当前对象的 name
function bar(){
console.log(this.name);//我是 foo2 中的 name -- 这是前面定义的全局环境中的 name
}
bar();
}
}
bar3.getName();解决方案:
1、在外层函数中使用变量 that 接受外层函数的 this 值,把 this 的体系转换成作用域体系
/* 1、在外层函数中使用变量 that 接受外层函数的 this 值,把 this 的体系转换成作用域体系 */
var bar4 = {
name : "我是 bar4 中的 name",
getName: function(){
console.log(this.name);//我是 bar4 中的 name -- 当前对象的 name
var that = this;
function bar(){
console.log(that.name);//我是 bar4 中的 name -- 当前对象的 name
}
bar();
}
}
bar4.getName();2、使用箭头函数(箭头函数不会创建自身的执行上下文,内部 this 为外层 this)
/* 2、使用箭头函数 -- 箭头函数不会创建自身的执行上下文,所以 this 取决于外层执行上下文中的 this*/
var bar5 = {
name : "我是 bar5 中的 name",
getName: function(){
console.log(this.name);//我是 bar5 中的 name -- 当前对象的 name
var bar = ()=>{
console.log(this.name);//我是 bar5 中的 name -- 当前对象的 name
}
bar();
}
}
bar5.getName();3、使用 apply、bind、call显示绑定 this 到内层函数中
/* 3、apply、bind、call显示绑定 this 到内层函数中 */
var bar6 = {
name : "我是 bar6 中的 name",
getName: function(){
console.log(this.name);//我是 bar6 中的 name -- 当前对象的 name
function bar(){
console.log(this.name);//我是 bar6 中的 name -- 当前对象的 name
}
bar.call(this);
}
}
bar6.getName();普通函数中的 this 默认指向全局对象 window,有可能在函数内部使用 this 不小心误操作了全局中的数据
/* 默认情况下的函数执行上下文中的 this -- Window 对象*/
function foo(){
console.log(this);
}
foo();解决方案:
1、使用 apply、bind、call 显示调用函数
2、使用严格模式 ‘use strict’ 把函数中的 this 设置成 undefined
/* this 的缺陷二:普通函数中的 this 默认指向全局对象 window,有可能在函数内部使用 this 不小心误操作了全局中的数据*/
function foo(){
console.log(this);
}
foo();
/* 解决方案 */
/* 1、显示绑定某个对象到 this 上 */
foo.call({});
/* 2、使用严格模式 ‘use strict’ 把函数中的 this 设置成 undefined */
function foo4(){
'use strict';
console.log(this);//undefined
}
foo4();