JavaScript 执行上下文的视角详解this使用

目录
  • 前言
    • 全局执行上下文中的 this
    • 函数执行上下文中的 this
  • this 的设计缺陷以及应对方案
    • 1. 嵌套函数中的 this 不会从外层函数中继承
    • 2. 普通函数中的 this 默认指向全局对象 window

前言

在对象内部的方法中使用对象内部的属性是一个非常普遍的需求。但是 JavaScript 的作用域机制并不支持这一点,基于这个需求,JavaScript 有另外一套 this 机制。

this 是和执行上下文绑定的,也就是说每个执行上下文中都有一个 this。执行上下文主要分为三种——全局执行上下文、函数执行上下文和 eval 执行上下文,所以对应的 this 也只有这三种——全局执行上下文中的 this、函数中的 this 和 eval 中的 this。

全局执行上下文中的 this

全局执行上下文中的 this 是指向 window 对象的。这也是 this 和作用域链的 唯一交点,作用域链的最底端包含了 window 对象,全局执行上下文中的 this 也是指向 window 对象。

函数执行上下文中的 this

function foo() {
  console.log(this);
}
foo();

执行这段代码,打印出来的也是 window 对象,这说明在默认情况下调用一个函数,其执行上下文中的 this 也是指向 window 对象的。

通常情况下,有下面三种方式来设置函数执行上下文中的 this 值:

1. 通过函数的 call 方法设置

let bar = {
  myName: "极客邦",
  test1: 1,
};
function foo() {
  this.myName = "极客时间";
}
foo.call(bar);
console.log(bar);
console.log(myName);

执行这段代码,你就能发现 foo 函数内部的 this 已经指向了 bar 对象,因为通过打印 bar 对象,可以看出 barmyName 属性已经由“极客邦”变为“极客时间”了,同时在全局执行上下文中打印 myName,JavaScript 引擎提示该变量未定义。其实除了 call 方法,你还可以使用 bindapply 方法来设置函数执行上下文中的 this

2. 通过对象调用方法设置

var myObj = {
  name: "极客时间",
  showThis: function () {
    console.log(this);
  },
};
myObj.showThis();

执行这段代码,你可以看到,最终输出的 this 值是指向 myObj 的。所以,你可以得出这样的结论:使用对象来调用其内部的一个方法,该方法的 this 是指向对象本身的。

接下来我们稍微改变下调用方式,把 showThis 赋给一个全局对象,然后再调用该对象,代码如下所示:

var myObj = {
  name: "极客时间",
  showThis: function () {
    this.name = "极客邦";
    console.log(this);
  },
};
var foo = myObj.showThis;
foo();

执行这段代码,你会发现 this 又指向了全局 window 对象。

结论:

在全局环境中调用一个函数,函数内部的 this 指向的是全局变量 window

通过一个对象来调用其内部的一个方法,该方法的执行上下文中的 this 指向对象本身。

3. 通过构造函数中设置

function CreateObj() {
  this.name = "极客时间";
}
var myObj = new CreateObj();

当执行 new CreateObj() 的时候,JavaScript 引擎做了如下四件事:

首先创建了一个空对象 tempObj

接着调用 CreateObj.call 方法,并将 tempObj 作为 call 方法的参数,这样当 CreateObj 的执行上下文创建时,它的 this 就指向了 tempObj 对象

然后执行 CreateObj 函数,此时的 CreateObj 函数执行上下文中的 this 指向了 tempObj 对象

最后返回 tempObj 对象

var tempObj = {};
CreateObj.call(tempObj);
return tempObj;

这样,就通过 new 关键字构建好了一个新对象,并且构造函数中的 this 其实就是新对象本身。

this 的设计缺陷以及应对方案

1. 嵌套函数中的 this 不会从外层函数中继承

var myObj = {
  name: "极客时间",
  showThis: function () {
    console.log(this);
    function bar() {
      console.log(this);
    }
    bar();
  },
};
myObj.showThis();

执行这段代码后,会发现函数 bar 中的 this 指向的是全局 window 对象,而函数 showThis 中的 this 指向的是 myObj 对象。

可以通过一个小技巧来解决这个问题,比如在 showThis 函数中声明一个变量 that 用来保存 this,然后在 bar 函数中使用 that。其实,这个方法的的本质是把 this 体系转换为了作用域的体系。

其实,你也可以使用 ES6 中的箭头函数来解决这个问题:

var myObj = {
  name: "极客时间",
  showThis: function () {
    console.log(this);
    var bar = () => {
      console.log(this);
    };
    bar();
  },
};
myObj.showThis();

这是因为 ES6 中的箭头函数并不会创建其自身的执行上下文,所以箭头函数中的 this 取决于它的外部函数。

2. 普通函数中的 this 默认指向全局对象 window

在实际工作中,我们并不希望函数执行上下文中的 this 默认指向全局对象,因为这样会打破数据的边界,造成一些误操作。如果要让函数执行上下文中的 this 指向某个对象,最好的方式是通过 call 方法来显示调用。

可以通过设置 JavaScript 的 严格模式 来解决(在第一行加上 "use strict";)。在严格模式下,默认执行一个函数,其函数的执行上下文中的 this 值是 undefined

以上就是JavaScript 执行上下文的视角详解this使用的详细内容,更多关于JavaScript 执行上下文 this的资料请关注我们其它相关文章!

(0)

相关推荐

  • js 执行上下文和作用域的相关总结

    前言 如果你是或者你想成为一名合格的前端开发工作者,你必须知道JavaScript代码在执行过程,知道执行上下文.作用域.变量提升等相关概念,并且熟练应用到自己的代码中.本文参考了你不知道的JavaScript,和JavaScript高级程序设计,以及部分博客. 正文     1.JavaScript代码的执行过程相关概念 js代码的执行分为编译器的编译和js引擎与作用域执行两个阶段,其中编译器编译的阶段(预编译阶段)分为分词/词法分析.解析/语法分析.代码生成三个阶段.   (1)在分词/词法

  • JS 中在严格模式下 this 的指向问题

    目录 前言 一.全局作用域中的this 二.全局作用域中函数中的this 三.对象的函数(方法)中的this 四.构造函数的this 五.事件处理函数中的this 六.内联事件处理函数中的this 七.定时器中的this,指向的是window 参考资料 前言 非严格模式下的 this 指向可能我们会经常遇到,但是严格模式下的 this 指向不是经常碰到,关于严格模式下的 this 指向是怎么样的,都是指向哪些,这篇博文将会很仔细地说清楚. 一.全局作用域中的this 在严格模式下,在全局作用域中

  • Vue.js中this如何取到data和method里的属性详解

    目录 准备工作 调试源码 initMethods initData 结束语 本篇文章介绍的是Vue.js如何取到data和methods里的属性? 准备工作 克隆源码到本地 git clone https://github.com/vuejs/vue.git 下载完毕后,用vscode打开,目光移动到package.json的scripts属性,我们看到有dev和build,dev会启动一个开发环境的服务,也就是说,我们在源码里做的改动,都会及时生效.build就是打包.和我们平时开发Vue.j

  • 通过实例了解JS执行上下文运行原理

    壹 ❀ 引 我们都知道,JS代码的执行顺序总是与代码先后顺序有所差异,当先抛开异步问题你会发现就算是同步代码,它的执行也与你的预期不一致,比如: function f1() { console.log('听风是风'); }; f1(); //echo function f1() { console.log('echo'); }; f1(); //echo 按照代码书写顺序,应该先输出 听风是风,再输出 echo才对,很遗憾,两次输出均为 echo:如果我们将上述代码中的函数声明改为函数表达式,结

  • 详解JavaScript中的执行上下文及调用堆栈

    一.执行上下文是什么 代码运行是在一定的环境之中运行的,这个运行环境我们就成为执行环境,也就是执行上下文,按照执行环境不同,我们可以分为三类: 全局执行环境:代码首次执行时候的默认环境 函数执行环境:每当执行流程进入到一个函数体内部的时候 Eval执行环境:当eval函数内部的文本执行的时候 二.执行上下文栈是什么 既然是'栈',那就得符合'栈'的特性,即数据结构是先进后出.下面我们看一段代码: function cat(a){ if(a<0){ return false; } console.

  • JavaScript 执行上下文的视角详解this使用

    目录 前言 全局执行上下文中的 this 函数执行上下文中的 this this 的设计缺陷以及应对方案 1. 嵌套函数中的 this 不会从外层函数中继承 2. 普通函数中的 this 默认指向全局对象 window 前言 在对象内部的方法中使用对象内部的属性是一个非常普遍的需求.但是 JavaScript 的作用域机制并不支持这一点,基于这个需求,JavaScript 有另外一套 this 机制. this 是和执行上下文绑定的,也就是说每个执行上下文中都有一个 this.执行上下文主要分为

  • javascript执行环境及作用域详解

    最近在重读<javascript高级程序设计3>,觉得应该写一些博客记录一下学习的一些知识,不然都忘光啦.今天要总结的是js执行环境和作用域. 首先来说一下执行环境 一.执行环境         书上概念,执行环境定义了变量或者函数有权访问的其他数据,决定了他们各自的行为.每个执行环境都有一个与之关联的变量对象.环境中定义的所有变量和函数都保存在这个对象中.虽然我们在编写代码的时候无法访问这个对象,但解析器在处理数据时会在后台用到它. 执行环境是一个概念,一种机制,它定义了变量或函数是否有权访

  • 关于Javascript闭包与应用的详解

    前言 Javascript闭包在学习过程中一般较难理解,本文从什么是闭包,常见闭包示例,闭包作用,闭包应用及闭包问题等方面来介绍闭包,希望能给大家带来更深层次的认识,有不恰当之处请指出,谢谢. 一.什么是闭包? 闭包是指一个嵌套的内部(子)函数引用了父函数作用域中数据的函数,这就产生了闭包. 关键理解: 1. 产生闭包必须要有嵌套函数 2. 闭包是函数,并是嵌套的内部函数 3. 闭包内部函数必须要引用父函数作用域中数据 如果不满足以上条件,则不能产生闭包,接下来示例说明. 1.1闭包满足条件代码

  • JavaScript中eval()函数用法详解

    eval() 函数计算 JavaScript 字符串,并把它作为脚本代码来执行. 如果参数是一个表达式,eval() 函数将执行表达式.如果参数是Javascript语句,eval()将执行 Javascript 语句. 语法 复制代码 代码如下: eval(string) 参数 描述 string 必需.要计算的字符串,其中含有要计算的 JavaScript 表达式或要执行的语句. eval()函数用法详解: 此函数可能使用的频率并不是太高,但是在某些情况下具有很大的作用,下面就介绍一下eva

  • Javascript 严格模式use strict详解

    严格模式:由ECMA-262规范定义的JavaScript标准,对javascrip的限制更强. (非严格的模式,被称为"马虎模式/稀松模式/懒散模式".) 一.严格模式的使用 严格模式可以在脚本或函数级别实现.(即全局和局部模式) 1.全局 在js文件的最前面添加    "use strict" 2.局部 在函数内部添加   "use strict",如下 function fn() { "use strict"; //so

  • JavaScript中 ES6 generator数据类型详解

    1. generator简介 generator 是ES6引入的新的数据类型, 看上去像一个函数,除了使用return返回, yield可以返回多次. generator 由function* 定义, (注意*号), 2. 示例 函数无法保存状态, 有时需要全局变量来保存数字: 2.1 'use strict'; function next_id(){ var id = 1; while(id<100){ yield id; id++; } return id; } // 测试: var x,

  • JavaScript数据类型的存储方法详解

    一个很基础的知识点,JavaScript中基本数据类型和引用数据类型是如何存储的. 由于自己是野生程序员,在刚开始学习程序设计的时候没有在意内存这些基础知识,导致后来在提到"什么什么是存在栈中的,栈中只是存了一个引用"这样的话时总是一脸懵逼.. 后来渐渐的了解了一些内存的知识,这部分还是非常有必要了解的. 基本数据结构 栈 栈,只允许在一段进行插入或者删除操作的线性表,是一种先进后出的数据结构. 堆 堆是基于散列算法的数据结构. 队列 队列是一种先进先出(FIFO)的数据结构. Jav

  • Javascript之BOM(window对象)详解

    ECMAScript是JavaScript的核心,但在web使用JavaScript,那么BOM(浏览器对象模型)才是真正的核心. BOM的核心对象是window,它表示浏览器的一个实例. 在浏览器中,window对象既是JavaScript访问浏览器窗口的一个接口,又是ECMAScript规定的Global对象.也就是说,在网页中定义的任何一个变量.对象和函数以window作为其Global对象. 1.全局作用域 既然window对象扮演着Global对象,那么所有在全局作用域中声明的对象.变

  • javascript 中的继承实例详解

    javascript 中的继承实例详解 阅读目录 原型链继承 借用构造函数 组合继承 寄生组合式继承 后记 继承有两种方式:接口继承和实现继承.接口继承只继承方法签名,而实现继承则继承实际的方法. 由于函数没有签名,在ECMAScript中无法实现接口继承.ECMAScript只支持实现继承,而且实现继承主要依靠原型链来实现. 下面介绍几种js的继承: 原型链继承 原型链继承实现的本质是重写原型对象,代之以一个新类型的实例.代码如下: function SuperType() { this.pr

  • JavaScript中的函数式编程详解

    函数式编程 函数式编程是一种编程范式,是一种构建计算机程序结构和元素的风格,它把计算看作是对数学函数的评估,避免了状态的变化和数据的可变,与函数式编程相对的是命令式编程.我们有这样一个需求,给数组的每个数字加一: // 数组每个数字加一, 命令式编程 let arr = [1, 2, 3, 4]; let newArr = []; for(let i = 0; i < arr.length; i++){ newArr.push(arr[i] + 1); } console.log(newArr)

随机推荐