声明变量var与let.const(常量)详解,堆栈详解

1.ES6简介

ECMA是一个标准 JavaScript是ECMAScript的实现。

ES6是ECMAScript的一个版本,是2015年发布的,ES2015.

let关键字

ES6新增的关键字,用来声明变量。用法类似于var,但是所声明的变量,只在let所在的代码块有效。

var a = [];
    for (var i = 0; i < 10; i++) {
    
    
       a[i] = function(){
    
    
           console.log(i);
       }
}
a[6](); //10

var定义的变量遇到for循环的大括号是不会形成作用域的。所以在上面代码调用时,调用的是一个全局的变量。

   var a = [];
    for (let i = 0; i < 10; i++) {
    
    
        a[i] = function () {
    
    
            console.log(i);
        },
       /*  a[i] = function () {
            console.log(i);
        }等于这个函数运行了6次*/
    }
    a[6](); //6

以上代码中,i是使用let声明的,当前的i只在本轮的循环中有效。每一个循环中的i其实都是一个新的变量。所以输出的是6.(原理 let i = 0 与 a[i] 不是同一个i)

例子演示
var

 for (var i = 0; i < 3; i++) {
    
    
        var i = "abc";
        console.log(i);//输出一次abc(原理因为输出了一次就弹出到控制台,所以循环停止)
    }

let

 for (let i = 0; i < 3; i++) {
    
    
           let i = "abc";
           console.log(i);//输出3次abc
       }

结果:输出3次abc
原因:在代码块中的变量i与for循环中的变量i不在用一个作用域。

扫描二维码关注公众号,回复: 13140669 查看本文章

1.1 不存在变量提升

var 存在变量提升。即变量在声明之前使用时,值为undefined。js会将声明语句放在所有js代码之前执行。

console.log(a);
var a = 10;
//相等于
var a;
console.log(a);
a = 10;

我们看下let

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

结果:提示错误Cannot access 'a' before initialization(在初始化之前无法访问a)

.2 暂时性死区

 var tmp = 123;
    if (true) {
    
    
        tmp = "abc";
        let tmp;
        console.log(tmp);
    }

结果:提示错误Cannot access 'a' before initialization(在初始化之前无法访问)

使用变量时,会先寻找同一作用域下的变量。以上代码中,tmp=abc会优先寻找到下面的let tmplet tmp不存在变量提升,所以提示错误。

总结:在代码块中,使用let命令声明变量之前,该变量都是不可用的状态,在语法上,称为“暂时性死区”

.3 不允许重复声明

let a = "yasuo";
let a = "jianhao"  
console.log(a);/Identifier 'a' has already been declared(标识符'a'已经声明)
function show(a){
    
    
	let a = 10;
}
show(100);
过程:调用函数时将100给了a,结果发现函数里面已经定义了a
函数的形参`a`与函数内部的`a`作用域相等,所以会报错。

可以这样演示

<script>
    	function show(a){
    
    
         {
    
    let a = 10;}   
    }
    show(100);
</script>

函数的形参a与函数内部的a作用域不是同一个作用域,所以不会报错。

块级作用域

.1 为什么需要块级作用域

ES5 只有全局作用域和局部作用域(函数作用域),没有块级作用域。

缺点1:内部变量可能会覆盖外层的变量

var date = new Date();
function f(){
    
    
    console.log(date);
    if(false){
    
    
    	var date = "今天是个好日子"  
    }
}
f();//undefined

上面案例中,if代码块外部原意为使用外层的date,内部使用内部的date。但是函数执行后,结果却为undefined,原因是存在变量的提升。导致内部的date覆盖了外部的date变量

<script>/这样写就是我们想要的效果
  var date = new Date();
function f(){
    
    
    if(true){
    
    
    	var date = "今天是个好日子"  
    }
    console.log(date);
    
}
f();//今天是个好日子
</script>

缺点2:for循环中的计数变量泄露为全局变量。

<script>
    var hello = "hello"

    for (var i = 0; i < hello.length; i++) {
    
    
        console.log(hello[i]);//h e l l o
    }
    console.log(i);//5
</script>

以上案例中,for·循环中的变量i只是用来作为计数使用,但是for循环执行完成后,它没有消失,而是作为全局变量仍然存在。以后可能不会再使用,造成资源的浪费。

缺点2:for循环中的计数变量泄露为全局变量。

.2 ES6块级作用域

<script>
    function show() {
    
    
        let a = "亚索";
        if (true) {
    
    
            let a = "剑豪";
            {
    
    
                let a = "孤儿索"
            }
            console.log(a);//剑豪

        }
        console.log(a);//亚索
    }
    show()
</script>

块级作用域下的变量不会对外层的变量造成影响,同时支持多层嵌套。

if(true)let  a = "亚索";
console.log(a);//报错
ES6的块级作用域必须有大括号,所以使用if判断时,不可以省略大括号

练习:点击其中的任意li弹出其li的下标

<body>
    <ul>
        <li>诺手</li>
        <li>狼人</li>
        <li>亚索</li>
        <li>卡莎</li>
        <li>猫咪</li>
    </ul>
</body>
<script>
    var lis = document.querySelectorAll("li");
   
    //=1================
   /* for (var i = 0; i < lis.length; i++) {
        lis[i].onclick = function () {
            alert(i);//无论点击哪个li弹出都为5,因为var声明的i与下面的i是同一个作用域
        }*/
  //=2=============================
 /*  for (var i = 0; i < lis.length; i++) {
        lis[i].dataset.index = i;//这是给每一个li都加上了自定义属性dataset.index="i"
        lis[i].onclick = function(){
            alert(this.dataset.index);//点击哪一个输出哪一个的下标
        }
    }*/
    //=3=================================
    for (let i = 0; i < lis.length; i++) {
    
    
        lis[i].onclick = function () {
    
    
            alert(i);//点击哪一个输出哪一个的下标(因为声明的i与代码块中的i不在一个作用域)
        }
    }
</script>

const声明

1.基本用法

const也是用来声明变量的,不过它声明的是一个只读的常量。一旦声明,值不可修改。

const PI = 3.1415926;
// alert(PI);
PI = 3.14;当改变一个常量时就会报错
//Assignment to constant variable 为一个常量赋值了
alert(PI)

1.2 必须初始化

const PI;//声明一个常量时就要赋值
PI = 3.1415926;
alert(PI);
/报错 Missing initializer in const declaration  const声明中缺少初始化式

因为const声明的变量的值不可修改,所以,const一旦声明变量,则必须初始化。

1.3 一般变量名大写

const PI = 3.1415926;

这是一个规范。我们遵循,方便分辨,不是必须这样做

1.4 也有块级作用域

const与``let`相同,只会在声明的所在代码块作用域内生效。

{
    
    const PI = 3.1415926;}
alert(PI);
/报错   PI未定义

1.5 不存在变量提升

alert(PI);
const PI = 3.1415926;
/报错   在初始化之前无法访问'PI'

1.6 不可重复声明

 var PI = "3.14";
 const PI = 3.1415926;

var 声明 重复时不会报错,但会覆盖上一个声明变量

1.7 对于数组和对象中的元素进行修改,不算对常量的改变

const变量保存的变量指向的值的内存地址。对于简单的数据(数值,字符串,布尔值)值保存在变量指向的内存地址。

对于复合数据类型,变量指向的内存地址,只是该对象的地址,其中的内容发生变化不会使该对象的内存地址发生变化。

<script>
    const obj = {
    
    
        name: "亚索"
    }
    obj.name = "疾风剑豪";
    console.log(obj.name);
//疾风剑豪
</script>

堆栈知识

小知识:对象,数组,函数,数据存放在堆里面,这三个是引用值
引用值是在栈里面放堆的地址

例子:
基础类型
number(数值)
string(字符串)
boolean(布尔值)
null(空)
undefined(未定义)

引用类型
数组 array
函数function
对象Object

一、数据类型

为了更好的来阐述栈和堆,我们先来了解一下数据类型
在js中数据类型主要分为以下两大类:

基本类型:String,Number,Boolean,Null,Undefined,这5种基本数据类型它们是直接按值存放的,所以可以直接访问。
引用类型:Function,Array,Object,当我们需要访问这三种引用类型的值时,首先得从栈中获得该对象的地址指针,然后再从堆内存中取得所需的数据。

二、什么是栈,什么是堆

栈(stack):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈;
堆(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。

三、二者区别

1、缓存方式区别
1.栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放;
2.堆是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。

2、堆栈数据结构区别

堆(数据结构):堆可以被看成是一棵树,如:堆排序;

栈(数据结构):一种先进后出的数据结构。

在这里插入图片描述

<script>
    var a = 12;
    var b = a;
    b = 13;
    console.log(a); //12
    console.log(b); //13
</script> 
<script>
    var obj1 = {
    
    
        n: 100
    };
    var obj2 = obj1;
    obj2.n = 200;
    console.log(obj1.n); //200
</script>

在这里插入图片描述

下面这个例子可能会认为是100,其实不是的 在这里插入图片描述引用类型中,两个变量共用一个地址的时候,两个变量都能够操作堆里面的值,看下面例子
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/z18237613052/article/details/114029023