[toc]
JS 数据类型
每种编程语言都具有内建的数据类型,而根据使用数据的方式从两个不同的维度将语言进行分类。
动态/静态:
动态类型:运行过程中需要检查数据类型
静态类型:使用之前就需要确认其变量数据类型
强/弱:
强类型:不支持隐式类型转换
弱类型:支持隐式类型转换
隐式类型转换 : 在赋值过程中, 编译器会把 int 型的变量转换为 bool 型的变量
每个变量只不过是一个用于保存任意值的命名占位符。
数据类型分类
六种数据类型:
数值 (Number)、字符串 (String)、布尔 (Boolean)、空 (Null)、未定义 (Undefined)、对象 (Object)。
数据类型分为原始类型与引用类型两大类,除了对象以外其他五个基础数据类型都是原始类型。
ECMAScript 有 8 种数据类型
- Undefined
- Null
- Boolean
- String
- Number
- Symbol (ES6 新增)
- BigInt (ES2020 新增)
- Object (基本引用类型、)
根据数据存储位置的不同,我们将 JS 数据类型分为两大类:
基本数据类型(primary) 存放在栈内存中,类型 1-7
复杂数据类型/引用类型 存放在堆内存中, 类型 8
1 | let sy = Symbol('789'), |
特例分析: null 值表示一个空对象指针。所以针对 typeof null 返回了一个”object”。
原始类型
数值 (Number)、字符串 (String)、布尔 (Boolean)、空 (Null)、未定义 (Undefined)。
引用类型
对象 (Object)又有 Array、Date、Math 等等。
原始类型与引用类型的区别
赋值的区别
原始类型赋值,引用类型赋的是引用。原始类型的赋值方式:
1
2
3
4
5let str1 = "Hello";
let str2 = str1; // str2: hello
str1 = "World";
console.log(str1) // World
console.log(str2) // Hello引用类型的赋值方式与原始类型的不一样
- obj1 可以链接到内存空间{name: “John”}(obj1 只是引用{name: “John”}),
- 把 obj1 赋值给 obj2,就是可以让 obj2 链接到 obj1 引用的内存空间{name: “John”},
- 当把 Korey 赋值给 obj1.name,修改的是 obj1 引用的内存空间{name: “John”}的 name 属性,也就是说 obj1 引用的内存空间{name: “John”}变成了 obj1 引用的内存空间{name: “Korey”}。
- 当输出 obj1 的 name 时访问的是 obj1 引用的内存空间,输入 obj2 的 name 访问的也是 obj2 引用的内存空间,结果是一样的是因为 obj1 和 obj2 引用的内存空间是一样的。
1
2
3
4
5
6
7let obj1 = {
name: "John"
};
let obj2 = obj1;
obj1.name = "Korey";
console.log(obj1.name); // Korey
console.log(obj2.name); // Korey比较的区别
原始类型比较的是值,引用类型比较的是是否指向同一个对象。1
2
3let str1 = "hello";
let str2 = "hello";
console.log(str1 === str2); // true引用类型的比较,比较的是是否指向同一个对象。
例 1 类比:班里出现同名的情况,同名不同人。
1
2
3
4
5
6
7let obj1 = {
name: "John"
};
let obj2 = {
name: "John"
};
console.log(obj1 === obj2); // false- obj1 引用的是第一个内存空间{name: “John”},obj2 引用的是第二个内存空间{name: “John”}
- 因为它们引用的内存空间不一样所以是 false
例 2
1
2
3
4
5let obj1 = {
name: "John"
};
let obj2 = obj1;
console.log(obj1 === obj2); // true- obj1 引用的是内存空间{name: “John”}
- 把 obj1 赋值给 obj,让 obj2 链接到 obj1 引用的内存空间{name: “John”}
- 因为它俩引用的是同一个内存空间,所以是 true
传参的区别
原始类型的传参不管怎样传都不会影响到外面的值的变化。
引用类型的传参,不管在内部还是外部,它指向都是同一个内存空间,同一个对象。把 number 传到 fun 函数里面,然后把 100 赋值给 number,所以 fun 里的 x 是 100,但不影响外面的 number。
1
2
3
4
5
6
7function fun(x) {
x = 100;
console.log(`x的值是${x}`);
}
let number = 10;
fun(number); //x的值是100
console.log(`number的值是${number}`); //number的值是10因为函数内部的受到影响了外部的也会被影响。
1
2
3
4
5
6
7
8
9
10let obj = {
name: "John"
};
function fun(o) {
o.name = "Koke";
console.log(`o的名字是${o.name}`); // Koke
}
fun(obj);
console.log(`obj的名字是${obj.name}`); // Koke
原始类型与引用类型的类型检测
- 原始数据类型检测:typeof(值)
经常用来检测一个变量是不是最基本的数据类型
对于 null 来说,虽然它是基本类型,但是会显示 object,这是一个存在很久了的 Bug引用类型用 typeof()检测的结果都是 object,所以没啥用,可以用 值 instanceof(类型)来检测。1
2
3
4
5
6
7
8
9
10let num = 100;
let str = "Hello World";
let und; // undefined 不用赋值就是undefined
let nu = null;
let bol = true;
console.log(typeof (num)); // number
console.log(typeof (str)); // string
console.log(typeof (und)); // undefined
console.log(typeof (nu)); // !! 不是null 是object
console.log(typeof (bol)); // boolean1
2
3
4
5
6let arr = [1, 2, 3];
let reg = /123/;
let date = new Date();
console.log(typeof (arr)); // object
console.log(typeof (reg)); // object
console.log(typeof (date)); // object - 引用数据类型检测:值 instanceof(类型)
用来判断某个构造函数的 prototype 属性所指向的对象是否存在于另外一个要检测对象的原型链上。简单说就是判断一个引用类型的变量具体是不是某种类型的对象。Array RegExp Date … extends Object; 它们都继承成于 Object,所以都是 true。1
2
3
4
5
6
7
8
9
10
11let arr = [1, 2, 3];
let reg = /123/;
let date = new Date();
console.log(arr instanceof Array); // true
console.log(reg instanceof RegExp); // true
console.log(date instanceof Date); // true
// Array RegExp Date ... extends Object; 它们都继承成于Object 所以都是true
console.log(date instanceof Object); // true
console.log(reg instanceof Object); // true
console.log(date instanceof Object); // true
// !!! console.log(null instanceof Object); // false - Object.prototype.toString.call(xx)
若参数(xx)不为 null 或 undefined,则将参数转为对象,再作判断。转为对象后,取得该对象的 [Symbol.toStringTag] 属性值(可能会遍历原型链)作为 tag,然后返回 “[object “ + tag + “]” 形式的字符串。
针对基本数据类型,通过装箱过程转为对象类型。
1 | Object.prototype.toString.call(null) //[ojbect Null] |
通过 Object.prototype.toString 可以将数据类型很容易的分开。但是,每次进行判断的时候,多了一堆额外的信息。所以,我们可以对该方法进行改进。
1 | function getDataType(type) { |
例 1 数组存储学生列表
- 数组 StudentList
- 点击按钮,将表单中【姓名】和【年龄】赋值给 Student 对象,并 push 到 StudentList 中。
1 | <div> |
1 | let inputName = document.querySelector(".name"); |
课后练习
- 制作一个学生列表,要求如下:
- 在表单中输入【姓名】和【年龄】,点击添加【添加】按钮,创建 Student 对象。
- 将 Student 对象添加到列表中(ul li)。
1 | <div> |
1 | let ul = document.querySelector('.student-list'); |