如何绕过面试题中的小坑

来源:互联网 发布:哨子办公软件 编辑:程序博客网 时间:2024/06/08 22:50

常见的面试题分析

  • 直接来几道面试题 看看你能否答对

var foo=1;function bar( ) {foo=10;return function foo() { };}bar( );alert(foo);**********分割线***********var foo = 1;function bar() {foo = 10;return;function foo() {}}bar();console.log(foo)**********分割线***********var a = 1;function fn() {console.log(a);var a = 2;}fn()console.log(a);**********分割线***********var a = 1;function fn(a) {console.log(a);a = 2;}fn(a)console.log(a);

如果你能够都答对可以忽略本文的阅读.免得浪费时间

  • 接下来我要带大家分析下为什么是这样的结果
    分析之前你需要明白的是
    创建应用程序的时候,总免不了要声明变量和函数 解析器(interpreter)是如何以及从哪里找到这些数据(变量,函数)的,
    当我们引用一个变量时,在解析器内部又发生了什么?


我们知道 变量和执行上下文相关 那么它就应该知道数据储存在哪里以及如何访问这些数据,这种机制被称为变量对象(variable object)
变量对象(简称为 VO)是与某个执行上下文相关的一个特殊对象,并储存了一下数据

1. 变量(var, VariableDeclaration)
2. 函数声明(FunctionDeclaration, 缩写为FD)
3. 函数形参
也就是所有的执行中的变量都会存储在 这个vo

  • 处理上下文代码的几个阶段 本文最核心的部分了 处理执行上下文代码分为两个阶段:

  • 进入执行上下文

  • 当进入执行上下文时(在代码执行前),VO 就会被下列属性填充


  1. 函数的所有形参(如果是在函数执行上下文中)
    每个形参都对应变量对象中的一个属性,该属性由形参名和对应的实参值构成,如果没有传递实参,那么该属性值就为 undefined


  2. 所有函数声明(FunctionDeclaration, FD)
    每个函数声明都对应变量对象中的一个属性,这个属性由一个函数对象的名称和值构成,如果变量对象中存在相同的属性名,则完全替换该属性。


  3. 所有变量声明(var, VariableDeclaration)
    每个变量声明都对应变量对象中的一个属性,该属性的键/值是变量名和 undefined,如果变量名与已经声明的形参或函数相同,则变量声明不会干扰已经存在的这类属性。

举例说明:function test(a, b) {var c = 10;function d() {}var e = function _e() {};(function x() {});}test(10);分析: 当进入 test 的执行上下文,并传递了实参 10,(VO)AO(VO 函数上下文中的变量对象) 对象如下:在函数上下文中,变量对象(VO)不能直接被访问到,此时活动对象(Activation Object,简称 AO)扮演着 VO 的角色。就是 VO(functionContext) === AO;在这个时候 AO对象的值是AO(test) = {a: 10,b: undefined,c: undefined,d:e: undefined};注意:AO 并不包含函数 x,这是因为 x 不是函数声明,而是一个函数表达式(FunctionExpression,简称为 FE),函数表达式不会影响 VO。同理,函数 _e 也是函数表达式,就像我们即将看到的那样,因为它分配给了变量 e,所以可以通过名称 e 来访问。函数声明与函数表达式的异同.(有时间我在进行分析)接下来就到了第二个阶段 代码执行阶段
  • 执行代码

  • 继续以上一例子,到了执行代码阶段,AO/VO 就会修改为如下形式

AO['c'] = 10;AO['e'] = ;这里需要注意的是函数表达式 _e 仍在内存中,它被保存在声明的变量 e 中。但函数表达式 x 却不在 AO/VO 中,如果尝试在其定义前或者定义后调用 x 函数,这时会发生“x未定义”的错误
  • 如果你看懂了我上面的分析 接下来我带大家分析几个例子

console.log(a);var a = 1;console.log(a);function a(){alert(2)}console.log(a);var a = 3;console.log(a);function a() {alert(4);}console.log(a);分析流程如下:1. 进入执行上下文(预解析)给vo对象定义变量1. 第二行有var定义的变量a,将其保存为a=undefined;2.4行有function声明和第二行的a同名此时由于函数的等级高于变量,于是就覆盖变量a,此时a= function a (){ alert(2); }3. 第六行又发现一个var定义的变量,名称与第四行的函数相同,但等级低,故a= function a (){ alert(2); }不变。4. 同理,由于第八行后定义,又为同一等级的函数,所以a= function a (){ alert(4); }5. 浏览器解析完成此时的vo= {a:}2. 开始执行1. 第一行 a应该是打印function a() {alert(4);}2. 第二行表达式修改了a的值,使其为1,所以第三行输出a=13. 第四行定义了一个函数,但没有执行,所以第五行输出还为14. 第六行表达式又一次更改了a的值,现在a=3,此时的a为一个数字,第七行输出35. 同理,第八行没有做更改,第九行还是输出3**是不是觉得特别简单 **
  • 转过头来我来和大家一起分析前面的几道面试题

var a = 1;function fn() {console.log(a);var a = 2;}fn()console.log(a);* 解析1. 第一行 var定义的变量a=undefined2. fn就是函数本身vo定义如下:vo={a: undefinedfn:}* 开始执行代码1. 第一行 表达式将修改 a=12. 第二到五行声明函数3. 到了fn() 开始执行函数此时函数为 一作用域,只要存在作用域 就先解析所有在函数fn内部1. 解析 a=undefined在fn内部 ao定义如下:ao(fn)= { a: undefined}2. 执行fn内部第一行 弹出 undefined 第二行 把a赋值为2 (这里的a是在fn中的局部变量 和外包的a全局变量不同)4. 最后一行打印全局变量a=1;说明 如果把function fn() {console.log(a);a = 2; (把var去掉 此事的a 变成了全局变量)}
分析这道题var foo = 1;function bar() {foo = 10;return;function foo() {}}bar();console.log(foo)* 解析阶段(进入执行上下文)vo = {foo: undefinedbar: <Function "bar">}* 执行代码阶段1. 第一行 foo =1;2.6行执行函数bar() (第二行到第5行的定义的函数)在函数bar的内部又有解析和执行阶段表示如下:vo(bar) = {foo: <Function "foo">}执行阶段在函数bar内部的第一行 foo=10 (这里需要注意的是 foo是bar的局部变量 不会影响 外面全局的变量foo)3: 最后一行 console.log() foo=1;

var foo=1;function bar( ) {foo=10;return function foo() { };}bar( );alert(foo);最后对于这道题 为什么结果是10 不是1 你只要明白 return function foo() { }; 其实是一个函数表达式 不是函数定义. 原因就在这里var a = 1;function fn(a) {console.log(a);a = 2;}fn(a)console.log(a); 这道题关键在于fn(a) 其实就是 fn(1) 在函数内部 解析阶段 a=1 (这里a在函数内部 是形参 a=2 也是改变fn内的局部变量 不影响全局的a )
  • 有一点需要说明的是,我们知道,JS中没有块级作用域,只有函数包含的块才会被当做是作用域。诸如for、if等用花括号包含起来的内容是不算作作用域的。
    也就是说,其中内容隶属于全局作用域,在全局范围内都可以访问到,如下

alert(fn);if(true){var a = 1;function fn() {alert(123);}}

总结

你只要明白了 函数执行分2个阶段 (确定上下文 执行代码) 在确定上下文阶段定义vo(ao)对象值时候 的规则
规则如下
argument(函数的形参) > function声明 > var声明 (也就之前提高的变量提升Hoisting)

  • 函数的所有形参(如果是在函数执行上下文中)
    每个形参都对应变量对象中的一个属性,该属性由形参名和对应的实参值构成,如果没有传递实参,那么该属性值就为 undefined


  • 所有函数声明(FunctionDeclaration, FD)
    每个函数声明都对应变量对象中的一个属性,这个属性由一个函数对象的名称和值构成,如果变量对象中存在相同的属性名,则完全替换该属性。


  • 所有变量声明(var, VariableDeclaration)
    每个变量声明都对应变量对象中的一个属性,该属性的键/值是变量名和 undefined,如果变量名与已经声明的形参或函数相同,则变量声明不会干扰已经存在的这类属性。






原创粉丝点击