1. 规范ES5英文版ES5中文版ES6英文版2. 进入全局代码执行全局代码2.1 1.global.jssrc1.global.jsvar a = 1; function one(c) { var b = 2; console.log(a, b, c); } one(3);2.2 1.global.run.jssrc1.global.run.js/** var a = 1; function one(c) { var b = 2; console.log(a, b, c); } one(3); */ const LexicalEnvironment = require('./LexicalEnvironment'); const ExecutionContext = require('./ExecutionContext'); const ECStack = require('./ECStack'); //1.将变量环境设置为全局环境 2.将词法环境设置为 全局环境 const globalLexicalEnvironment = LexicalEnvironment.NewObjectEnvironment(global, null); const globalEC = new ExecutionContext(globalLexicalEnvironment, global); ECStack.push(globalEC); //令 env 为当前运行的执行环境的环境变量的 环境记录 let env = ECStack.current.lexicalEnvironment.environmentRecord; //如果 code 是 eval 代码 ，则令 configurableBindings 为 true，否则令 configurableBindings 为 false。 let configurableBindings = false; //如果代码是 严格模式下的代码 ，则令 strict 为 true，否则令 strict 为 false。 let strict = false; //按源码顺序遍历 code，对于每一个 VariableDeclaration 和 VariableDeclarationNoIn 表达式： //令 dn 为 d 中的标识符。 let dn = 'a'; //以 dn 为参数，调用 env 的 HasBinding 具体方法，并令 varAlreadyDeclared 为调用的结果 let varAlreadyDeclared = env.HasBinding(dn); //如果 varAlreadyDeclared 为 false，则： if (!varAlreadyDeclared) { //以 dn 和 configurableBindings 为参数，调用 env 的 CreateMutableBinding 具体方法。 env.CreateMutableBinding(dn, configurableBindings); //以 dn、undefined 和 strict 为参数，调用 env 的 SetMutableBinding 具体方法。 env.SetMutableBinding(dn, undefined, strict); } console.log(env.GetBindingValue('a'));2.3 LexicalEnvironment.jssrcLexicalEnvironment.jsconst DeclarativeEnvironmentRecords = require("./DeclarativeEnvironmentRecords"); const ObjectEnvironmentRecords = require("./ObjectEnvironmentRecords"); class LexicalEnvironment { /** * 当调用 GetIdentifierReference 抽象运算时 * @param {*} lex 词法环境 * @param {*} name 标识符字符串 * @param {*} strict 布尔型标识 */ static GetIdentifierReference(lex, name, strict) { //if (!lex) return { name: undefined }; if (!lex) throw new Error(`ReferenceError: ${name} is not defined`); let envRec = lex.environmentRecord; let exists = envRec.HasBinding(name); if (exists) { const value = envRec.GetBindingValue(name); return { name: value }; } else { lex = lex.outer; return LexicalEnvironment.GetIdentifierReference(lex, name, strict); } } /** * 创建新的声明式词法环境 * @param {*} lexicalEnvironment 父词法环境 * @returns */ static NewDeclarativeEnvironment(lexicalEnvironment) { let env = new LexicalEnvironment(); let envRec = new DeclarativeEnvironmentRecords(); env.environmentRecord = envRec; env.outer = lexicalEnvironment; return env; } /** * 创建新的对象式词法环境 * @param {*} lexicalEnvironment 父词法环境 * @returns */ static NewObjectEnvironment(object, lexicalEnvironment) { let env = new LexicalEnvironment(); let envRec = new ObjectEnvironmentRecords(object); env.environmentRecord = envRec; env.outer = lexicalEnvironment; return env;; } } module.exports = LexicalEnvironment;2.4 DeclarativeEnvironmentRecords.jssrcDeclarativeEnvironmentRecords.jsconst EnvironmentRecord = require('./EnvironmentRecord'); class DeclarativeEnvironmentRecords extends EnvironmentRecord { /** * 判断环境记录项是否包含对某个标识符的绑定 * 如果包含该绑定则返回 true，反之返回 false。其中字符串 N 是标识符文本。 * @param {*} N 名称 */ HasBinding(N) { let envRec = this; return N in envRec; } /** * 在环境记录项中创建一个新的可变绑定 * @param {*} N 绑定名称 * @param {*} D 可选参数 D 的值为true，则该绑定在后续操作中可以被删除 */ CreateMutableBinding(N, D) { //令 envRec 为函数调用时对应的声明式环境记录项 let envRec = this; //执行断言：envRec 没有 N 的绑定。 console.assert(!this.HasBinding(N)); //在 envRec 中为 N 创建一个可变绑定,并将绑定的值设置为 undefined //如果 D 为 true 则新创建的绑定可在后续操作中通过调用 DeleteBinding 删除 Object.defineProperty(envRec, N, { value: undefined, writable: true, configurable: D }); } /** * 环境记录项中设置一个已经存在的绑定的值 * @param {*} N 绑定名称 * @param {*} V 绑定的值 * @param {*} S S 用于指定是否为严格模式 当 S 为 true 并且该绑定不允许赋值时，则抛出一个 TypeError 异常 */ SetMutableBinding(N, V, S) { //令 envRec 为函数调用时对应的声明式环境记录项 let envRec = this; //执行断言：envRec 必须有 N 的绑定。 console.assert(this.HasBinding(N)); //否则该操作会尝试修改一个不可变绑定的值，因此如果 S 的值为 true，则抛出一个 TypeError 异常。 const propertyDescriptor = Object.getOwnPropertyDescriptor(envRec, N); if (S && !propertyDescriptor.writable) { throw new Error("TypeError: Assignment to constant variable."); } //如果 envRec 中 N 的绑定为可变绑定，则将其值修改为 V envRec[N] = V; } /** * 返回环境记录项中一个已经存在的绑定的值 * @param {*} N 绑定的名称 * @param {*} S 用于指定是否为严格模式 如果 S 的值为 true 并且该绑定不存在或未初始化，则抛出一个 ReferenceError 异常 */ GetBindingValue(N, S) { //令 envRec 为函数调用时对应的声明式环境记录项 let envRec = this; //执行断言：envRec 必须有 N 的绑定。 console.assert(this.HasBinding(N)); //如果 envRec 中 N 的绑定是一个未初始化的不可变绑定，则如果 S 为 false，返回 undefined，否则抛出一个 ReferenceError 异常。 const V = envRec[N]; const propertyDescriptor = Object.getOwnPropertyDescriptor(envRec, N); if (!propertyDescriptor.uninitialized) { if (!S) return V; throw new Error(`ReferenceError: Cannot access '${N}' before initialization`); } //否则返回 envRec 中与 N 绑定的值 return V; } /** * 从环境记录项中删除一个绑定 * @param {*} N 绑定的名称 如果 N 指定的绑定存在，将其删除并返回 true。如果绑定存在但无法删除则返回false。如果绑定不存在则返回 true */ DeleteBinding(N) { //令 envRec 为函数调用时对应的声明式环境记录项 let envRec = this; if (!this.HasBinding(N)) { return true; } const propertyDescriptor = Object.getOwnPropertyDescriptor(envRec, N); if (!propertyDescriptor.configurable) { return false; } delete envRec[N]; return true; } /** * 当从该环境记录项的绑定中获取一个函数对象并且调用时，该方法返回该函数对象使用的 this 对象的值。 */ ImplicitThisValue() { return undefined; } /** * 创建一个不可变绑定 * @param {*} N 名称 */ CreateImmutableBinding(N) { //令 envRec 为函数调用时对应的声明式环境记录项 let envRec = this; //执行断言：envRec 没有 N 的绑定。 console.assert(!this.HasBinding(N)); //在 envRec 中为 N 创建一个不可变绑定，并记录为未初始化 Object.defineProperty(envRec, N, { value: undefined, uninitialized: true }); } /** * 将当前名称为参数 N 的绑定的值修改为参数 V 指定的值 * @param {*} N 当前名称 * @param {*} V 绑定的值 */ InitializeImmutableBinding(N, V) { //令 envRec 为函数调用时对应的声明式环境记录项 let envRec = this; //执行断言：envRec 没有 N 的绑定 const propertyDescriptor = Object.getOwnPropertyDescriptor(envRec, N); console.assert(propertyDescriptor && propertyDescriptor.uninitialized); envRec[N] = V; propertyDescriptor.uninitialized = true; } } module.exports = DeclarativeEnvironmentRecords;2.5 EnvironmentRecord.jssrcEnvironmentRecord.jsclass EnvironmentRecord { } module.exports = EnvironmentRecord;2.6 ObjectEnvironmentRecords.jssrcObjectEnvironmentRecords.jsconst EnvironmentRecord = require('./EnvironmentRecord'); class ObjectEnvironmentRecords extends EnvironmentRecord { constructor(bindings) { super(); this.bindings = bindings; this.provideThis = false; } /** * 判断其关联的绑定对象是否有名为 N 的属性 * 如果包含该绑定则返回 true，反之返回 false。其中字符串 N 是标识符文本。 * @param {*} N 名称 */ HasBinding(N) { const envRec = this; const bindings = envRec.bindings; return bindings.hasOwnProperty(N); } /** * 会在其关联的绑定对象上创建一个名称为 N 的属性 * @param {*} N 绑定名称 * @param {*} D 可选参数 D 的值为true，则该绑定在后续操作中可以被删除 */ CreateMutableBinding(N, D) { //令 envRec 为函数调用时对应的声明式环境记录项 const envRec = this; const bindings = envRec.bindings; //执行断言：envRec 没有 N 的绑定。 console.assert(!bindings.hasOwnProperty(N)); const configValue = D; //在 envRec 中为 N 创建一个可变绑定,并将绑定的值设置为 undefined //如果 D 为 true 则新创建的绑定可在后续操作中通过调用 DeleteBinding 删除 Object.defineProperty(bindings, N, { value: undefined, enumerable: true, configurable: configValue, writable: true }); } /** * 尝试设置其关联的绑定对象中名为 N 的属性的值为 V * @param {*} N 绑定名称 * @param {*} V 绑定的值 * @param {*} S S 用于指定是否为严格模式 当 S 为 true 并且该绑定不允许赋值时，则抛出一个 TypeError 异常 */ SetMutableBinding(N, V, S) { //令 envRec 为函数调用时对应的声明式环境记录项 const envRec = this; const bindings = envRec.bindings; //执行断言：envRec 必须有 N 的绑定。 console.assert(this.HasBinding(N)); //否则该操作会尝试修改一个不可变绑定的值，因此如果 S 的值为 true，则抛出一个 TypeError 异常。 const propertyDescriptor = Object.getOwnPropertyDescriptor(bindings, N); if (S && !propertyDescriptor.writable) { throw new Error("TypeError: Assignment to constant variable."); } //如果 envRec 中 N 的绑定为可变绑定，则将其值修改为 V bindings[N] = V; } /** *返回其关联的绑定对象中名为 N 的属性的值 * @param {*} N 绑定的名称 * @param {*} S 用于指定是否为严格模式 如果 S 的值为 true 并且该绑定不存在或未初始化，则抛出一个 ReferenceError 异常 */ GetBindingValue(N, S) { //令 envRec 为函数调用时对应的声明式环境记录项 const envRec = this; const bindings = envRec.bindings; //执行断言：envRec 必须有 N 的绑定。 console.assert(bindings.hasOwnProperty(N), `变量${N}尚未定义`); //如果 envRec 中 N 的绑定是一个未初始化的不可变绑定，则如果 S 为 false，返回 undefined，否则抛出一个 ReferenceError 异常。 const V = bindings[N]; const propertyDescriptor = Object.getOwnPropertyDescriptor(bindings, N); if (!propertyDescriptor.uninitialized) { if (!S) return V; throw new Error(`ReferenceError: Cannot access '${N}' before initialization`); } //否则返回 envRec 中与 N 绑定的值 return V; } /** * 删除其关联的绑定对象上 Configurable 特性的值为 true 的属性所对应的绑定 * @param {*} N 绑定的名称 如果 N 指定的绑定存在，将其删除并返回 true。如果绑定存在但无法删除则返回false。如果绑定不存在则返回 true */ DeleteBinding(N) { //令 envRec 为函数调用时对应的声明式环境记录项 const envRec = this; const bindings = envRec.bindings; if (!bindings.hasOwnProperty(N)) { return true; } const propertyDescriptor = Object.getOwnPropertyDescriptor(bindings, N); if (!propertyDescriptor.configurable) { return false; } delete bindings[N]; return true; } /** * 返回 undefined，除非其 provideThis 标识的值为 true。 */ ImplicitThisValue() { const envRec = this; if (provideThis) { return envRec.bindings; } return undefined; } } module.exports = ObjectEnvironmentRecords;2.7 ExecutionContext.jssrcExecutionContext.jsclass ExecutionContext { constructor(lexicalEnvironment, thisBinding) { this.variableEnvironment = this.lexicalEnvironment = lexicalEnvironment; this.thisBinding = thisBinding; } } module.exports = ExecutionContext;2.8 ECStack.jssrcECStack.jsclass ECStack { constructor() { this.contexts = []; } push(EC) { this.contexts.push(EC); } get current() { return this.contexts[this.contexts.length - 1]; } pop() { this.contexts.pop(); } } module.exports = new ECStack();3. 注册函数3.1 FunctionDeclaration.jssrc/FunctionDeclaration.jsclass FunctionDeclaration { static createInstance(fn, FormalParameterList, FunctionBody, Scope) { //Strict 为布尔标记 let Strict = false; //按照如下步骤构建函数对象 //创建一个新的 ECMAScript 原生对象，令 F 为此对象 let F = { name: fn }; //依照 8.12 描述设定 F 的除 Get 以外的所有内部方法 //设定 F 的 Class 内部属性为 "Function"。 F[` Class`] = Function; //设定 F 的 Prototype 内部属性为 15.3.3.1 指定的标准内置 Function 对象的 prototype 属性 //F.__proto__=Function.prototype F[`Prototype`] = Function.prototype; //依照 15.3.5.4 描述，设定 F 的Get 内部属性 //依照 13.2.1 描述，设定 F 的Call 内部属性 //依照 13.2.2 描述，设定 F 的Construct 内部属性 //依照 15.3.5.3 描述，设定 F 的HasInstance 内部属性 //设定 F 的Scope 内部属性为 Scope 的值 F[`Scope`] = Scope; //令 names 为一个列表容器，其中元素是以从左到右的文本顺序对应 FormalParameterList 的标识符的字符串。 let names = FormalParameterList; //设定 F 的 FormalParameters 内部属性为 names F[`FormalParameters`] = names; //设定 F 的 Code 内部属性为 FunctionBody F[`Code`] = FunctionBody; //设定 F 的 Extensible 内部属性为 true。 F[`Extensible`] = true; //令 len 为 FormalParameterList 指定的形式参数的个数。如果没有指定参数，则令 len 为 0。 let len = FormalParameterList.length; //以参数 "length"，属性描述符 {Value: len, Writable: false, Enumerable: false, Configurable: false}，false 调用 F 的 DefineOwnProperty 内部方法 Object.defineProperty(F, 'length', { value: len, writable: false, enumerable: false, configurable: false }); //令 proto 为仿佛使用 new Object() 表达式创建新对象的结果，其中 Object 是标准内置构造器名 let proto = new Object(); //以参数 "constructor", 属性描述符 {Value: F, { Writable: true, Enumerable: false, Configurable: true}, false 调用 proto 的 DefineOwnProperty 内部方法。 Object.defineProperty(proto, 'constructor', { value: F, writable: true, enumerable: false, configurable: false }); //以参数 "prototype", 属性描述符 {Value: proto, { Writable: true, Enumerable: false, Configurable: false}, false 调用 F 的 DefineOwnProperty 内部方法。 //每个函数都会自动创建一个 prototype 属性，以满足函数会被当作构造器的可能性 Object.defineProperty(F, 'prototype', { value: proto, writable: true, enumerable: false, configurable: false }); return F; } } module.exports = FunctionDeclaration;3.2 global.run.jssrc1.global.run.js/** var a = 1; function one(c) { var b = 2; console.log(a, b,c); } one(); */ const LexicalEnvironment = require('./LexicalEnvironment'); const ExecutionContext = require('./ExecutionContext'); const ECStack = require('./ECStack'); +const FunctionDeclaration = require('./FunctionDeclaration'); //1.将变量环境设置为全局环境 2.将词法环境设置为 全局环境 const globalLexicalEnvironment = LexicalEnvironment.NewObjectEnvironment(global, null); const globalEC = new ExecutionContext(globalLexicalEnvironment, global); ECStack.push(globalEC); //令 env 为当前运行的执行环境的环境变量的 环境记录 let env = ECStack.current.lexicalEnvironment.environmentRecord; //如果 code 是 eval 代码 ，则令 configurableBindings 为 true，否则令 configurableBindings 为 false。 let configurableBindings = false; //如果代码是 严格模式下的代码 ，则令 strict 为 true，否则令 strict 为 false。 let strict = false; //按源码顺序遍历 code，对于每一个 VariableDeclaration 和 VariableDeclarationNoIn 表达式： //令 dn 为 d 中的标识符。 let dn = 'a'; //以 dn 为参数，调用 env 的 HasBinding 具体方法，并令 varAlreadyDeclared 为调用的结果 let varAlreadyDeclared = env.HasBinding(dn); //如果 varAlreadyDeclared 为 false，则： if (!varAlreadyDeclared) { //以 dn 和 configurableBindings 为参数，调用 env 的 CreateMutableBinding 具体方法。 env.CreateMutableBinding(dn, configurableBindings); //以 dn、undefined 和 strict 为参数，调用 env 的 SetMutableBinding 具体方法。 env.SetMutableBinding(dn, undefined, strict); } console.log(env.GetBindingValue('a')); +//按源码顺序遍历 code，对于每一个 FunctionDeclaration 表达式 f： +//令 fn 为 FunctionDeclaration 表达式 f 中的 标识符 +let fn = 'one'; +//创建函数对象 +//指定 FormalParameterList 为可选参数列表 +let FormalParameterList = ['c']; +//指定 FunctionBody 为函数体 +let FunctionBody = ` + var b = 2; + console.log(a, b); +`; +//指定 Scope 为 词法环境 +let Scope = ECStack.current.lexicalEnvironment; +//按 第 13 章 中所述的步骤初始化 FunctionDeclaration 表达式 ，并令 fo 为初始化的结果。 +let fo = FunctionDeclaration.createInstance(fn, FormalParameterList, FunctionBody, Scope); +//以 fn 为参数，调用 env 的 HasBinding 具体方法，并令 argAlreadyDeclared 为调用的结果。 +let argAlreadyDeclared = env.HasBinding(fn); +if (!argAlreadyDeclared) { + env.CreateMutableBinding(fn, configurableBindings); +} else { + //否则如果 env 是全局环境的 环境记录 对象，则 + //令 go 为全局对象。 + let go = global; + //以 fn 为参数，调用 go 和 GetProperty 内部方法，并令 existingProp 为调用的结果。 + let existingProp = Object.getOwnPropertyDescriptor(go, fn); + if (existingProp.configurable) { + Object.defineProperty(go, fn, { value: undefined, writable: true, enumerable: true, configurable: +configurableBindings }); + if (!existingProp.writable) { + throw new Error(`TypeError`); + } + } +} +env.SetMutableBinding(fn, fo, strict); +console.log(env.GetBindingValue('one'));4. 执行全局代码和函数4.1 CreateArgumentsObject.jssrcCreateArgumentsObject.js/** * Arguments 对象通过调用抽象方法 CreateArgumentsObject 创建， * 调用时将以下参数传入：func, names, args, env, strict。 * @param {*} func 将要执行的函数对象作为 func 参数 * @param {*} names 将该函数的所有形参名加入一个 List 列表，作为 names 参数 * @param {*} args 将所有传给内部方法 Call 的实际参数作为 args 参数 * @param {*} env 将该函数代码的环境变量作为 env 参数 * @param {*} strict 将该函数代码是否为严格代码作为strict 参数 */ function CreateArgumentsObject(func, names, args, env, strict) { //令 len 为参数 args 的元素个数 let len = args.length; //令 obj 为一个新建的 ECMAScript 对象 //按照 8.12 章节中的规范去设定 obj 对象的所有内部方法 let obj = {}; //将 obj 对象的内部属性 Class 设置为 "Arguments" obj[`Class`] = 'Arguments'; //令 Object 为标准的内置对象的构造函数 (15.2.2) obj.constructor = Object; //将 obj 对象的内部属性 Prototype 设置为标准的内置对象的原型对象 obj[`Prototype`] = Object.prototype; //调用 obj 的内部方法 DefineOwnProperty，将 "length" 传递进去，属性描述符为：{Value: len, Writable: true, Enumerable: false, Configurable: true}，参数为 false Object.defineProperty(obj, 'length', { value: len, writable: true, enumerable: false, configurable: true }); let indx = len - 1; while (indx >= 0) { let val = args[indx]; Object.defineProperty(obj, indx.toString(), { value: val, writable: true, enumerable: true, configurable: true }); indx = indx - 1; } return obj; } module.exports = CreateArgumentsObject;4.2 global.run.jssrc1.global.run.js/** var a = 1; function one(c) { var b = 2; console.log(a, b,c); } one(); */ const LexicalEnvironment = require('./LexicalEnvironment'); const ExecutionContext = require('./ExecutionContext'); const ECStack = require('./ECStack'); const FunctionDeclaration = require('./FunctionDeclaration'); +const CreateArgumentsObject = require('./CreateArgumentsObject'); //1.将变量环境设置为全局环境 2.将词法环境设置为 全局环境 const globalLexicalEnvironment = LexicalEnvironment.NewObjectEnvironment(global, null); const globalEC = new ExecutionContext(globalLexicalEnvironment, global); ECStack.push(globalEC); //令 env 为当前运行的执行环境的环境变量的 环境记录 let env = ECStack.current.lexicalEnvironment.environmentRecord; //如果 code 是 eval 代码 ，则令 configurableBindings 为 true，否则令 configurableBindings 为 false。 let configurableBindings = false; //如果代码是 严格模式下的代码 ，则令 strict 为 true，否则令 strict 为 false。 let strict = false; //按源码顺序遍历 code，对于每一个 VariableDeclaration 和 VariableDeclarationNoIn 表达式： //令 dn 为 d 中的标识符。 let dn = 'a'; //以 dn 为参数，调用 env 的 HasBinding 具体方法，并令 varAlreadyDeclared 为调用的结果 let varAlreadyDeclared = env.HasBinding(dn); //如果 varAlreadyDeclared 为 false，则： if (!varAlreadyDeclared) { //以 dn 和 configurableBindings 为参数，调用 env 的 CreateMutableBinding 具体方法。 env.CreateMutableBinding(dn, configurableBindings); //以 dn、undefined 和 strict 为参数，调用 env 的 SetMutableBinding 具体方法。 env.SetMutableBinding(dn, undefined, strict); } console.log(env.GetBindingValue('a'));//undefined //按源码顺序遍历 code，对于每一个 FunctionDeclaration 表达式 f： //令 fn 为 FunctionDeclaration 表达式 f 中的 标识符 let fn = 'one'; //创建函数对象 //指定 FormalParameterList 为可选参数列表 let FormalParameterList = ['c']; //指定 FunctionBody 为函数体 let FunctionBody = ` var b = 2; console.log(a, b); `; //指定 Scope 为 词法环境 let Scope = ECStack.current.lexicalEnvironment; //按 第 13 章 中所述的步骤初始化 FunctionDeclaration 表达式 ，并令 fo 为初始化的结果。 let fo = FunctionDeclaration.createInstance(fn, FormalParameterList, FunctionBody, Scope); //以 fn 为参数，调用 env 的 HasBinding 具体方法，并令 argAlreadyDeclared 为调用的结果。 let argAlreadyDeclared = env.HasBinding(fn); if (!argAlreadyDeclared) { env.CreateMutableBinding(fn, configurableBindings); } else { //否则如果 env 是全局环境的 环境记录 对象，则 //令 go 为全局对象。 let go = global; //以 fn 为参数，调用 go 和 GetProperty 内部方法，并令 existingProp 为调用的结果。 let existingProp = Object.getOwnPropertyDescriptor(go, fn); if (existingProp.configurable) { Object.defineProperty(go, fn, { value: undefined, writable: true, enumerable: true, configurable: configurableBindings }); if (!existingProp.writable) { throw new Error(`TypeError`); } } } env.SetMutableBinding(fn, fo, strict); //console.log(env.GetBindingValue('one'));//1 +// 给a赋值为1 +env.SetMutableBinding('a', 1); +console.log(env.GetBindingValue('a'));//1 +//进入函数代码 +//当控制流根据一个函数对象 F、调用者提供的 thisArg 以及调用者提供的 argumentList，进入 函数代码 的执行环境时，执行以下步+骤： +let thisArg = global; +let argumentList = [3]; +//以 F 的 Scope 内部属性为参数调用 NewDeclarativeEnvironment，并令 localEnv 为调用的结果 +let localEnv = LexicalEnvironment.NewDeclarativeEnvironment(fo[`Scope`]); +//设词法环境为 localEnv 设变量环境为 localEnv +let oneExecutionContext = new ExecutionContext(localEnv, thisArg); +ECStack.push(oneExecutionContext); +//按 [10.5](#10.5) 描述的方案，使用 函数代码 code 和 argumentList 执行定义绑定初始化步骤 +env = ECStack.current.lexicalEnvironment.environmentRecord; +configurableBindings = false; +strict = false; +//令 func 为通过 Call 内部属性初始化 code 的执行的函数对象 +let func = fo; +//令 code 为 F 的 Code 内部属性的值。 +let code = func[`Code`]; +//令 names 为 func 的 FormalParameters 内部属性。 +names = func[`FormalParameters`]; +let args = [3]; +//令 argCount 为 args 中元素的数量 +let argCount = args.length; +let n = 0; +names.forEach(argName => { + n += 1; + let v = n > argCount ? undefined : args[n - 1]; + //以 argName 为参数，调用 env 的 HasBinding 具体方法，并令 argAlreadyDeclared 为调用的结果。 + argAlreadyDeclared = env.HasBinding(argName); + if (!argAlreadyDeclared) { + env.CreateMutableBinding(argName); + } + env.SetMutableBinding(argName, v, strict); +}); +let argumentsAlreadyDeclared = env.HasBinding('arguments'); +if (!argumentsAlreadyDeclared) { + let argsObj = CreateArgumentsObject(func, names, args, env, strict); + env.CreateMutableBinding('arguments'); + env.SetMutableBinding('arguments', argsObj); +} + +dn = 'b'; +//以 dn 为参数，调用 env 的 HasBinding 具体方法，并令 varAlreadyDeclared 为调用的结果 +varAlreadyDeclared = env.HasBinding(dn); +//如果 varAlreadyDeclared 为 false，则： +if (!varAlreadyDeclared) { + //以 dn 和 configurableBindings 为参数，调用 env 的 CreateMutableBinding 具体方法。 + env.CreateMutableBinding(dn, configurableBindings); + //以 dn、undefined 和 strict 为参数，调用 env 的 SetMutableBinding 具体方法。 + env.SetMutableBinding(dn, undefined, strict); +} +env.SetMutableBinding(dn, 2); + +console.log(env.GetBindingValue('arguments'));//['3'] + +console.log( + LexicalEnvironment.GetIdentifierReference(ECStack.current.lexicalEnvironment, 'a'), + LexicalEnvironment.GetIdentifierReference(ECStack.current.lexicalEnvironment, 'b'), + LexicalEnvironment.GetIdentifierReference(ECStack.current.lexicalEnvironment, 'c') +);5. 块级作用域块级作用域5.1 block.jsvar a = 1; function one(c) { var b = 2; console.log(a, b, c, e /**,f*/); let d = 4; { let d = 5; var e = 6; let f = 7; console.log(a, b, c, d, e, f); } } one(3);5.2 block.run.jssrcblock.run.js/** var a = 1; function one(c) { var b = 2; console.log(a, b, c, e); let d = 4; { let d = 5; var e = 6; console.log(a, b, c, d, e); } } one(3); */ const LexicalEnvironment = require('./LexicalEnvironment'); const ExecutionContext = require('./ExecutionContext'); const ECStack = require('./ECStack'); const FunctionDeclaration = require('./FunctionDeclaration'); const CreateArgumentsObject = require('./CreateArgumentsObject'); //1.将变量环境设置为全局环境 2.将词法环境设置为 全局环境 const globalLexicalEnvironment = LexicalEnvironment.NewObjectEnvironment(global, null); const globalEC = new ExecutionContext(globalLexicalEnvironment, global); ECStack.push(globalEC); //令 env 为当前运行的执行环境的环境变量的 环境记录 let env = ECStack.current.lexicalEnvironment.environmentRecord; //如果 code 是 eval 代码 ，则令 configurableBindings 为 true，否则令 configurableBindings 为 false。 let configurableBindings = false; //如果代码是 严格模式下的代码 ，则令 strict 为 true，否则令 strict 为 false。 let strict = false; //按源码顺序遍历 code，对于每一个 VariableDeclaration 和 VariableDeclarationNoIn 表达式： //令 dn 为 d 中的标识符。 let dn = 'a'; //以 dn 为参数，调用 env 的 HasBinding 具体方法，并令 varAlreadyDeclared 为调用的结果 let varAlreadyDeclared = env.HasBinding(dn); //如果 varAlreadyDeclared 为 false，则： if (!varAlreadyDeclared) { //以 dn 和 configurableBindings 为参数，调用 env 的 CreateMutableBinding 具体方法。 env.CreateMutableBinding(dn, configurableBindings); //以 dn、undefined 和 strict 为参数，调用 env 的 SetMutableBinding 具体方法。 env.SetMutableBinding(dn, undefined, strict); } console.log(env.GetBindingValue('a'));//undefined //按源码顺序遍历 code，对于每一个 FunctionDeclaration 表达式 f： //令 fn 为 FunctionDeclaration 表达式 f 中的 标识符 let fn = 'one'; //创建函数对象 //指定 FormalParameterList 为可选参数列表 let FormalParameterList = ['c']; //指定 FunctionBody 为函数体 let FunctionBody = ` var b = 2; console.log(a, b); `; //指定 Scope 为 词法环境 let Scope = ECStack.current.lexicalEnvironment; //按 第 13 章 中所述的步骤初始化 FunctionDeclaration 表达式 ，并令 fo 为初始化的结果。 let fo = FunctionDeclaration.createInstance(fn, FormalParameterList, FunctionBody, Scope); //以 fn 为参数，调用 env 的 HasBinding 具体方法，并令 argAlreadyDeclared 为调用的结果。 let argAlreadyDeclared = env.HasBinding(fn); if (!argAlreadyDeclared) { env.CreateMutableBinding(fn, configurableBindings); } else { //否则如果 env 是全局环境的 环境记录 对象，则 //令 go 为全局对象。 let go = global; //以 fn 为参数，调用 go 和 GetProperty 内部方法，并令 existingProp 为调用的结果。 let existingProp = Object.getOwnPropertyDescriptor(go, fn); if (existingProp.configurable) { Object.defineProperty(go, fn, { value: undefined, writable: true, enumerable: true, configurable: configurableBindings }); if (!existingProp.writable) { throw new Error(`TypeError`); } } } env.SetMutableBinding(fn, fo, strict); //console.log(env.GetBindingValue('one'));//1 // 给a赋值为1 env.SetMutableBinding('a', 1); console.log(env.GetBindingValue('a'));//1 //进入函数代码 //当控制流根据一个函数对象 F、调用者提供的 thisArg 以及调用者提供的 argumentList，进入 函数代码 的执行环境时，执行以下步骤： let thisArg = global; let argumentList = [3]; //以 F 的 Scope 内部属性为参数调用 NewDeclarativeEnvironment，并令 localEnv 为调用的结果 let localEnv = LexicalEnvironment.NewDeclarativeEnvironment(fo[`Scope`]); //设词法环境为 localEnv 设变量环境为 localEnv let oneExecutionContext = new ExecutionContext(localEnv, thisArg); ECStack.push(oneExecutionContext); //按 [10.5](#10.5) 描述的方案，使用 函数代码 code 和 argumentList 执行定义绑定初始化步骤 env = ECStack.current.lexicalEnvironment.environmentRecord; configurableBindings = false; strict = false; //令 func 为通过 Call 内部属性初始化 code 的执行的函数对象 let func = fo; //令 code 为 F 的 Code 内部属性的值。 let code = func[`Code`]; //令 names 为 func 的 FormalParameters 内部属性。 names = func[`FormalParameters`]; let args = [3]; //令 argCount 为 args 中元素的数量 let argCount = args.length; let n = 0; names.forEach(argName => { n += 1; let v = n > argCount ? undefined : args[n - 1]; //以 argName 为参数，调用 env 的 HasBinding 具体方法，并令 argAlreadyDeclared 为调用的结果。 argAlreadyDeclared = env.HasBinding(argName); if (!argAlreadyDeclared) { env.CreateMutableBinding(argName); } env.SetMutableBinding(argName, v, strict); }); let argumentsAlreadyDeclared = env.HasBinding('arguments'); if (!argumentsAlreadyDeclared) { let argsObj = CreateArgumentsObject(func, names, args, env, strict); env.CreateMutableBinding('arguments'); env.SetMutableBinding('arguments', argsObj); } dn = 'b'; //以 dn 为参数，调用 env 的 HasBinding 具体方法，并令 varAlreadyDeclared 为调用的结果 varAlreadyDeclared = env.HasBinding(dn); //如果 varAlreadyDeclared 为 false，则： if (!varAlreadyDeclared) { //以 dn 和 configurableBindings 为参数，调用 env 的 CreateMutableBinding 具体方法。 env.CreateMutableBinding(dn, configurableBindings); //以 dn、undefined 和 strict 为参数，调用 env 的 SetMutableBinding 具体方法。 env.SetMutableBinding(dn, undefined, strict); } +dn = 'e'; +//以 dn 为参数，调用 env 的 HasBinding 具体方法，并令 varAlreadyDeclared 为调用的结果 +varAlreadyDeclared = env.HasBinding(dn); +//如果 varAlreadyDeclared 为 false，则： +if (!varAlreadyDeclared) { + //以 dn 和 configurableBindings 为参数，调用 env 的 CreateMutableBinding 具体方法。 + env.CreateMutableBinding(dn, configurableBindings); + //以 dn、undefined 和 strict 为参数，调用 env 的 SetMutableBinding 具体方法。 + env.SetMutableBinding(dn, undefined, strict); +} + +localEnv = LexicalEnvironment.NewDeclarativeEnvironment(ECStack.current.variableEnvironment); +env = localEnv.environmentRecord; +dn = 'd'; +//以 dn 为参数，调用 env 的 HasBinding 具体方法，并令 varAlreadyDeclared 为调用的结果 +varAlreadyDeclared = env.HasBinding(dn); +//如果 varAlreadyDeclared 为 false，则： +if (!varAlreadyDeclared) { + //以 dn 和 configurableBindings 为参数，调用 env 的 CreateMutableBinding 具体方法。 + env.CreateMutableBinding(dn, configurableBindings); + //以 dn、undefined 和 strict 为参数，调用 env 的 SetMutableBinding 具体方法。 + env.SetMutableBinding(dn, undefined, strict); +} +ECStack.current.lexicalEnvironment = localEnv; +//开始执行one +ECStack.current.variableEnvironment.environmentRecord.SetMutableBinding('b', 2); +console.log( + 'a=' + LexicalEnvironment.GetIdentifierReference(ECStack.current.lexicalEnvironment, 'a').name, + 'b=' + LexicalEnvironment.GetIdentifierReference(ECStack.current.lexicalEnvironment, 'b').name, + 'c=' + LexicalEnvironment.GetIdentifierReference(ECStack.current.lexicalEnvironment, 'c').name, + 'e=' + LexicalEnvironment.GetIdentifierReference(ECStack.current.lexicalEnvironment, 'e').name, + //LexicalEnvironment.GetIdentifierReference(ECStack.current.lexicalEnvironment, 'f') +); +ECStack.current.variableEnvironment.environmentRecord.SetMutableBinding('d', 4); + +localEnv = LexicalEnvironment.NewDeclarativeEnvironment(ECStack.current.variableEnvironment); +ECStack.current.lexicalEnvironment = localEnv; +env = localEnv.environmentRecord; +dn = 'd'; +//以 dn 为参数，调用 env 的 HasBinding 具体方法，并令 varAlreadyDeclared 为调用的结果 +varAlreadyDeclared = env.HasBinding(dn); +//如果 varAlreadyDeclared 为 false，则： +if (!varAlreadyDeclared) { + //以 dn 和 configurableBindings 为参数，调用 env 的 CreateMutableBinding 具体方法。 + env.CreateMutableBinding(dn, configurableBindings); + //以 dn、undefined 和 strict 为参数，调用 env 的 SetMutableBinding 具体方法。 + env.SetMutableBinding(dn, undefined, strict); +} +dn = 'f'; +//以 dn 为参数，调用 env 的 HasBinding 具体方法，并令 varAlreadyDeclared 为调用的结果 +varAlreadyDeclared = env.HasBinding(dn); +//如果 varAlreadyDeclared 为 false，则： +if (!varAlreadyDeclared) { + //以 dn 和 configurableBindings 为参数，调用 env 的 CreateMutableBinding 具体方法。 + env.CreateMutableBinding(dn, configurableBindings); + //以 dn、undefined 和 strict 为参数，调用 env 的 SetMutableBinding 具体方法。 + env.SetMutableBinding(dn, undefined, strict); +} +ECStack.current.lexicalEnvironment.environmentRecord.SetMutableBinding('d', 5); +ECStack.current.variableEnvironment.environmentRecord.SetMutableBinding('e', 6); +ECStack.current.lexicalEnvironment.environmentRecord.SetMutableBinding('f', 7); +console.log( + 'a=' + LexicalEnvironment.GetIdentifierReference(ECStack.current.lexicalEnvironment, 'a').name, + 'b=' + LexicalEnvironment.GetIdentifierReference(ECStack.current.lexicalEnvironment, 'b').name, + 'c=' + LexicalEnvironment.GetIdentifierReference(ECStack.current.lexicalEnvironment, 'c').name, + 'd=' + LexicalEnvironment.GetIdentifierReference(ECStack.current.lexicalEnvironment, 'd').name, + 'e=' + LexicalEnvironment.GetIdentifierReference(ECStack.current.lexicalEnvironment, 'e').name, + 'f=' + LexicalEnvironment.GetIdentifierReference(ECStack.current.lexicalEnvironment, 'f').name +);6. 闭包闭包6.1 closure.jsvar a = 1; function one(c) { var b = 2; function two() { console.log(a,b,c); } return two; } var two = one(3); two();6.2 closure.run.jsclosure.run.js/** var a = 1; function one(c) { var b = 2; return function two() { console.log(a, b, c); } } var two = one(3); two(); */ const LexicalEnvironment = require('./LexicalEnvironment'); const ExecutionContext = require('./ExecutionContext'); const ECStack = require('./ECStack'); const FunctionDeclaration = require('./FunctionDeclaration'); const CreateArgumentsObject = require('./CreateArgumentsObject'); //1.将变量环境设置为全局环境 2.将词法环境设置为 全局环境 const globalLexicalEnvironment = LexicalEnvironment.NewObjectEnvironment(global, null); const globalEC = new ExecutionContext(globalLexicalEnvironment, global); ECStack.push(globalEC); //令 env 为当前运行的执行环境的环境变量的 环境记录 let env = ECStack.current.lexicalEnvironment.environmentRecord; //如果 code 是 eval 代码 ，则令 configurableBindings 为 true，否则令 configurableBindings 为 false。 let configurableBindings = false; //如果代码是 严格模式下的代码 ，则令 strict 为 true，否则令 strict 为 false。 let strict = false; //按源码顺序遍历 code，对于每一个 VariableDeclaration 和 VariableDeclarationNoIn 表达式： //令 dn 为 d 中的标识符。 let dn = 'a'; //以 dn 为参数，调用 env 的 HasBinding 具体方法，并令 varAlreadyDeclared 为调用的结果 let varAlreadyDeclared = env.HasBinding(dn); //如果 varAlreadyDeclared 为 false，则： if (!varAlreadyDeclared) { //以 dn 和 configurableBindings 为参数，调用 env 的 CreateMutableBinding 具体方法。 env.CreateMutableBinding(dn, configurableBindings); //以 dn、undefined 和 strict 为参数，调用 env 的 SetMutableBinding 具体方法。 env.SetMutableBinding(dn, undefined, strict); } //console.log(env.GetBindingValue('a'));//undefined +dn = 'two'; +//以 dn 为参数，调用 env 的 HasBinding 具体方法，并令 varAlreadyDeclared 为调用的结果 +varAlreadyDeclared = env.HasBinding(dn); +//如果 varAlreadyDeclared 为 false，则： +if (!varAlreadyDeclared) { + //以 dn 和 configurableBindings 为参数，调用 env 的 CreateMutableBinding 具体方法。 + env.CreateMutableBinding(dn, configurableBindings); + //以 dn、undefined 和 strict 为参数，调用 env 的 SetMutableBinding 具体方法。 + env.SetMutableBinding(dn, undefined, strict); +} //console.log(env.GetBindingValue(dn));//undefined //按源码顺序遍历 code，对于每一个 FunctionDeclaration 表达式 f： //令 fn 为 FunctionDeclaration 表达式 f 中的 标识符 let fn = 'one'; //创建函数对象 //指定 FormalParameterList 为可选参数列表 let FormalParameterList = ['c']; //指定 FunctionBody 为函数体 let FunctionBody = ` var b = 2; console.log(a, b); `; //指定 Scope 为 词法环境 let Scope = ECStack.current.lexicalEnvironment; //按 第 13 章 中所述的步骤初始化 FunctionDeclaration 表达式 ，并令 fo 为初始化的结果。 let fo = FunctionDeclaration.createInstance(fn, FormalParameterList, FunctionBody, Scope); //以 fn 为参数，调用 env 的 HasBinding 具体方法，并令 argAlreadyDeclared 为调用的结果。 let argAlreadyDeclared = env.HasBinding(fn); if (!argAlreadyDeclared) { env.CreateMutableBinding(fn, configurableBindings); } else { //否则如果 env 是全局环境的 环境记录 对象，则 //令 go 为全局对象。 let go = global; //以 fn 为参数，调用 go 和 GetProperty 内部方法，并令 existingProp 为调用的结果。 let existingProp = Object.getOwnPropertyDescriptor(go, fn); if (existingProp.configurable) { Object.defineProperty(go, fn, { value: undefined, writable: true, enumerable: true, configurable: configurableBindings }); if (!existingProp.writable) { throw new Error(`TypeError`); } } } env.SetMutableBinding(fn, fo, strict); //console.log(env.GetBindingValue('one'));//1 // 给a赋值为1 env.SetMutableBinding('a', 1); //进入函数one代码 //当控制流根据一个函数对象 F、调用者提供的 thisArg 以及调用者提供的 argumentList，进入 函数代码 的执行环境时，执行以下步骤： let thisArg = global; //以 F 的 Scope 内部属性为参数调用 NewDeclarativeEnvironment，并令 localEnv 为调用的结果 let localEnv = LexicalEnvironment.NewDeclarativeEnvironment(fo[`Scope`]); //设词法环境为 localEnv 设变量环境为 localEnv let oneExecutionContext = new ExecutionContext(localEnv, thisArg); ECStack.push(oneExecutionContext); //按 [10.5](#10.5) 描述的方案，使用 函数代码 code 和 argumentList 执行定义绑定初始化步骤 env = ECStack.current.lexicalEnvironment.environmentRecord; configurableBindings = false; strict = false; //令 func 为通过 Call 内部属性初始化 code 的执行的函数对象 let func = fo; //令 code 为 F 的 Code 内部属性的值。 let code = func[`Code`]; //令 names 为 func 的 FormalParameters 内部属性。 names = func[`FormalParameters`]; let args = [3]; //令 argCount 为 args 中元素的数量 let argCount = args.length; let n = 0; names.forEach(argName => { n += 1; let v = n > argCount ? undefined : args[n - 1]; //以 argName 为参数，调用 env 的 HasBinding 具体方法，并令 argAlreadyDeclared 为调用的结果。 argAlreadyDeclared = env.HasBinding(argName); if (!argAlreadyDeclared) { env.CreateMutableBinding(argName); } env.SetMutableBinding(argName, v, strict); }); let argumentsAlreadyDeclared = env.HasBinding('arguments'); if (!argumentsAlreadyDeclared) { let argsObj = CreateArgumentsObject(func, names, args, env, strict); env.CreateMutableBinding('arguments'); env.SetMutableBinding('arguments', argsObj); } dn = 'b'; //以 dn 为参数，调用 env 的 HasBinding 具体方法，并令 varAlreadyDeclared 为调用的结果 varAlreadyDeclared = env.HasBinding(dn); //如果 varAlreadyDeclared 为 false，则： if (!varAlreadyDeclared) { //以 dn 和 configurableBindings 为参数，调用 env 的 CreateMutableBinding 具体方法。 env.CreateMutableBinding(dn, configurableBindings); //以 dn、undefined 和 strict 为参数，调用 env 的 SetMutableBinding 具体方法。 env.SetMutableBinding(dn, undefined, strict); } //开始执行 env.SetMutableBinding(dn, 2); +//按源码顺序遍历 code，对于每一个 FunctionDeclaration 表达式 f： +//令 fn 为 FunctionDeclaration 表达式 f 中的 标识符 +fn = 'two'; +//创建函数对象 +//指定 FormalParameterList 为可选参数列表 +FormalParameterList = []; +//指定 FunctionBody 为函数体 +FunctionBody = ` + console.log(a, b, c); +`; +//指定 Scope 为 词法环境 +Scope = ECStack.current.lexicalEnvironment; +//按 第 13 章 中所述的步骤初始化 FunctionDeclaration 表达式 ，并令 fo 为初始化的结果。 +fo = FunctionDeclaration.createInstance(fn, FormalParameterList, FunctionBody, Scope); +//退出one +ECStack.pop(); +env = ECStack.current.lexicalEnvironment.environmentRecord; +//以 fn 为参数，调用 env 的 HasBinding 具体方法，并令 argAlreadyDeclared 为调用的结果。 +argAlreadyDeclared = env.HasBinding(fn); +if (!argAlreadyDeclared) { + env.CreateMutableBinding(fn, configurableBindings); +} else { + //否则如果 env 是全局环境的 环境记录 对象，则 + //令 go 为全局对象。 + let go = global; + //以 fn 为参数，调用 go 和 GetProperty 内部方法，并令 existingProp 为调用的结果。 + let existingProp = Object.getOwnPropertyDescriptor(go, fn); + if (existingProp.configurable) { + Object.defineProperty(go, fn, { value: undefined, writable: true, enumerable: true, configurable: +configurableBindings }); + if (!existingProp.writable) { + throw new Error(`TypeError`); + } + } +} +env.SetMutableBinding(fn, fo, strict); +//执行two +//当控制流根据一个函数对象 F、调用者提供的 thisArg 以及调用者提供的 argumentList，进入 函数代码 的执行环境时，执行以下步+骤： +thisArg = global; +//以 F 的 Scope 内部属性为参数调用 NewDeclarativeEnvironment，并令 localEnv 为调用的结果 +localEnv = LexicalEnvironment.NewDeclarativeEnvironment(fo[`Scope`]); +//设词法环境为 localEnv 设变量环境为 localEnv +oneExecutionContext = new ExecutionContext(localEnv, thisArg); +ECStack.push(oneExecutionContext); +//按 [10.5](#10.5) 描述的方案，使用 函数代码 code 和 argumentList 执行定义绑定初始化步骤 +env = ECStack.current.lexicalEnvironment.environmentRecord; +configurableBindings = false; +strict = false; +//令 func 为通过 Call 内部属性初始化 code 的执行的函数对象 +func = fo; +//令 code 为 F 的 Code 内部属性的值。 +code = func[`Code`]; +//令 names 为 func 的 FormalParameters 内部属性。 +names = func[`FormalParameters`]; +args = [3]; +//令 argCount 为 args 中元素的数量 +argCount = args.length; +n = 0; +names.forEach(argName => { + n += 1; + let v = n > argCount ? undefined : args[n - 1]; + //以 argName 为参数，调用 env 的 HasBinding 具体方法，并令 argAlreadyDeclared 为调用的结果。 + argAlreadyDeclared = env.HasBinding(argName); + if (!argAlreadyDeclared) { + env.CreateMutableBinding(argName); + } + env.SetMutableBinding(argName, v, strict); +}); +argumentsAlreadyDeclared = env.HasBinding('arguments'); +if (!argumentsAlreadyDeclared) { + let argsObj = CreateArgumentsObject(func, names, args, env, strict); + env.CreateMutableBinding('arguments'); + env.SetMutableBinding('arguments', argsObj); +} +console.log( + 'a=' + LexicalEnvironment.GetIdentifierReference(ECStack.current.lexicalEnvironment, 'a').name, + 'b=' + LexicalEnvironment.GetIdentifierReference(ECStack.current.lexicalEnvironment, 'b').name, + 'c=' + LexicalEnvironment.GetIdentifierReference(ECStack.current.lexicalEnvironment, 'c').name +);10. 可执行代码与执行环境10.1 可执行代码类型10.1.1 全局代码全局代码 是指被作为 ECMA 脚本 程序 处理的源代码文本console.log('global');10.1.2 Eval 代码Eval 代码 是指提供给 eval 内置函数的源代码文本eval('console.log("hello")');10.1.3 函数代码函数代码 是指作为 函数体 被解析的源代码文本函数代码 同时还特指 以构造器方式调用 Function 内置对象 时所提供的源代码文本 function one() { console.log('one'); } one(); const sum = new Function("a,b", "return a+b"); console.log(sum(1, 2));10.2 词法环境词法环境 是一个用于定义特定变量和函数标识符在 ECMAScript 代码的词法嵌套结构上关联关系的规范类型一个词法环境由一个环境记录项和可能为空的外部词法环境引用构成通常词法环境会与特定的 ECMAScript 代码诸如 FunctionDeclaration,WithStatement 或者 TryStatement 的 Catch 块这样的语法结构相联系，且类似代码每次执行都会有一个新的语法环境被创建出来环境记录项记录了在它的关联词法环境域内创建的标识符绑定情形外部词法环境引用用于表示词法环境的逻辑嵌套关系模型（内部）词法环境的外部引用是逻辑上包含内部词法环境的词法环境外部词法环境自然也可能有多个内部词法环境词法环境和环境记录项是纯粹的规范机制，而不需要 ECMAScript 的实现保持一致。ECMAScript 程序不可能直接访问或者更改这些值let a = 1; function one() { let a = 2; console.log(a); try { let a = 3; console.log(a); throw new Error('wrong'); } catch (error) { let a = 4; console.log(a); with ({}) { let a = 5; console.log(a); } } } one();10.2.1 环境记录项在本标准中，共有 2 类环境记录项： 声明式环境记录项 和 对象式环境记录项声明式环境记录项用于定义那些将 标识符 与语言值直接绑定的 ECMA 脚本语法元素，例如 函数定义 ， 变量定义 以及 Catch 语句对象式环境记录项用于定义那些将 标识符 与具体对象的属性绑定的 ECMA 脚本元素，例如 程序 以及 With 表达式出于标准规范的目的，可以将环境记录项理解为面向对象中的一个简单继承结构，其中环境记录项是一个抽象类,它有 2 个具体实现类，分别为声明式环境记录项和对象式环境记录项10.2.1.1 声明式环境记录项每个声明式环境记录项都与一个包含变量和（或）函数声明的 ECMA 脚本的程序作用域相关联声明式环境记录项用于绑定作用域内定义的一系列标识符除了所有环境记录项都支持的可变绑定外，声明式环境记录项还提供不可变绑定在不可变绑定中，一个标识符与它的值之间的关联关系建立之后，就无法改变创建和初始化不可变绑定是两个独立的过程，因此类似的绑定可以处在已初始化阶段或者未初始化阶段10.2.1.2 对象式环境记录项每一个对象式环境记录项都有一个关联的对象，这个对象被称作 绑定对象对象式环境记录项直接将一系列标识符与其绑定对象的属性名称建立一一对应关系不符合IdentifierName 的属性名不会作为绑定的标识符使用无论是对象自身的，还是继承的属性都会作为绑定,无论该属性的 Enumerable 特性的值是什么由于对象的属性可以动态的增减，因此对象式环境记录项所绑定的标识符集合也会隐匿地变化，这是增减绑定对象的属性而产生的副作用通过以上描述的副作用而建立的绑定，均被视为可变绑定，即使该绑定对应的属性的 Writable 特性的值为 false对象式环境记录项没有不可变绑定10.2.2 词法环境的运算GetIdentifierReference (lex, name, strict)NewDeclarativeEnvironment (E)NewObjectEnvironment (O, E)10.2.3 全局环境全局环境 是一个唯一的词法环境 ，它在任何 ECMA 脚本的代码执行前创建全局环境的 环境数据 是一个object-environment-record对象环境数据该环境数据使用 全局对象作为 绑定对象全局环境的 外部环境引用 为 null在 ECMA 脚本的代码执行过程中，可能会向 全局对象 添加额外的属性，也可能修改其初始属性的值10.2.3.1 全局对象唯一的全局对象建立在控制进入任何执行环境之前全局对象的标准内置属性拥有特性 {Writable: true, Enumerable: false, Configurable: true}全局对象没有 Construct 内部属性 ; 全局对象不可能当做构造器用 new 运算符调用全局对象没有 Call 内部属性，全局对象不可能当做函数来调用全局对象的 Prototype 和 Class 内部属性值是依赖于实现的除了本规范定义的属性之外，全局对象还可以拥有额外的宿主定义的属性 全局对象可包含一个值是全局对象自身的属性；例如，在 HTML 文档对象模型中全局对象的 window 属性是全局对象自身10.3 执行环境当控制器转入 ECMA 脚本的可执行代码时，控制器会进入一个执行环境当前活动的多个执行环境在逻辑上形成一个栈结构该逻辑栈的最顶层的执行环境称为当前运行的执行环境任何时候，当控制器从当前运行的执行环境相关的可执行代码转入与该执行环境无关的可执行代码时，会创建一个新的执行环境新建的这个执行环境会推入栈中，成为当前运行的执行环境执行环境包含所有用于追踪与其相关的代码的执行进度的状态组件作用目的词法环境指定一个词法环境对象，用于解析该执行环境内的代码创建的标识符引用变量环境指定一个词法环境对象，其环境数据用于保存由该执行环境内的代码通过变量表达式和函数表达式创建的绑定This绑定指定该执行环境内的 ECMA 脚本代码中 this 关键字所关联的值执行环境的词法环境和变量环境组件始终为 词法环境 对象当创建一个执行环境时，其词法环境组件和变量环境组件最初是同一个值在该执行环境相关联的代码的执行过程中，变量环境组件永远不变，而词法环境组件有可能改变在本标准中，通常情况下，只有正在运行的执行环境（执行环境栈里的最顶层对象）会被算法直接修改因此当遇到词法环境，变量环境和This 绑定这三个术语时，指的是正在运行的执行环境的对应组件执行环境是一个纯粹的标准机制，并不代表任何ECMA脚本实现的工件。在ECMA脚本程序中是不可能访问到执行环境的10.3.1 标识符解析标识符解析是指使用正在运行的执行环境中的词法环境,通过一个标识符获得其对应的绑定的过程解释执行一个标识符得到的结果必定是 引用 类型的对象，且其引用名属性的值与Identifier字符串相等10.4 建立执行环境解释执行全局代码或使用eval函数输入的代码会创建并进入一个新的执行环境每次调用 ECMA 脚本代码定义的函数也会建立并进入一个新的执行环境，即便函数是自身递归调用的每一次 return 都会退出一个执行环境抛出异常也可退出一个或多个执行环境当控制流进入一个执行环境时，会设置该执行环境的this绑定，定义变量环境和初始词法环境，并执行定义绑定初始化过程10.4.1 进入全局代码按 10.4.1.1 描述的方案，使用 全局代码 初始化执行环境按 10.5 描述的方案，使用 全局代码 执行定义绑定初始化步骤。10.4.1.1 初始化全局执行环境将变量环境设置为 全局环境将词法环境设置为 全局环境将 this 绑定设置为 全局对象10.5 定义绑定初始化每个执行环境都有一个关联的变量环境当在一个执行环境下执行一段ECMA脚本时，变量和函数定义会以绑定的形式添加到这个变量环境的环境记录中对于函数函数代码，参数也同样会以绑定的形式添加到这个变量环境的环境记录中选择使用哪一个、哪一类型的 环境记录 来绑定定义，是由执行环境下执行的 ECMA 脚本的类型决定的
1.call的实现Function.prototype.call = function(context,...args){ let cxt = context || window; let func = Symbol() cxt[func] = this; args = args ? args : [] const res = args.length > 0 ? cxt[func](...args) : cxt[func](); delete cxt[func]; return res; }2. apply的实现Function.prototype.apply = function(context,args = []){ let cxt = context || window; let func = Symbol() cxt[func] = this; const res = args.length > 0 ? cxt[func](...args) : cxt[func](); delete cxt[func]; return res; }3. bind的实现Function.prototype.bind = function (context, ...args) { const fn = this args = args ? args : [] return function newFn(...newFnArgs) { if (this instanceof newFn) { return new fn(...args, ...newFnArgs) } return fn.apply(context, [...args,...newFnArgs]) } }4. 寄生式组合继承function Person(obj) { this.name = obj.name this.age = obj.age } Person.prototype.add = function(value){ console.log(value) } var p1 = new Person({name:"zhangsan", age: 18}) function Person1(obj) { Person.call(this, obj) this.sex = obj.sex } Person1.prototype = Object.create(Person.prototype); Person1.prototype.constructor = Person1; Person1.prototype.play = function(value){ console.log(value) } var p2 = new Person1({name:"lisi", age: 18, sex: "男"}) 5. ES6继承class Person{ constructor(name='zhangsan',age=18){ this.name = name; this.age = age; } desc(){ console.log(`${this.name} ${this.age}`) } } class Man extends Person{ constructor(name = 'lisi',age = 18){ super(name, age); } desc(){ super.eat() } }6. new的实现function new(ctor,...args){ if(typeof ctor !== 'function'){ throw 'the first param must be a function'; } var newObj = Object.create(ctor.prototype); var ctorReturnResult = ctor.apply(newObj, args); var isObject = typeof ctorReturnResult === 'object' && ctorReturnResult !== null; var isFunction = typeof ctorReturnResult === 'function'; if(isObject || isFunction){ return ctorReturnResult; } return newObj; } let c = new(Ctor);7.instanceof的实现function myInstanceOf(a,b){ let left = a.__proto__; let right = b.prototype; while(true){ if(left == null){ return false } if(left == right){ return true } left = left.__proto__ } } function myInstanceof(left, right) { let proto = Object.getPrototypeOf(left), prototype = right.prototype; while (true) { if (!proto) return false; if (proto === prototype) return true; proto = Object.getPrototypeOf(proto); } }8.Object.create()的实现 function myCreate(obj){ function C(){}; C.prototype = obj; return new C() } if (typeof Object.create !== "function") { Object.create = function (proto, propertiesObject) { if (typeof proto !== 'object' && typeof proto !== 'function') { throw new TypeError('Object prototype may only be an Object: ' + proto); } else if (proto === null) { throw new Error("This browser's implementation of Object.create is a shim and doesn't support 'null' as the first argument."); } if (typeof propertiesObject !== 'undefined') throw new Error("This browser's implementation of Object.create is a shim and doesn't support a second argument."); function F() {} F.prototype = proto; return new F(); }; }9.实现 Object.assignObject.assign2 = function(target, ...source) { if (target == null) { throw new TypeError('Cannot convert undefined or null to object') } let ret = Object(target) source.forEach(function(obj) { if (obj != null) { for (let key in obj) { if (obj.hasOwnProperty(key)) { ret[key] = obj[key] } } } }) return ret }10.Ajax的实现function ajax(url,method,body,headers){ return new Promise((resolve,reject)=>{ let req = new XMLHttpRequest(); req.open(methods,url); for(let key in headers){ req.setRequestHeader(key,headers[key]) } req.onreadystatechange(()=>{ if(req.readystate == 4){ if(req.status >= '200' && req.status <= 300){ resolve(req.responseText) }else{ reject(req) } } }) req.send(body) }) }11.实现防抖函数（debounce）let debounce = (fn,time = 1000) => { let timeLock = null return function (...args){ clearTimeout(timeLock) timeLock = setTimeout(()=>{ fn(...args) },time) } }12.实现节流函数（throttle）let throttle = (fn,time = 1000) => { let flag = true; return function (...args){ if(flag){ flag = false; setTimeout(()=>{ flag = true; fn(...args) },time) } } }13. 深拷贝function deepClone(obj,hash = new WeakMap()){ if(obj instanceof RegExp) return new RegExp(obj); if(obj instanceof Date) return new Date(obj); if(obj === null || typeof obj !== 'object') return obj; if(hash.has(obj)){ return hash.get(obj) } let constr = new obj.constructor(); hash.set(obj,constr); for(let key in obj){ if(obj.hasOwnProperty(key)){ constr[key] = deepClone(obj[key],hash) } } let symbolObj = Object.getOwnPropertySymbols(obj) for(let i=0;i<symbolObj.length;i++){ if(obj.hasOwnProperty(symbolObj[i])){ constr[symbolObj[i]] = deepClone(obj[symbolObj[i]],hash) } } return constr }14. 数组扁平化的实现(flat)let arr = [1,2,[3,4,[5,[6]]]] console.log(arr.flat(Infinity)) function fn(arr){ return arr.reduce((prev,cur)=>{ return prev.concat(Array.isArray(cur)?fn(cur):cur) },[]) }15. 函数柯里化function sumFn(a,b,c){return a+ b + c}; let sum = curry(sumFn); sum(2)(3)(5) sum(2,3)(5)function curry(fn,...args){ let fnLen = fn.length, argsLen = args.length; if(fnLen > argsLen){ return function(...arg2s){ return curry(fn,...args,...arg2s) } }else{ return fn(...args) } }16.使用闭包实现每隔一秒打印 1,2,3,4for (var i=1; i<=5; i++) { (function (i) { setTimeout(() => console.log(i), 1000*i) })(i) }17. 手写一个 jsonpconst jsonp = function (url, data) { return new Promise((resolve, reject) => { let dataString = url.indexOf('?') === -1 ? '?' : '' let callbackName = `jsonpCB_${Date.now()}` url += `${dataString}callback=${callbackName}` if (data) { for (let k in data) { url += `${k}=${data[k]}` } } let jsNode = document.createElement('script') jsNode.src = url window[callbackName] = result => { delete window[callbackName] document.body.removeChild(jsNode) if (result) { resolve(result) } else { reject('没有返回数据') } } // js加载异常的情况 jsNode.addEventListener('error', () => { delete window[callbackName] document.body.removeChild(jsNode) reject('JavaScript资源加载失败') }, false) // 添加js节点到document上时，开始请求 document.body.appendChild(jsNode) }) } jsonp('http://127.0.0.1/jsonp', { a: 1, b: 'hello' }) .then(result => { console.log(result) }) .catch(err => { console.error(err) })18.手写一个观察者模式class Subject{ constructor(name){ this.name = name this.observers = [] this.state = 'off' } attach(observer){ this.observers.push(observer) } setState(newState){ this.state = newState this.observers.forEach(o=>{ o.update(newState) }) } } class Observer{ constructor(name){ this.name = name } update(newState){ console.log(`${this.name}say:${newState}`) } } let sub = new Subject('') let zhangsan = new Observer('zhangsan') let lisi = new Observer('lisi') sub.attach(zhangsan) sub.attach(lisi) sub.setState('on')19. EventEmitter 实现EventEmitter 实现 class EventEmitter { constructor() { this.events = {}; } on(event, callback) { let callbacks = this.events[event] || []; callbacks.push(callback); this.events[event] = callbacks; return this; } off(event, callback) { let callbacks = this.events[event]; this.events[event] = callbacks && callbacks.filter(fn => fn !== callback); return this; } emit(event, ...args) { let callbacks = this.events[event]; callbacks.forEach(fn => { fn(...args); }); return this; } once(event, callback) { let wrapFun = function (...args) { callback(...args); this.off(event, wrapFun); }; this.on(event, wrapFun); return this; } } 20. 生成随机数的各种方法？function getRandom(min, max) { return Math.floor(Math.random() * (max - min)) + min }21. 如何实现数组的随机排序？let arr = [1,2,3,4,5] arr.sort(randomSort) function randomSort(a, b) { return Math.random() > 0.5 ? -1 : 1; }22. 写一个通用的事件侦听器函数const EventUtils = { addEvent: function(element, type, handler) { if (element.addEventListener) { element.addEventListener(type, handler, false); } else if (element.attachEvent) { element.attachEvent("on" + type, handler); } else { element["on" + type] = handler; } }, removeEvent: function(element, type, handler) { if (element.removeEventListener) { element.removeEventListener(type, handler, false); } else if (element.detachEvent) { element.detachEvent("on" + type, handler); } else { element["on" + type] = null; } }, getTarget: function(event) { return event.target || event.srcElement; }, getEvent: function(event) { return event || window.event; }, stopPropagation: function(event) { if (event.stopPropagation) { event.stopPropagation(); } else { event.cancelBubble = true; } }, preventDefault: function(event) { if (event.preventDefault) { event.preventDefault(); } else { event.returnValue = false; } } };23. 使用迭代的方式实现 flatten 函数var arr = [1, 2, 3, [4, 5], [6, [7, [8]]]] function wrap() { var ret = []; return function flat(a) { for (var item of a) { if (item.constructor === Array) { ret.concat(flat(item)) } else { ret.push(item) } } return ret } } console.log(wrap()(arr));24. 怎么实现一个sleepfunction sleep(delay) { var start = (new Date()).getTime(); while ((new Date()).getTime() - start < delay) { continue; } } function test() { console.log('111'); sleep(2000); console.log('222'); } test()25. 实现正则切分千分位（10000 => 10,000）let num1 = '1321434322222' num1.replace(/(\d)(?=(\d{3})+$)/g,'$1,') let num2 = '342243242322.3432423' num2.replace(/(\d)(?=(\d{3})+\.)/g,'$1,')26. 对象数组去重输入: [{a:1,b:2,c:3},{b:2,c:3,a:1},{d:2,c:2}] 输出: [{a:1,b:2,c:3},{d:2,c:2}]function objSort(obj){ let newObj = {} Object.keys(obj).sort().map(key => { newObj[key] = obj[key] }) return JSON.stringify(newObj) } function unique(arr){ let set = new Set(); for(let i=0;i<arr.length;i++){ let str = objSort(arr[i]) set.add(str) } arr = [...set].map(item => { return JSON.parse(item) }) return arr }27.解析 URL Params 为对象let url = 'http://www.domain.com/?user=zhangsan&id=100&id=200&city=beijing&enabled'; parseParam(url)function parseParam(url) { const paramsStr = /.+\?(.+)$/.exec(url)[1]; const paramsArr = paramsStr.split('&'); let paramsObj = {}; paramsArr.forEach(param => { if (/=/.test(param)) { let [key, val] = param.split('='); val = decodeURIComponent(val); val = /^\d+$/.test(val) ? parseFloat(val) : val; if (paramsObj.hasOwnProperty(key)) { paramsObj[key] = [].concat(paramsObj[key], val); } else { paramsObj[key] = val; } } else { paramsObj[param] = true; } }) return paramsObj; }28.模板引擎实现let template = '我是{{name}}，年龄{{age}}，性别{{sex}}'; let data = { name: '姓名', age: 18 } render(template, data);function render(template, data) { const reg = /\{\{(\w+)\}\}/; if (reg.test(template)) { const name = reg.exec(template)[1]; template = template.replace(reg, data[name]); return render(template, data); } return template; }29.转化为驼峰命名var s1 = "get-element-by-id" // 转化为 getElementByIdvar f = function(s) { return s.replace(/-\w/g, function(x) { return x.slice(1).toUpperCase(); }) }30.查找字符串中出现最多的字符和个数例: abbcccddddd -> 字符最多的是d，出现了5次 `js let str = "abcabcabcbbccccc"; let num = 0; let char = '';str = str.split('').sort().join('');let re = /(w)1+/g; str.replace(re,($0,$1) => { if(num < $0.length){ num = $0.length; char = $1;} }); console.log(字符最多的是${char}，出现了${num}次); ## 31.图片懒加载let imgList = [...document.querySelectorAll('img')]let length = imgList.lengthconst imgLazyLoad = function() {let count = 0 return (function() { let deleteIndexList = [] imgList.forEach((img, index) => { let rect = img.getBoundingClientRect() if (rect.top < window.innerHeight) { img.src = img.dataset.src deleteIndexList.push(index) count++ if (count === length) { document.removeEventListener('scroll', imgLazyLoad) } } }) imgList = imgList.filter((img, index) => !deleteIndexList.includes(index)) })()}document.addEventListener('scroll', imgLazyLoad)