执行上下文

执行上下文(Execution Contexts),简称上下文,是一种标准策略,用于跟踪ECMAScript实现对于代码运行时的评估。在任何工夫点,每个理论执行代码的代理最多有一个执行上下文。 这称为代理的运行执行上下文(running execution context)。

简而言之,变量或函数的上下文决定了它们能够拜访哪些数据,以及它们的行为。

上下文一共有以下三种:

  • 全局上下文
  • 函数上下文(部分上下文)
  • eval()调用外部的上下文

执行上下文栈

执行上下文堆栈(execution context stack)用于跟踪执行上下文。 正在运行的执行上下文始终是此堆栈的顶部元素。 每当管制从与以后运行的执行上下文相关联的可执行代码转移到与该执行上下文无关的可执行代码时,就会创立一个新的执行上下文。 新创建的执行上下文被压入堆栈,成为运行的执行上下文。

全局上下文

全局上下文是最外层的上下文。依据ECMAScript实现的宿主环境,示意全局上下文的对象可能不一样。在浏览器环境中,全局上下文就是咱们常说的window对象,因而所有通过var定义的全局变量和函数都会成为window对象的属性和办法。应用letconst的顶级申明不会定义在全局上下文中,但在作用域链解析成果上是一样的。

函数上下文

每个函数调用都有本人的函数上下文。当代码执行流进入函数时,函数的上下文被推到一个上下文栈上。在函数执行实现之后,上下文栈就会弹出该函数上下文,将控制权返还给之前的执行上下文。

eval()调用外部的上下文

在非严格模式下,eval函数外部变量的申明会影响调用上下文(callerContext

"use strict";var x = 1;let y = 3;eval("var x = 2;let y = 4;");eval("console.log(x, y);"); // 严格模式输入1 3;非严格模式输入2 3console.log(x, y); // 严格模式输入1 3;非严格模式输入2 3
如果调用上下文的代码或eval码是严格模式代码,则eval代码不能实例化调用eval的调用上下文的变量环境中的变量或函数绑定。 相同,这样的绑定在一个新的VariableEnvironment中实例化,只有eval代码能够拜访。 由letconstclass申明引入的绑定总是在新的LexicalEnvironment中实例化。

What's the difference between "LexicalEnvironment" and "VariableEnvironment" in spec

A LexicalEnvironment is a local lexical scope, e.g., for let-defined variables. If you define a variable with let in a catch block, it is only visible within the catch block, and to implement that in the spec, we use a LexicalEnvironment. VariableEnvironment is the scope for things like var-defined variables. vars can be thought of as "hoisting" to the top of the function. To implement this in the spec, we give functions a new VariableEnvironment, but say that blocks inherit the enclosing VariableEnvironment.

所以,非严格模式下,应用var申明的变量会影响调用上下文,由letconstclass申明的变量不会影响调用上下文。

作用域

在代码执行之前,所有ECMAScript代码都必须与作用域(Realms)相关联。 从概念上讲,一个作用域由一组外在对象、一个ECMAScript全局环境、在该全局环境范畴内加载的所有ECMAScript代码以及其余相干的状态和资源组成。

当咱们创立了一个函数或者 {} 块,就会生成一个新的作用域。须要留神的是,通过 var 创立的变量只有函数作用域,而通过 letconst 创立的变量既有函数作用域,也有块作用域。

作用域分为以下两种:

  • 词法作用域(动态作用域)
  • 动静作用域

词法作用域

What is lexical scope?

词法作用域指一个函数由定义即可确定能拜访的作用域,在编译时即可推导进去。
function foo() {    let a = 5;    function foo2() {        console.log(a);    }    return foo2;}

动静作用域

动静作用域,指函数由调用函数的作用域链确定的可拜访作用域,是动静的。
function fn() {    console.log('隐式绑定', this.a);}const obj = {    a: 1,    fn}obj.fn = fn;obj.fn();

作用域链

每一个作用域都有对其父作用域的援用。当咱们应用一个变量的时候,Javascript引擎 会通过变量名在以后作用域查找,若没有查找到,会始终沿着作用域链始终向上查找,直到 global 全局作用域。作用域链决定了各级上下文中的代码在拜访变量和函数时的程序。

作用域链加强

某些语句会导致在作用域链前端长期增加一个上下文,这个上下文在代码执行后会被删除。

  • try/catch语句的catch
  • with语句

    function buildUrl() {  let qs = "?debug=true";  with(location) {      let url = href + qs;  }  return url;}

this指向问题

this是在执行时动静读取上下文决定的,而不是创立时

全局上下文中的this

无论是否在严格模式下,在全局执行上下文中(在任何函数体内部)this都指代全局对象

// 在浏览器中,window对象同时也是全局对象console.log(this===window); // true

函数上下文中的this

在函数外部,this的值取决于函数被调用的形式

函数间接调用

函数间接调用中,非严格模式下this指向的是window,严格模式下this指向的是undefined

function foo() {    console.log('函数外部this', this);}foo();

隐式绑定

this指向它的调用者,即谁调用函数,他就指向谁。

function fn() {    console.log('隐式绑定', this.a);}const obj = {    a: 1,    fn}obj.fn = fn;obj.fn(); // 1

显式绑定

通过callapplycall办法扭转this的行为。

var name = 'a';function foo() {    console.log('函数外部this', this.name);}foo(); // afoo.call({name: 'b'}); // bfoo.apply({name: 'c'}); // cconst bindFoo = foo.bind({name: 'd'});bindFoo(); // d

构造函数的new绑定

当一个函数用作构造函数时(应用new关键字),它的this被绑定到正在结构的新对象。

new关键字会进行如下操作:

  1. 创立一个空的简略JavaScript对象(即{});
  2. 为步骤1新创建的对象增加属性__proto__,将该属性链接至构造函数的原型对象 ;
  3. 将步骤1新创建的对象作为this的上下文 ;
  4. 如果该函数没有返回对象,则返回this。

箭头函数中的this

箭头函数中,this与关闭词法上下文的this保持一致。

setTimeout回调函数中的this

setTimeout回调函数中的this指向window。能够应用箭头函数作为回调函数,让回调中的this指向父级作用域中的this。

闭包

MDN的解释:

一个函数和对其四周状态(lexical environment,词法环境)的援用捆绑在一起(或者说函数被援用突围),这样的组合就是闭包(closure)。

上面介绍一些闭包的利用:

事件处理(异步执行)的闭包

解决var的变量晋升问题

    let lis = document.getElementsByTagName('li');    for(var i = 0; i < lis.length; i++) {        (function(i) {            lis[i].onclick = function() {                console.log(i);            }        })(i);    }

实现公有变量

function People(num) { // 构造函数    let age = num;    this.getAge = function() {        return age;    };    this.addAge = function() {        age++;    };}let tom = new People(18);let pony = new People(20);console.log(tom.getAge()); // 18pony.addAge();console.log(pony.getAge()); // 21

装璜器函数

A function decorator is a higher-order function that takes one function as an argument and returns another function, and the returned function is a variation of the argument function — Javascript Allongé
  • 函数防抖与函数节流(详情点击这里)
  • once(fn)

    function once(fn){  let returnValue;  let canRun = true;  return function runOnce(){      if(canRun) {          returnValue = fn.apply(this, arguments);          canRun = false;      }      return returnValue;  }}var processonce = once(process);processonce(); //processprocessonce(); //

应用闭包绑定函数上下文(实现bind函数的性能)

Function.prototype.myBind = function() {    let fn = this,        args = [...arguments],        obj = args.shift();    return function() {        return fn.apply(object, args.concat(...arguments));    }}