金色小芝麻

vuePress-theme-reco 金色小芝麻    2021 - 2023
金色小芝麻 金色小芝麻

Choose mode

  • dark
  • auto
  • light
主页
分类
  • JavaScript
  • BUG复盘
  • SVG笔记
  • TypeScript
  • 个人总结
  • CSS笔记
  • 开发工具
  • 前端入门
  • Vue2.0
  • 性能优化
  • 架构学习
  • 每日一题
标签
时间轴
社交
  • 掘金 (opens new window)
author-avatar

金色小芝麻

83

文章

27

标签

主页
分类
  • JavaScript
  • BUG复盘
  • SVG笔记
  • TypeScript
  • 个人总结
  • CSS笔记
  • 开发工具
  • 前端入门
  • Vue2.0
  • 性能优化
  • 架构学习
  • 每日一题
标签
时间轴
社交
  • 掘金 (opens new window)
  • 前端入门

    • 博客搭建过程及相关配置
    • 数组中的16种常用方法
    • JS中的数据类型简析——基本数据类型值
    • JS中的数据类型object基础
    • JS中三种常见的判断
    • 数据类型之间的区别(堆内存Heap VS 栈内存Stack)
    • JS基础练习题及解析
    • 开关灯的小案例
    • JS中数据类型检测方法——typeof
    • 条件判断几个小练习
    • JS循环——for循环
    • 自定义属性实现选项卡小案例
    • 字符串中的12种常用方法
    • DOM操作的简单原理
    • JS实现隔行变色鼠标跟随小案例
    • JS中function的基础知识
    • JS中数组去重的三种方法
    • JS中时间格式化的三种方法
    • JS中URL参数处理的三种方法
    • JS小案例——获取随机验证码
    • DOM操作之——获取DOM标签的9种方式
    • DOM操作之——DOM节点类型及属性
    • DOM操作之——元素的增删改、样式修改、插入内容
    • JS中THIS相关问题梳理
    • JS中的变量提升机制
    • 在全局:私有上下文中:带VAR和不带VAR的区别
    • 作用域和作用域链查找机制
    • JS中堆栈内存的释放问题
    • JS中的闭包机制
    • ES3和ES6定义变量的区别
    • JS中的单例和工厂设计模式
    • JS中的面向对象OOP理论基础
    • 构造函数创建自定义类
    • JS中的原型和原型链
    • Math中常用的9种方法
    • 从一道阿里经典面试题剖析函数三种角色
    • 重写内置call
    • 重写一个内置new
    • 浏览器渲染页面的主体流程
    • 减少DOM的回流和重绘
    • JS中的多种继承方式
    • JS中数据类型检测四种方式的优缺点
    • JS中的正则表达式&&全面梳理
    • 非严格模式 🆚 严格模式的区别
    • 数组方法reduce、filter、flat
    • 获取数组中最大值/最小值的三种基础方法
    • 轮播图——渐隐渐显版
    • 深克隆 VS 浅克隆|深比较 VS 浅比较|回调函数
    • 轮播图——左右切换版
    • 事件及事件绑定、事件对象及事件传播
    • 从在地址栏输入网址到看到页面的过程&&AJAX基础
    • 想自学JS吗?想提升JS底层原理吗?76张脑图带你彻底搞懂原生JS
    • 面试手写API系列
    • 30张脑图带你从零开始学VUE
    • JS中的盒子模型
    • 初识JS-基础中的基础
    • 前端发展简史
    • JS中的三大类输出方式

ES3和ES6定义变量的区别

vuePress-theme-reco 金色小芝麻    2021 - 2023

ES3和ES6定义变量的区别

金色小芝麻 2020-05-22

var 和 let 的区别,实际上就是 ES3 定义变量 和 ES6 定义变量的区别

  • ES3 定义变量的方式:
    • var
    • function
  • ES6 定义变量的方式:
    • let
    • const
    • class
    • import

ES6 语法相对于 ES3 来说,最大的特点就是让 JS 变得更加严谨,告别 JS 松散的特点

# 思维导图

# 一、LET 和 VAR 的区别

# 1、变量提升方面

  • let 不存在变量提升,所以变量只能在声明定义后使用
  • var 存在变量提升
console.log(n); //=>undefined
var n = 10;
console.log(n); //=>10

console.log(n); //=>Uncaught ReferenceError: Cannot access 'n' before initialization  LET不存在变量提升,所以变量只能在声明定义后使用
let n = 10;
console.log(n); //=>10 

# 2、重复声明方面

  • let 是不允许重复声明的
    • 在当前上下文中,不管用什么方式,只要声明了这个变量,都不能基于 let 重复声明了,会报错
    • 是否重新声明,并不是在代码执行阶段检测的,而是在词法解析的阶段检测的
  • var 允许重复声明
    • 浏览器本身只识别一次,但不会报错
var n = 12;
var n = 13;
console.log(n); //=>13

let n = 12;
let n = 13;
console.log(n); //=>Uncaught SyntaxError: Identifier 'n' has already been declared

var n = 12;
let n = 13; //=>Uncaught SyntaxError: Identifier 'n' has already been declared

//=>是否重新声明,并不是在代码执行阶段检测的,而是在词法解析阶段检测的(词法解析阶段类似于变量提升,在代码还没有执行之前,就发生了,一旦发现有词法错误 SyntaxError ,当前代码都不会再执行了)
console.log('OK');
var n = 12;
let n = 13; //=>报错 但是OK都没有被执行

# 词法解析、词法错误、语法错误

  • 词法解析:词法解析阶段类似于变量提升,在代码还没有执行之前就发生了
  • 词法错误SyntaxError :在词法解析阶段报错,代码还没有执行就报错,一旦发现有词法错误 SyntaxError ,当前代码不会在执行了
  • 语法错误ReferenceError:在代码执行阶段报错,报错前的代码会正常执行

# 3、全局对象GO方面

在全局上下文中

  • let 声明的变量仅仅是全局变量,和GO没什么关系
  • var 声明的变量即是全局变量,也相当于给GO(window)设置了一个属性,而且两者建立映射机制
var n = 10;
console.log(window.n); //=>10

let n = 10;
console.log(window.n); //=>undefined

# 4、暂时性死区问题

  • let能解决基于typeof检测一个没有被声明过的变量结果是"undefined"的暂时性死区问题;
  • var 不能

浏览器有一个BUG(暂时性死区):基于typeof检测一个没有被声明过的变量,并不会报错,结果是"undefined";但是从正确角度来讲应该报错才是正常的!

console.log(typeof n); //=>"undefined"

//=> 如果这个变量在后面会用`LET`声明,则前面再基于`typeof`检测就会报错:不能在声明之前使用
console.log(typeof n); //=>Uncaught ReferenceError: Cannot access 'n' before initialization
let n = 10;

# 5、块级作用域

  • let 在遇到除对象/函数等大括号以外的大括号(例如判断和循环)时,会形成新的块级作用域
  • var 没有块级作用域

# 二、LET 的块级作用域

在整个JS中,目前为止我们接触的上下文(作用域)只有两种

  • 全局上下文 EC(G)
  • 函数执行形成的私有上下文 EC(XX)

此时循环体或者判断体等,都不会单独形成私有的上下文,里面的变量都是所在上下文中的变量。

if (1 === 1) {
    var n = 10;
    console.log(n); //=>10
}
console.log(n); //=>10

for (var i = 0; i < 5; i++) {...}
console.log(i); //=>5

但是在ES6中,提供了一个新的上下文(作用域)形式:块级作用域

  • 除对象/函数等大括号以外,如果在其余的大括号中(例如:判断和循环)出现 LET/CONST 等,则会把当前大括号包起来的部分形成一个独立的私有上下文,基于 LET/CONST创建的变量是当前块级作用域域中的私有变量;
if (1 === 1) {
    var n = 10; //=>n是全局变量
    let m = 20; //=>m是当前大括号包起来的 私有的 块级作用域中的 私有变量
    console.log(m); //=>20
}
console.log(n); //=>10
console.log(m); //=>Uncaught ReferenceError: m is not defined


// 类似于形成两个闭包(这里叫做私有的块级作用域)
{
    let x = 10,
        y = 20;
    console.log(x, y);
}
{
    let x = 100,
        y = 200;
    console.log(x, y);
}


for (let i = 0; i < 3; i++) {
    console.log(i); //=>0 1 2
}
console.log(i); //=>Uncaught ReferenceError: i is not defined

# 循环中遇到let/const等

  • 图解:
  • 1、创建一个总的块级作用域(父作用域):用来控制循环一轮轮执行
  • 2、每一轮循环都会形成一个新的私有上下文(子作用域):
    • 用来实现循环中要处理的业务需求
    • 会把每一轮循环 i 的值,作为子作用域中的私有变量

和我们基于 VAR ,每一轮循环都创建一个闭包类似

# 三、LET 和 CONST 的区别

  • CONST声明的变量,不能重新指向新的值(不能修改指针的指向);
let n = 10;
n = 20;
console.log(n); //=>20

const n = 10;
n = 20; //=>Uncaught TypeError: Assignment to constant variable.
console.log(n);

# 一道经典面试题

用const 定义的变量值,永远不能被修改了?

  • 答:不对
  • 1、const 不能更改指的是:这个变量不能在和其他值进行关联了,也就是不能修改 const 声明变量的指向
  • 2、但是可以在不改变指向的情况下,修改堆内存中的信息(这样也是把值更改了)
  • 3、所以:const 声明的变量,不能修改他的指针指向,但是可以改变其储存值的

例如下面这种情况:

const obj = {
    name: '金色小芝麻'
};
obj = [10, 20]; //=>Uncaught TypeError: Assignment to constant variable. 
//=> 它不能更改指的是:obj这个变量不能在和其它值进行关联了,也就是不能修改const声明变量的指向

obj.name = '哈哈';
console.log(obj);//=>{name: '哈哈'};
 //=>但是可以在不改变指向的情况下,修改堆内存中的信息(这样也是把值更改了),
 //=> 所以记住:const声明的变量,不能修改它的指针指向,但是可以改变其存储值的
 //=> 所以也就不能说const创建的是一个常量;
欢迎来到 金色小芝麻
看板娘