你不知道的js系列(12)-声明提升(代码片段)

Zina Zina     2022-12-24     362

关键词:

我们直觉上会认为 JavaScript 代码在执行时是由上到下一行一行执行的。但实际这并不完全正确

 

a = 2;
var a;
console.log(a);
这里可能会认为是 undefined,因为 var a 声明在 a = 2 之后。实际输出了 2。

 

console.log(a);
var a = 2;
鉴于上面的代码可能会是 2,还有人认为可能会报异常 ReferenceError,不幸这两种猜测都不对,输出来的是 undefined

 

要搞明白这个,要明白编译的原理,在编译阶段中的一部分工作就是找到所有的声明,并用合适的作用域将它们关联起来。这也是词法作用域的核心内容

 

正确的思路是,包括变量和函数在内的所有声明都会在任何代码被执行前首先被处理

 

当你看到 var a = 2; 时,可能会认为这是一个声明。但 JavaScript 实际上会将其看成两个声明:var a 和 a = 2;第一个定义声明是在编译阶段进行的,第二个赋值声明会被留在原地等待执行。所以第一段代码会以如下形式进行处理
var a;
a = 2;
console.log(a); //2

 

第二段代码实际按一下流程处理
var a;
console.log(a); // undefined
a = 2;

 

这个过程就好像变量和函数声明从它们在代码中出现的位置被“移动”到了最上面。这个过程就叫做提升。每个作用域都会进行提升操作,函数自身也会对内容 var a 进行提升(显然并不是提升到整个程序的最上方)

 

foo(); // TypeError
bar(); // ReferenceError
var foo = function bar()
  // ...
可以看到函数声明被提升,但是函数表达式却不会被提升。这段程序的 foo 被提升并分配到所在作用域,因此不会导致 ReferenceError,而是抛出了 TypeError: foo is not a function。bar 抛出了 ReferenceErro。上段代码实际上会被理解为
var foo;

foo();
bar();

foo = function() 
  var bar = ...self...
  //...

 

变量和函数都会被提升。但是函数会首先被提升,然后才是变量
foo(); // 1
var foo;
function foo() 
  console.log(1);

foo = function()
  console.log(2)

这个代码会被引擎理解成如下形式

function foo() 
  console.log(1)

foo(); // 1
foo = function() 
  console.log(2)

尽管重复的声明会被忽略掉,但是后面的函数声明还是可以覆盖前面的

 

foo();
var a = true;
if(a)
  function foo() 
    console.log(‘a‘)
  
else
  function foo()
    console.log(‘b‘)
  

这里 foo 输出了 b ,不是我们想要的结果,因为声明被覆盖掉了,所以需要注意这种行为并不可靠,尽可能避免在块内部声明函数

你不知道的js(代码片段)

1、作用域 块级作用域let只在函数内部自己的作用域内有效 全局作用域var 函数作用域 找不到作用域抛出ReferenceError变量有了则抛出TypeError 先声明后赋值 函数提升变量提升函数优先,函数声明提升在普通变量之前 函数表达... 查看详情

你不知道的js系列(7)-欺骗词法作用域(代码片段)

如果词法作用域完全由写代码期间函数所声明的位置来定义,怎样才能在运行时来“修改”词法作用域呢?有些人喜欢特殊的办法来解决遇到的问题。我们规定词法作用域是代码写在哪里决定的,一旦决定了无法更改,因... 查看详情

你不知道的javascript系列中(2)-undefined和未声明(代码片段)

vara;typeofa;//undefinedvarb=c;varc;b=c;typeofb;//undefinedtypeofc;//undefined大多数开发者倾向于将undefined等同于undeclared(未声明),但在JavaScript中它们完全是两回事 vara;a;//undefinedb;//ReferenceError:bisnotdefined‘bisnot 查看详情

你不知道的js系列(33)-对象复制(代码片段)

JS初学者最常见的问题之一就是如何复制一个对象。看起来应该有一个内置的copy()方法,实际上比想象中的更复杂,我们无法选择一个默认的复制算法functionanotherFunction()/**...*/;varanotherObject=c:true;varanotherArray=[];varmyObject=  a:2, ... 查看详情

你不知道的javascript系列中(12)-特殊数值无穷数(代码片段)

熟悉传统语言(C)的开发人员可能都遇到过编译错误(compilererror)或者运行时错误(runtimeexception),例如“除以0”。然后在JavaScript中上例的结果为Infinity vara=1/0;//Infinityvara=-1/0;//-Infinity如果除法运算中的一个操作数... 查看详情

你不知道的javascript系列中(12)-特殊数值无穷数(代码片段)

熟悉传统语言(C)的开发人员可能都遇到过编译错误(compilererror)或者运行时错误(runtimeexception),例如“除以0”。然后在JavaScript中上例的结果为Infinity vara=1/0;//Infinityvara=-1/0;//-Infinity如果除法运算中的一个操作数... 查看详情

你不知道的js系列上(45)-隐式混入(代码片段)

varSomething=  cool:function()    this.greeting=‘HelloWorld‘;    this.count=this.count?this.count+1:1;  Something.cool();Something.greeting;//‘HelloWorld‘Something.count;//1varAnother=  cool:func 查看详情

你不知道的js系列(39)-对象遍历(代码片段)

for循环可以遍历数组varmyArray=[1,2,3];for(vari=0;i<myArray.length;i++)  console.log(myArray[i])//123ES5增加了数组的辅助迭代器,包括forEach(...)、every(...)、some(...)forEach(...)会遍历数组中的所有值并忽略回调函数的返回值every(...)会一直运行直... 查看详情

你不知道的js系列(13)-什么是闭包(代码片段)

当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行functionfoo()  vara=2;  functionbar()    console.log(a);    returnbarvarbaz=foo();baz();//2——朋友,这就是闭包的效果在foo... 查看详情

你不知道的js系列上(45)-显式混入(代码片段)

JS的对象机制并不会自动执行复制行为,由于其他语言中表现出来的复制行为,因此JS开发者也想出了一个方式来模拟类的复制行为,这个方法就是混入。我们先看第一种,显式混入。//非常简单的mixin()例子functionmixin(sourceObj,targe... 查看详情

你不知道的js系列(14)-闭包无处不在(代码片段)

上一节的闭包是为了解释如何使用闭包而人为地在结构上进行修饰,在昨天的闭包基础上,我们可以更加灵活的使用闭包functionwait(message)  setTimeout(functiontimer()    console.log(message)  ,1000)wait(‘hello,consure‘);内部函数timer... 查看详情

你不知道的js系列(29)-对象属性(代码片段)

存储在对象容器内部的是这些属性的名称,它们就像指针(从技术角度来说是引用)一样,指向这些值真正的存储位置。 varmyObject=  a:2myObject.a;//2myObject[‘a‘];//2.语法通常被称为‘属性访问’,[]语法通常被称为&lsqu... 查看详情

《你不知道的js(中卷)》对象(代码片段)

三、对象:一)、语法:对象有两种形式定义:声明(文字)形式:varmyObj=key:value//...;构造形式:varmyObj=newObject();myObj.key=value;在声明形式中可以添加多个键/值对,但是在构造形式中必须逐个添加属性。1、类型:? 对象是JS的基... 查看详情

你不知道的js系列(22)-thisnew绑定(代码片段)

在传统的面向类的语言中,“构造函数“是类中的一些特殊方法,使用new初始化类时会调用类中的构造函数。通常的形式是这样的something=newMyClass(..);然而JavaScript中new的机制实际上和面向类的语言不同。它们只是被new操作... 查看详情

那些你不知道的炫酷开关交互效果(12种)(代码片段)

...总计收集12款不同交互效果,相信总有一款适合你。那些你不知道的炫酷交互效果系列:那些你不知道的炫酷按钮交互效果那些你不知道的炫酷导航交互效果那些你不知道的炫酷开关交互效果效果1基于普通的开关效果增加了向内... 查看详情

你不知道的js系列(30)-对象属性(可计算属性名)(代码片段)

如果你需要通过表达式来计算属性名,[]操作符就派上用场了。ES6中使用[]包裹一个表达式来当作属性名varprefix=‘foo‘;varmyObject=  [prefix+‘bar‘]:‘hello‘,  [prefix+‘baz‘]:‘world‘,myObject[‘foobar‘];//hellomyObject[‘foobaz‘];//world... 查看详情

你不知道的js系列-引擎怎么查找变量(代码片段)

对代码进行处理的三个角色引擎:从头到尾负责整个JavaScript程序的编译和执行过程编译器:负责语法分析及代码生成等作用域:负责收集并维护所有变量的查询 vara=2;编译器首先会将这段程序分解成词法单元,然后将词法单... 查看详情

你不知道的js系列(19)-this调用位置(代码片段)

我们排除了一些对于this对错误理解并且明白了每个函数的this是在调用时被绑定的,完全取决于函数的调用位置。寻找调用位置就是寻找“函数被调用的位置”,但是做起来并没有这么简单,因为某些编程模式可能会隐藏... 查看详情