大数跨境
0
0

变量让新手感到JavaScript的诡异

变量让新手感到JavaScript的诡异 摩尔线程
2024-01-24
0
导读:你好, Alan, 我在之前的一篇文章中说道, 新手不适合直接上手JavaScript, 因为JavaScri

你好, 我是Alan, 我在之前的一篇文章中说道, 新手不适合直接上手JavaScript, 因为JavaScript的一些历史设计会对新手造成很大的困扰, 而变量就是其中之一。

在说那些坑之前, 我们先要搞明白这样的两个事情, 变量声明和赋值, 理解这两个你才能知道JavaScript的变量的那些吊诡之处。 

首先变量的声明意味着: 

  1.  在当前作用域中登记了这个变量名(保证你可以找到)

  2.  在JavaScript代码执行之前, 可以找到这个变量(规定了发生的时间)


那么变量赋值又意味着什么呢? 首先变量赋值是一个动作,它是需要执行的,变量赋值操作执行完成, 就意味着, 当前作用域中有一个变量,被绑定了一个值

同时 JavaScript会为已经声明但是没有赋值的变量, 绑定了一个undefined值, 在一些静态类型的编程语言比如Java, 编译器是给变量赋上一个与变量类型相对应的值。

一、变量提升

 有了这些基础知识, 我们就分别介绍一下与变量有关的那些巨坑,第一个介绍的是变量的预读取或叫变量提升问题,看一下下面这段代码:

console.log(a); //输出undefined值
var a;

你发现虽然var a;的代码是位于console.log之后的, 但是我们依然是可以访问到a的值, 也就是undefined, 这说明在我们可以访问到a并且在执行这条语句的时候a已经被绑定了undefined值, 也可以推出引擎执行这段代码的顺应该是下面这样:

var a; //在当前作用域中登记一个变量a = undefined; //为变量a赋上一个值//执行console.log(a); 输出undefined
console.log(a);

正是因为这种"代码位置与执行顺序"的不一致, 导致很多新手会产生深深的困惑。

将上面的代码稍稍改动, 得到下面这段, 下面的代码和上面不一样的地方在于, 我们在声明变量a的同时, 并为它赋上了一个值1。

console.log(a); //输出了undefinedvar a = 1; 
console.log(a); //输出了值1

看到这里你可能会有点困惑, 明明我已经给a去绑定了一个值, 为什么第一个console.log还输出undefined值, 这是因为第一个console.log和给a绑定的值1的操作都发生于执行期间, 而console.log先发生于给a绑定值1。

var a;
a = undefined;

//执行第一处的console.log
console.log(a);
a = 1;

//执行第二处的console.log
console.log(a);

上面的例子也说明了变量声明发生于代码执行之前, 而赋值行为是发生在代码执行阶段的

关于JavaScript的赋值的另外一个历史遗留问题, 就是向一个不存在的变量赋值, JavaScript将会在全局范围(一个全局对象)中去创建它, 但是这种问题在ES5的严格模式就被禁止了。

console.log(a); //得到undefined
a = 1

而在ES6之后, 为了兼容旧的JavaScript设计, 规定在全局对象之外, 继续维护一个变量名列表, 所有静态语法分析期间(使用声明的)或者eval()中使用var声明的变量都放在在这个变量名列表中, 同时这个变量名列表是无法被delete给删除的。

var a;
delete a; //删除失败

二、赋值表达式

在一些其它的编程语言中, 赋值动作是不会返回值, 我们通常叫赋值语句, 比如C语言中赋值就是一个语句, 不会返回任何的值, 但是在JavaScript的赋值会返回一个值, 也就是说这是一个赋值表达式。

比如下面这个赋值表达式, 会给我们返回一个值1。

console.log(a=1); //返回1

三、其它声明

ES6充分考虑了上面的种种问题, 并对旧有的语法充分地兼容, 当语法中出现var声明的变量, 包括函数声明使用"变量作用域"来管理, 其它情况下的变量声明, 都使用"词法作用域"管理。

这个其它声明, 指的就是下面这几种:

let a; 无法访问到undefined
cont a;
class a {}; 声明一个类
import ...; 导出标识符
try catch(a); 
for (let|const a)

上面说的是, 引擎分析层面的东西, 下面说一说关于使用层面的东西, 使用上面let声明是会拒绝提前访问的, 也就是会报错, 但是还是和我给你说的两条规则。

比如我们看下面这段代码, 报了这样的一个错"can't access lexical declaration 'a' before initialization" 无法访问a, 也就是说引擎是提前创建了这个a的。

console.log(a);
let a = 1;


好了到这里, 这篇文章就是结束了, 这篇文章主要介绍了JavaScript变量声明和赋值所对应的语义, 这些语义是构建JavaScript的基础。

如果你觉得这篇文章对你有所帮助的话, 可以给我点个赞, 也欢迎与我交流。


【声明】内容源于网络
0
0
摩尔线程
摩尔线程以全功能 GPU 为核心,致力于向全球提供计算加速的基础设施和一站式解决方案,为各行各业的数智化转型提供强大的AI计算支持。
内容 301
粉丝 0
摩尔线程 摩尔线程以全功能 GPU 为核心,致力于向全球提供计算加速的基础设施和一站式解决方案,为各行各业的数智化转型提供强大的AI计算支持。
总阅读117
粉丝0
内容301