this

238 阅读4分钟

执行上下文的分类

  • 全局执行上下文
  • 函数执行上下文
  • 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();