多平台统一管理软件接口,如何实现多平台统一管理软件接口
394
2022-11-01
图解 Google V8 # 05:函数表达式的底层工作机制
说明
图解 Google V8 学习笔记
我们先来看两段代码的对比:
代码片段1:
foo()function foo(){ console.log('foo')}
执行代码,发现没有报错,并且成功打印了 foo。
代码片段2:
foo()var foo = function (){ console.log('foo')}
同样我们执行代码,发现代码执行报错了:Uncaught TypeError: foo is not a function
都是在定义的函数之前调用函数,为什么代码片段1执行成功,而代码片段2执行报错?
原因:这两种定义函数的方式具有不同语义,不同的语义触发了不同的行为。
第二种称之为函数表达式
函数表达式是在表达式语句中使用 function 的,最典型的表达式是a=b 这种形式,因为函数也是一个对象,我们把a = function (){} 这种方式称为函数表达式;在函数表达式中,可以省略函数名称,从而创建匿名函数(anonymous functions)一个函数表达式可以被用作一个立即调用的函数表达式(IIFE)(Immediately Invoked Function Expression)
先看一个例子:
var x = 5function foo(){ console.log('Foo')}
V8 执行这段代码的流程大致如下:
在编译阶段:
在执行阶段:
如果使用了某个变量,或者调用了某个函数,那么 V8 便会去作用域查找相关内容。
查看作用域的数据
没有d8环境的可以参考我这篇文章window 系统里怎么使用 jsvu 工具快速调试 v8?
我们在 kaimo.js 里添加上面的代码
然后执行命令
v8-debug --print-scopes kaimo.js
执行之后我们就能看到作用域的数据
Inner function scope:function foo () { // (00000254208756A0) (23, 53) // NormalFunction // 2 heap slots}Global scope:global { // (00000254208753F0) (0, 53) // will be compiled // NormalFunction // 1 stack slots // temporary vars: TEMPORARY .result; // (0000025420875900) local[0] // local vars: VAR foo; // (0000025420875870) VAR x; // (0000025420875610) function foo () { // (00000254208756A0) (23, 53) // lazily parsed // NormalFunction // 2 heap slots }}
我们可以看到,作用域中包含了变量 x 和 foo,变量 x 的默认值是 undefined,变量 foo 指向了 foo 函数对象,foo 函数对象被 V8 存放在内存中的堆空间了,这些变量都是在编译阶段被装进作用域中的。
在编译阶段,将所有的变量提升到作用域的过程称为变量提升。
表达式和语句
这是为什么呢?这跟表达式和语句有关。
简单的说:表达式就是表示值的式子,而语句是操作值的式子。
举个例子:
x = 11 === 2
上面这些就是表达式,因为执行这段代码,它会返回一个值。
而定义了一个变量,比如:
var x
就是一个语句,执行该语句时,V8 并不会返回任何值给你。
function foo(){ return 1}
执行这段代码时,V8 并没有返回任何的值,它只是解析 foo 函数,并将函数对象存储到内存中。
var x = 313
V8 执行这段代码时,会认为它是两段代码:
var x = undefined:定义变量的语句,在编译阶段完成。V8 将这些变量存放在作用域时,还会给它们赋一个默认的 undefined 值。x = 313:赋值的表达式,在执行阶段完成的,不会在编译阶段执行的。
V8 是怎么处理函数表达式的?
在一个表达式中使用 function 来定义一个函数,那么就把该函数称为函数表达式。
foo = function (){ console.log('foo')}
函数表达式
例子:
var foo = function (){ console.log('foo')}
可以把这段代码拆分为下面两行代码:
var foo = undefined
foo = function (){ console.log('foo')}
这一行是函数表达式,在编译阶段,V8 并不会处理函数表达式,也就不会将该函数表达式提升到作用域中。
立即调用的函数表达式(IIFE)
(function () { })
因为小括号之间存放的必须是表达式,如果在小阔号里面定义一个函数,那么 V8 就会把这个函数看成是函数表达式,执行时它会返回一个函数对象。如果直接在表达式后面加上调用的括号,这就称为立即调用函数表达式(IIFE)。
比如下面这个就是 IIFE
(function () { })()
函数立即表达式也是一个表达式,所以 V8 在编译阶段,并不会为该表达式创建函数对象。这样的一个好处就是不会污染环境,函数和函数内部的变量都不会被其他部分的代码访问到。
因为函数立即表达式是立即执行的,所以将一个函数立即表达式赋给一个变量时,不是存储 IIFE 本身,而是存储 IIFE 执行后返回的结果。
比如:
var kaimo = (function () { return 777})()
经典面试题
通过上面的学习,这两个题目应该得心应手。【手动狗头】
var n = 1;(function foo(){ n = 100; console.log(n);}())console.log(n);
var n = 1;function foo(){ n = 100; console.log(n);}console.log(n);foo()
参考资料
window 系统里怎么使用 jsvu 工具快速调试 v8?
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~