S01E02-变量及数据类型
万物起源
变量
概念
什么是变量?存储值的一个容器或代号
什么是值?
存储的数据
声明变量的几种方式
var(ES3)
function(ES3)创建函数(函数名也是变量,只不过存储的值是函数类型的)
ES6 新增:
- let
- const
import
ES6 的模块导入class
创建类
注意:常量声明必须赋值,而且不能重复赋值。
1 | const a // Uncaught SyntaxError: Missing initializer in const declaration |
数据类型
值的类型:JS 中的变量是没有类型的,只有值才有。
值的类型可分为:
- 基本类型(值类型)
- Null
- Undefined
- String
- Boolean
- Number
- Symbol(ES6 中新增加的一个特殊的类型,唯一的值)
- 引用类型
- 普通对象
- RegExp(正则对象)
- Date(日期对象)
- Math(数学对象)
- Error(错误对象)
- Function
注意:说到数据的类型都是大写哦,尽管 typeof 会返回小写的
Function: 特殊的引用类型,不用于存储数据
需要注意的知识点
这部分内容比较基础,不会全部列出,会介绍一些特殊的,需要注意的。
基本包装类型
3 个特殊的基本类型:String、Number、Boolean
在逻辑上讲,基本类型值是没有属性和方法的,但却有 .length 属性和很多的 API,这是因为 JS 底层会自动将 String、Number、Boolean 类型值包装为一个封装对象。
栗子:
1 | var a = new Boolean( false ); |
答案是:输出 2!
null && undefined 的区别
都代表空或者没有,作为值时小写
- null:
空对象指针(没有指向任何的内存空间)
- undefined:未定义
null 一般都是在初始化值时,先手动的先赋值为 null,然后再给他赋具体的值
1 | var num = null; |
undefined 一般都不是人为手动控制的,大部分都是浏览器自主为空(后面可以赋值也可以不赋值)
1 | var num; //=>此时变量的值浏览器给分配的就是 undefined |
项目中一些细节问题:
初始化值时,一般初始化为 null,因为它在内存中是不占空间的。而 0、[]、{} 等是有值的,会在内存中占空间。
undeclared 是一种语法错误。访问未声明的变量, 则会抛出异常, 终止执行。ReferenceError:a is not defined。
特殊的 NaN
我们来介绍一个非常特殊的数字:
NaN:not a number,不是一个数字
其实,not a number 容易引起误解,因为 NaN 仍然是数字类型,叫无效数值更准确些。
1 | var a = 2 / "foo"; // NaN |
isNaN():检测当前的数字是不是无效数字
在重学 JS 系列 - 数据类型转换会对 isNaN 有更为细致的讲解。NaN 的比较
特殊到自己不等于自己。
isNaN(num) 常作为语句的条件,来检测是否是有效数字
1
2
3if(isNaN(num)){
}
// 条件不可以用 Number(num) == NaN
对象字面量语法需要注意的几点
一般来说,对象的属性名只能是字符串格式的或者数字格式的,不能是其它类型的。
当对象的属性名是数字时,不支持点表示法。1
2
3
4
5
6
7var obj = {
name: 'chen',
0: 100,
};
obj[0] //=>100
obj['0'] //=>100
obj.0 //=>Uncaught Syntax: Unexpected number 语法错误当属性名是其他格式时,浏览器会把这个值 toString() 转换为字符串,然后再
以这个字符串为key
进行存储。1
obj[{}] = 300; //=>先把({}).toString()后的结果作为对象的属性名存储进来 obj['[object Object]']=300
访问对象的属性
- 点表示法:对象.属性
- 方括号表示法:对象[“属性”],可以通过变量来访问属性。
不管是哪种写法,
- 有这个属性名,则可以正常获取到值(哪怕是 null),赋值操作会修改这个属性的值
- 没有这个属性名,则获取的结果是 undefined,赋值操作会新增加这个属性
栗子
1
2
3
4
5
6
7
8
9var obj = {
name:'chen',
age:9
};
var name = 'chen';
obj.name //=>'chen'
obj['name'] //=>'chen'
obj[name] //=>?
基本类型与引用类型的区别
这是非常常见的、又非常基础的面试题哟!
红宝书中是这样概括的:
存储位置的区别
- 基本类型的值一般被保存在于栈内存中,引用类型的值是对象,被保存在堆内存中。
- 包含引用类型的变量的值是一个指向该对象的一个指针,这个指针被保存在栈内存中。
访问方式的区别
- 基本类型是按值访问的。因为可以操作存储在变量中的实际的值
- 引用类型是按引用访问的。因为引用类型的值是保存在堆内存中的对象,这块不同于其它语言,JS 不允许直接访问对象的内存空间。在操作对象时,实际上是操作的是对象的引用而不是实际的对象。
复制操作的区别
- 基本类型复制的是这个值的一个副本,操作两个变量互不影响。
- 引用类型复制的其实是一个指针(地址的副本),复制操作结束后,两个变量将指向堆中的同一个对象,改变一个,会影响另一个。
为了彻底理解,我们来看一个的栗子:
1 | var a = 12; |
执行过程是这样子的:
- 首先声明一个变量 a、b(变量提升,值为 undefined),在栈内存中开辟一块内存空间存储 12
- 执行 var b = a;,复制过程:
复制一份 12 的副本,在栈内存中重新开辟一块内存空间,存储这个副本,然后将这个副本赋值给变量 b
注意:原来的 12 和它的副本没有任何关系,在栈内存中占据不同的内存空间,互不影响
- 为了验证这句话,我们执行 b = 13,会在栈内存中再开辟一块内存空间,存储 13,将 13 赋值给变量 b,原来的 12 的副本已废弃,修改b的值不会影响a的值
值类型复制的过程,如图所示:
引用类型是如何实现复制的呢?还是上栗子吧。。。
1 | var obj1 = {m: 20}; |
上面的代码,一起来分析一下:
- 首先声明一个变量 obj1、obj2(变量提升,值为 undefined),然后在堆内存中开辟一块内存空间,存储对象的键值对(为这个空间加了一个
16进制的地址
的标记,就是我们常说的指针),接着将这个地址赋值给变量 obj1 - 遇见 var obj2 = obj1;,是这样子复制的:
复制一份这个地址的副本,在栈内存中重新开辟一块内存空间,存储起来,然后将这个副本赋值给变量 obj2,这时,obj2 和 obj1 指向堆内存中同一个对象,不管修改谁,其实修改的是一个值,所以最后输出 100.
最后,我们画一个图,来形象的展示一个引用类型复制的过程:
思考题:
1 | var obj = { |
答案是:
在 m: obj.n * 10 行报错:Uncaught TypeError: Cannot read property ‘n’ of undefined,思考一下,为什么?
我们一起来分析一下:
- 变量提升,obj = undefined
- 开辟一个新的堆内存(比如地址是 AAAFFF111),把键值对存储到堆内存中
->n: 10
->m: obj.n*10
=>obj.n 此时堆内存信息还没有存储完成,空间的地址还没有给 obj,此时的 obj 是undefined, 访问 obj.n 就是在访问 undefined.n
结束
重学 JS 系列 预计 25 篇左右,这是一个旨在帮助大家,其实也是帮助我自己捋顺 JavaScript 底层知识的系列。主要包括变量和类型、执行上下文、作用域及闭包、原型和继承、单线程和异步、JS Web API、渲染和优化几个部分,将重点讲解如执行上下文、作用域、闭包、this、call、apply、bind、原型、继承、Event-loop、宏任务和微任务等比较难懂的部分。让我们一起拥抱整个 JavaScript 吧。
大家或有疑问、或指正、或鼓励、或感谢,尽管留言回复哈!非常欢迎 star 哦!