一,数据类型
1.1 分类
js的数据类型主要分为两大类:基本/值类型(7个)、引用类型(3种)
值类型:数值number、字符串string、布尔boolean、未定义undefined、空null、唯一值symbol(ES6新增)、任意精度整数bigInt(ES2020新增)
引用类型:对象object(Array、Date、Map、Set等)、函数function
js的数据类型是一种弱类型或者动态类型,在运行的过程中变量的类型会被自动确定,一个变量也可以保存任意类型的数据。
1.2 数值-number
- js不区分整数和浮点数,都统一用number类型表示
- 浮点数不是一个确定的值,例如var a = 0.1 + 0.2; // a的值可能是0.30000000004
- Infinity表示无穷,用来表示正数值太大,或者负数值太小两种情况
(即正无穷:Infinity、负无穷:-Infinity)
- NaN(not a number)表示不是一个数字,它不等于任何值,包括他本身。主要出现在将字符串解析成数字出错的场合
isFinite() - 判断当前数值是否为一个正常的数值
isFinite(1) // true
isFinete(Infinity) // false
isNaN() - 判断当前字符串是否是一个数值
isNaN('123') // false
isNaN('abc') // true
1.3 字符串-string
- 字符串是0~多个字符组成的一个序列。
- ES5中的字符串可以用单引号、双引号实现;ES6中可以用模板字符串实现。
- 如果字符串中要使用引号,用反斜杠\转译(eg:"你好\"世界\"")
- \n 可以用作换行符
1.4 布尔-boolean
- 布尔类型只有true和false两个值,通常用来做判断逻辑
- js如果在某个预期的位置上需要布尔值,那么他就会将当前位置的值进行自动转换。
转换为false的值有“0”、“0n”、“false”、“空字符串”、“NaN”、“null”、“undefined”;
其他的值均转换成true
1.5 未定义-undefined
- undefined表示一个值被声明了,但没有赋值
- 当一个函数没有返回值时,返回的就是undefined
1.6 空值-null
- null是表示一个空对象的指针,即一个空值;与undefined不同,undefined表示没定义值
- undefined是派生于null的,所以undefined == null 返回true。但实际开发中没必要区分二者
1.7 唯一值-symbol
1.7.1 理解Symbol
- ES6新增的symbol类型是一个函数,他返回一个独一无二的值。主要用来作为对象属性的标识符使用,避免了对象的属性名不能重复命名的情况
- Symbol不是一个完整的构造函数,因为它不能使用new关键字进行调
1.7.2 Symbol使用示例
let sl = Symbol();
let obj = {
[sl]: 'symbol符号属性sl',
sl: '字面量属性sl'
}
console.log(obj[sl]) // symbol符号属性sl
console.log(obj.sl) // 字面量属性sl
1.7.3 Symbol使用注意事项
- 在定义、引用对象的属性标识符时,一定要放到中括号[]中,不然识别出来就是一个字符串
- 在Object.keys()和for...in循环中是无法获取到Symbol属性的
1.7.4 Symbol常用方法
- 创建symbol值时,接受一个可选的描述字段。但他只能用来调试,不能用来访问symbol本身。(eg: let sl = Symbol('description'))
- 获取对象中symbol属性的数组 - Object.getOwnPropertySymbols(obj)
其他的方法可以查看API
1.7.5 理解全局symbol
symbol虽然是唯一的,但是如果现在项目全局作用域中共享与复用symbol,就需要用到Symbol.for()和Symbol.keyFor()了
Symbol.for():for接受一个key值,用来从全局symbol注册表中获取当前的值并返回,如果全局当前没有这个symbol的key值,则会创建一个新的symbol值,与当前的key值关联。
eg:var userId = Symbol.for('userId');
Symbol.keyFor():他接受一个symbol值,用来从全局symbol注册表中获取与当前symbol值关联的key。
eg:var userSl = Symbol.for('userId');
var userKey = Symbol.keyFor(userSl);
1.7.6 Symbol的应用场景
- 上文说的作为对象的属性标识符使用
- 模拟类的私有属性和方法
- 用来代替常量
1.8 任意精度整数 - bigInt
1.8.1 理解bigInt
- number类型有最大和最小值的限制,于是bigInt用来表示任意大的整数值。但他和number是两种类型,不能混合使用二者进行算术运算。
- bigInt不是一个完整的构造函数,因为它不能使用new关键字进行调
1.8.2 bigInt使用方式
- 使用构造函数:BigInt(10)
- 直接在末尾加n:10n
console.log(BigInt(10) == 10n) // true
1.8.3 bigInt应用场景
- 超出number类型的场景,比如整数型的id
- bigInt也可以隐式转换为boolean类型,例如if(2n)
- bigInt和number数据转换的方式可以用BigInt(number)、Number(bigInt)
1.9 对象
1.9.1 理解对象的概念
- js是一个面向对象(广义)的语言。因为除了值类型,万物皆对象!(数组是对象,函数是对象,[狭义的]对象还是对象)
- js的对象是若干属性的集合,也就是js对象中都是属性,没有方法。
- 在别的开发语言(java、c#)中,对象都是new出来的,但是在js中,对象比较随意,数组是对象、函数是对象、对象还是对象。
- js的对象可以扩展任意属性,没有class类的约束。
1.9.2 对象和函数的关系
对象的创建方式有两种:
1.字面量直接赋值,例如 var obj = { name: 'a', age: 12 }
2.new一个构造函数,例如var obj = new Object();
直接量是一个语法糖,实际还是调用了new Object()
new Object()带有小括号,带有小括号的就是函数。因此可以说,对象是通过函数创建的,而函数也是一种对象,因此二者的关系有点类似于鸡生蛋和蛋生鸡。(可以通过原型和原型链作为理解)
1.10 检测数据类型typeof和instanceof
typeof主要用来检测基本数据类型。他可以检测所有的基本数据类型
number、boolean、string、undefined、symbol、bigInt;但是null是一个特例,他输出object。(原因是在第一版js设计的时候,只有5个基本类型,没有考虑到null,后来升级的版本为了兼容老版本,也没法修改)
而对于引用类型,它只能检测出对象object和函数function。对于对象,无法再细化(比如数组、日期)
instanceof用来判断具体的引用类型。
他的原理是:基于原型链检测,判断当前对象的原型链是否包含构造函数的protorype属性。
console.log([1, 2, 3] instanceof Array); // true console.log(/abc/ instanceof RegExp); // true console.log({} instanceof Object); // true console.log(function() {} instanceof Function); // true
1.11 数据类型间的隐式转换
算术运算符类:
+运算符:+运算符两边有一个是字符串,另一个是其他类型,则会隐式转换为字符串
eg: var str = 'a' + 1; var str = 1 + 'a';
-运算符:-运算符两边一个是字符串,另一个是数值,则会隐式转换为数值
eg:console.log( '12' - 2 ) // 10 -number console.log( 12 - '2' ) // 10 - number
比较运算符类:
==双等号:js做比较的时候会先进行隐式类型转换,再做比较(也是js的设计缺陷,三等号===避免了这个问题)
eg: console.log( '12' == 12 ) // true
其他比较运算符(例如>,<,>=,<=,!=):会根据运算符两边的类型自动转换为数值类型或字符串类型,再进行比较
eg: console.log( '2' > 1) // true console.log('b' > 'a') // true
二、堆栈内存空间
在js中,内存分为三种:
代码内存空间:用于存放可执行的代码
栈内存空间:用于存储基本数据类型,特点是占用内存小,调用频率高,通过值来访问
堆内存空间:用于存储引用数据类型,特点是占用空间大,大小不固定,通过引用的指针来访问
栈内存:像一个天梯
- 栈内存的特点是后入先出,例如栈底元素是a,栈顶元素是b,a先入栈,则最后出栈
var a = 1;
var b = '世界';
... ...
... | ... |
b | 世界 |
a | 1 |
堆内存:像一个二叉树
- 堆内存是在栈内存开辟一个空间,存放堆内存地址的指针
var people = { name: '张三', age: 18 };
function foo() {
console.log(window);
}
三、从堆栈内存的思维看深拷贝与浅拷贝
浅拷贝:引用类型在栈内存开辟一个新空间,将指针指向同一个引用。这样实际是一个堆内存的同一个对象同时被两个栈内存的变量指针引用。所以改变对象的值,另一个变量也会跟着改变
深拷贝:在堆内存中开辟一个新的空间,存放一个字面量与当前对象相同的值。这样就是两个独立的对象,互补干扰