声明
一、声明式
V8 编译执行声明式函数的过程 在编译阶段,如果解析到了某个函数声明,那么将其函数名放到作用域中,将其值设为函数的引用地址。在执行阶段,如果调用了某个函数,就可以直接去作用域中去查找。
function foo(){
console.log('foo');
}
foo();
二、赋值表达式
V8 编译执行赋值表达式函数的过程 如果解析到了某个变量声明,也会将其放到作用域中,但是会将其值设置为 undefined,表示该变量还未被使用**。在执行阶段,如果使用了某个变量,就可以直接去作用域中去查找。
var foo = function(){
console.log('foo');
}
foo();
三、立即表达式
V8 编译执行立即表达式函数的过程 在编译阶段,V8并不会处理表达式。函数立即表达式也是一个表达式,所以V8并不会为该表达式创建函数对象。这样的一个好处就是不会污染环境,函数和函数内部的变量都不会被其他部分的代码访问到。
(function (){console.log('哈哈');})()
四、构造函数式
new Function()
创建了一个新的 Function
对象。直接调用构造函数可以动态创建函数,但可能会经受一些安全和类似于 eval()
(但远不重要)的性能问题。然而,不像 eval
(可能访问到本地作用域),Function
构造函数只创建全局执行的函数。
4.1 语法
new Function(functionBody)
new Function(arg0, functionBody)
new Function(arg0, arg1, functionBody)
new Function(arg0, arg1, /* … ,*/ argN, functionBody)
4.2 用法
4.3 场景
场景一、动态生成 render
函数
const functionArgu0 = `x`;
const functionArgu1 = `y`
const functionBody = `
return function render(z){
const result = x + y + z;
return "_x + _y + z 的值为:" + result;
}
`
const renderFn = new Function(functionArgu0,functionArgu1,functionBody);
const render = renderFn(1,2);
console.log(render(3));
场景二、动态生成 createElement
函数
import Babel from '@babel/core';
import React from '../../react/index.js';
const template = `<div className="home-page"> Home Page </div>`;
const result = Babel.transform(template, {
plugins: [['@babel/plugin-transform-react-jsx', { runtime: 'classic' }]]
});
const createElementStr = result.code;
const createElement = new Function('React', `return ${createElementStr}`);
4.4 作用域
new Function
无论在哪块调用 new Function()
, 创建的函数始终被创建到了全局环境。 因此, 通过new Function()
创建的函数在运行时它们只能访问全局作用域中的变量和函数 和 自己的局部变量, 不能访问它们被 new Function()
构造函数创建时所在的作用域的变量。
new Function()
VS eval()
: 它们都可以动态解析和执行字符串,但是它们对解析内容的运行环境判定不同。
-
eval()
:eval
中的代码执行时的作用域为当前作用域, 如果找不到, 在从作用链中的上一层寻找。eval
是一个危险的函数, 它使用与调用者相同的权限执行代码。如果你用eval()
运行的字符串代码被恶意方(不怀好意的人)修改,您最终可能会在您的网页/扩展程序的权限下,在用户计算机上运行恶意代码。更重要的是,第三方代码可以看到某一个eval()
被调用时的作用域,这也有可能导致一些不同方式的攻击。eval()
通常比其他替代方法更慢,因为它必须调用JS
解释器,而许多其他结构则可被现代JS
引擎进行优化。现代JavaScript
解释器将JavaScript
转换为机器代码。这意味着任何变量命名的概念都会被删除。因此,任意一个eval
的使用都会强制浏览器进行冗长的变量名称查找,以确定变量在机器代码中的位置并设置其值。另外,新内容将会通过eval()
引进给变量,比如更改该变量的类型,因此会强制浏览器重新执行所有已经生成的机器代码以进行补偿。var x = 'gloabl scope';
function foo() {
var x = 'local scope';
eval('console.log(x)'); // local scope
}
foo(); -
new Function()
:new Function()
中的代码执行时的作用域为全局作用域。无论在哪个地方调用, 它访问的是全局变量。new Function()
要比eval()
函数的执行速度快得多,new Function()
不会强制浏览器重新执行所有已经生成的机器代码进行补偿, 也不会强制浏览器进行冗长的变量名查找。var x = 'gloabl scope';
function foo() {
var x = 'local scope';
new Function('console.log(x)')(); // global scope
}
foo();注意: 虽然这段代码可以在浏览器中正常运行,但在
Node.js
中foo()
会产生一个“找不到变量x
的ReferenceError
。这是因为在Node
中顶级作用域不是全局作用域,而x
其实是在当前模块的作用域之中。