js基础阵营:变量篇之变量定义

这一次我们首先将视角投向js基础知识之变量,下面看下变量的基础信息
姓名:变量
类型:松散型
定义方式:var,const,let或者直接变量名
常见问题:变量覆盖,变量提升,变量作用域
我们从上面的基础信息来一点一点的剖析变量这士兵甲

1:变量的类型

var num = 14;//这个时候变量num的类型为数字
console.log(typeof num);//查看num的类型为number
num = '改成字符串';//这个时候变量num的类型为字符串
console.log(typeof num);//查看num的类型string

js的变量的松散类型(弱类型)其实是相对于java等其他语言的强类型来说的,强语言类型在定义变量的时候需要指定变量的类型,如果试图去修改变量类型的话就会报错。

2:变量定义的方法

2.1 var

在ES5的时候我们定义变量的方法是通过var关键字+变量名的方式定义一个变量,例如:

var name;//定义了变量name

当然也可以直接通过变量名的方法来定义一个全局变量,不过不是很推荐这种做法,例如:

name;//定义了一个全局变量

不过随着时光的飞逝,规则也在悄悄的发生了变化,出现了ES6语法,在ES6语法中出现了通过let,和const来定义变量的方法,首先我们来看const

2.2 const

const是定义常量的方法,定义的时候必须初始化,且定义后不可以修改。常量通常是大写的方式,并且通过const定义的常量是不允许被修改的,如果尝试修改会报错,例如:

const NAME;
NAME = '来瓶二锅头';

将上面代码复制到浏览器控制台运行,会直接报错


同样的下面也会报错

const PI = 3.14;
console.log(PI);
PI= 3.1415;

那么我们设想一下,如果我们将常量定义为一个对象,尝试修改对象属性的话会发生什么情况呢?心动不如行动。

const USER = {
    name:'来瓶二锅头'
};
console.log(USER);
USER.name="再来瓶二锅头";
console.log(USER);

这个时候我觉得下面这个图片挺适合的



上面我们已经说过了const定义的是一个常量,常量是不允许修改的,修改的话就会报错,但是为什么修改对象属性就可以的呢?这里就需要从原理来说了,每次听到原理这个词的时候都觉得好高大上,这就跟我们看到张无忌使用乾坤大罗移在光明顶大战六大派的时候一样,哇塞,乾坤大挪移好腻害啊。但是为什么厉害呢?不知道,反正就是厉害。我们今天就要去好好剖析一下乾坤大挪移为啥子那么腻害,不对是const为什么可以修改对象的属性。其实这里为什么能够修改对象类型需要引入两个概念(真烦,一个东西说不清楚非要再来一个东西,嘿嘿):引用类型和基本类型。我们下面先来说明下这两个概念,等着两个概念弄通了,你就会了乾坤大挪移了,咳咳,滚犊子,是理解了为什么可以修改对象属性

2.2.1 基本类型

基本类型指的是简单的数据段,按照数据类型(这玩意后面介绍)来说就是基本的数据类型都是基本类型,包括undefined,Null,Boolean,Number,String。这些类型的值都是按值访问的,操作的话就是实际的值。什么叫操作的是实际的值呢?那么我们看下一下的例子:

var num1 = 5;
console.log('num1的值为:' + num1);
var num2 = num1;
console.log('num2的值为:' + num2);
num1 = 10;
console.log('修改后num1的值为:' + num1);
console.log('num2的值为:' + num2);

我们把代码复制到控制台,查看运行结果如下:



我们可以看到将num1的值赋值给num2,相当于直接将5赋值过去,修改num1的值不会造成num2值得改变。那么我们就可以得到以下一个结论:操作基本类型的数据的时候实际上操作的就是实际的值。

2.2.2 引用类型

引用类型指的是多个值构成的对象。按照数据类型来划分的那就是Object类型(数组也是object类型的数据哦)的数据。那么我们操作引用类型的值的时候是实际的值吗?我们看下一下例子:

var obj1 = {
    name:'二锅头'
};
console.log('obj1的数据为' + JSON.stringify(obj1));
var obj2 = obj1;
console.log('obj2的数据为' + JSON.stringify(obj2));
obj1.name='我爱二锅头';
console.log('修改后obj1的数据为' + JSON.stringify(obj1));
console.log('修改后obj2的数据为' + JSON.stringify(obj2));

复制到控制台中看结果为



这个时候脑海里肯定会有以下的表情



这个就要说到引用类型变量值的问题了。按照js规定,js不允许直接访问内存中的位置,而Object类型的数据是放在内存中的,所以我们这个时候定义的引用类型的变量是一个指针,指向改对象所在内存的位置。举个例子吧,就是我们都有银行卡,银行卡里有自己的钱,我们可以直接从银行卡里把钱取出来吗?肯定不能(违法行为不在考虑范围内哈),我们需要去银行,然后取出我们的钱,我们能操作的只是我们的银行卡。而银行卡就是一个中介,一个指向我们钱的中介。我们可以用一下图来表示上面写的obj1和obj2的例子

因此当我们复制一个引用类型数据赋值给另一个变量的时候其实只是创建了一个副本,这个副本就是一个指针。指向堆栈内存中的一个对象。
到此为止我们搞懂了什么是引用类型什么是基本类型,那么我们回归到我们的主体中,为什么const定义的对象类型的常量可以修改。答案就是对象类型的常量是引用类型的,此时我们定义的常量其实就是一个指针,指向堆栈内存中一个对象的指针,这个指针是不能修改的,但是堆栈内存中的变量可以随便变。惊不惊喜意不意外。哇咔咔。那么我们看下面的例子:

const OBJ1 = {
   name:'二锅头'
};
console.log('OBJ1的数据为:' + JSON.stringify(OBJ1));
OBJ1 = {
   name:'我爱二锅头'
};

复制到控制台中看结果



哇咔咔。收功。

2.3 let

在ES5时代没有块级作用域这一说(又来了一作用域的概率,嘿嘿嘿,后面专门介绍这一老哥),在ES5那个时代只有全局作用域和函数内作用域,所以在ES6的时候引入了块级作用域。通过let来定义变量,定义的就是一块级作用域,我们来看以下的例子:

if(true){
   let num = 5;
   console.log('num的值为:' + num);
}
console.log('num的值为:' + num);

复制到控制台来看下结果



这个时候定义的变量num的作用域为块级作用域,只在if代码块中起作用,在外部就不起作用了,我们来对比下var定义变量,同样的方式,小二,上代码

if(true){
   var num = 5;
   console.log('num的值为:' + num);
}
console.log('num的值为:' + num);

复制到控制台看结果



看到不同了吧,那么通过let就能解决一个很老套的问题了,小二上问题,以下程序运行出现的结果是什么?

for(var i = 0;i<5;i++){
    setTimeout(function(){
        console.log(i);
    },100);
}

结果是什么?输出5个5,为什么呢?具体原因涉及到宏任何和微任务,后面单独介绍(嘿嘿嘿,又埋坑了)那么怎么解决呢?答案很显然,用let来定义变量就可(这是最简单的,当然还有其他方法,后面别的章节会说到),上代码

for(let i = 0;i<5;i++){
    setTimeout(function(){
        console.log(i);
    },100);
}

答案是什么呢?自己复制到控制台就能看到结果了
这个时候关于变量定义的相关问题都已经结束了。

你可能感兴趣的:(js基础阵营:变量篇之变量定义)