参考文章:

  1. 深拷贝与浅拷贝的区别,实现深拷贝的几种方法

基本数据类型

JavaScript 有5种基本数据类型:Undefined、Null、Boolean、Number 和 String。

Undefined类型

Undefined 类型只有一个值 undefined,如果使用var定义的变量未初始化值,其默认的值为 undefined。

  1. var str;
  2. console.log(str); // undefined

Null类型

Null 类型只有一个值 null,表示空对象,所以typeof操作 null 值的结果是”object”。

Boolean类型

Boolean 类型有两个值 true 和 false,分别表示”是”和”否”。

Number类型

Number 类型取值是所有数值,不区分整型数值和浮点型数值,JavaScript 采用 IEEE-754 浮点数表示法,这是一种二进制表示法,可以精确地表示分数,但是不能精确表示类似0.1这样简单的数字,也就是说,在进行涉及浮点数的计算时,会出现类似以下问题:

  1. var x = .3 - .2;
  2. var y = .2 - .1;
  3. console.log(x == y); //false
  4. console.log(x == .1); //false
  5. console.log(y == .1); //false

如果一定要进行浮点型数值计算,可以将相加的两个浮点数转为整数,最后再将相加结果转为浮点数:

  1. // 普通的浮点计算
  2. var sum1 = 0.1 + 0.2;
  3. console.log(sum1); // 0.30000000000000004
  4. console.log(sum1 === 0.3); // false
  5. // 特殊处理的浮点计算
  6. var sum2 = 0.1 * 10 + 0.2 * 10;
  7. console.log(sum2 / 10); // 0.3
  8. console.log(sum2 === 0.3); // true

可以使用指数记数法表示浮点型直接量,即在实数后跟字母 e 或 E,后面再跟正负号,其后再加一个整型的指数。这种记数方法表示的数值,是由前面的实数乘以10的指数次幂。例如3.14e2表示的就是314。

JavaScript 定义的数值还可以是十进制、八进制和十六进制值,定义的所有进制的值最终都会被转换为十进制值。

  1. var n1 = 10; // 十进制,值10
  2. var n2 = 0o10; // 八进制,值8
  3. var n3 = 0x10; // 十六进制,值16

Number 有一个特殊值 NaN,表示一个本来要返回数值的操作值未返回数值的情况。

  1. console.log(Number('a') + 10); // NaN

判断一个值是否为 NaN 只能通过isNaN()方法。

  1. console.log(isNaN('a')); // false
  2. console.log(isNaN(1 + 'n')); // true

注:NaN == NaN返回 false。

String类型

声明字符串:var str = 'WJT'

字符串中可以嵌入字符字面量,例如:var str = 'WJT\nHello',其中,\n表示换行符。

字符串属于类数组对象(类数组对象的特点:可以通过索引号访问指定位置元素,有 length 属性):

  1. var l = 'ABC'.length;
  2. console.log(l); // 输出3

可以使用+号拼接字符串:var str = 'AB' + 'C'

使用字符串的concat()方法也可以拼接字符串:var str = 'A'.concat('B'),这句代码返回一个由”A”拼接”B”形成的新字符串——“AB”。

字符串常用的属性和方法参考:JavaScript高级程序设计实记——数组与字符串API

引用数据类型

Object类型

Object 类型(对象)其实是一个键值对集合,可以直接使用花括号创建对象,然后定义属性或方法。所有引用类型都基于 Object 类型。

  1. var obj = {
  2. name: 'WJT', // 定义属性
  3. // 定义方法
  4. sayHello: function(name) {
  5. console.log('Hello, ' + name);
  6. }
  7. };
  8. obj.id = 123; // 外部定义属性
  9. console.log(obj.name); // 访问属性,输出: "WJT"
  10. obj.sayHello('WJT20'); // 调用方法,输出: "Hello, WJT20"

Date类型

Date类型就是日期时间的访问操作接口,使用new关键字可以创建一个Date实例对象。

  1. var time = new Date();

将日期对象转为数值可以使用”+”号,也可以使用valueOf()方法获取,本人比较推荐使用第二种:

  1. var time = new Date();
  2. console.log(+time); // 1533089048343
  3. console.log(time.valueOf()); // 1533089048343

Date 日期对象常用 API:

  1. dateObj.getFullYear():返回创建时的年份;
  2. dateObj.getMonth() + 1:从0开始计起,返回0实际上是一月;
  3. dateObj.getDate():返回创建时的日期;
  4. dateObj.getHours():返回创建时的小时;
  5. dateObj.getMinutes():返回创建时的分钟;
  6. dateObj.getSeconds():返回创建时的秒数;
  7. dateObj.getMilliseconds():返回创建时的毫秒数;
  8. dateObj.getDay():返回创建时的星期(数值0-6)。

函数实现:返回”2018-08-01 10:48:24 Wed”格式的日期字符串。

  1. function getCurrentTime() {
  2. var time = new Date();
  3. var ary = [];
  4. ary.push(time.toISOString().substr(0, 10));
  5. ary.push(time.toTimeString().match(/[0-9]+:[0-9]+:[0-9]+/)[0]);
  6. ary.push(time.toUTCString().match(/\w+,/)[0].slice(0, -1));
  7. return ary.join(' ');
  8. }

iOS 日期转换的坑点:

  1. var date1 = (new Date('2021-03-05 17:40')).valueOf();
  2. console.log(date1); // iOS上返回NaN,其他设备返回1614937200000
  3. var date2 = (new Date('2021/03/05 17:40')).valueOf();
  4. console.log(date2); // 所有设备都能返回1614937200000

Array类型

创建数组有两种方式:使用new Array()和使用数组字面量表示法。JavaScript 数组不限制数组元素的数据类型保持统一,一个数组可以包含各种类型的值。

数组实例对象具有一个 length 属性返回数组的元素个数,length 属性可以手动修改,例如想要重置一个数组只要将 length 属性值设为0。数组元素可以通过”[]”加索引号访问。

  1. var arr1 = new Array(5); // 创建一个包含5个空值的数组
  2. var arr2 = new Array('a', 1, true); // 创建数组并填充元素
  3. var arr3 = ['a', 1, true]; // 推荐使用数组字面量写法
  4. console.log(arr3.length); // 输出: 3
  5. console.log(arr3[0]); // 输出: "a"
  6. console.log(arr3[arr3.length - 1]); // 访问最后一个元素: true
  7. arr3.length = 0;
  8. console.log(arr3[0]); // 输出: undefined

数组常用的属性和方法参考:JavaScript高级程序设计实记——数组与字符串API

Math类型

Math类型包含了一些常用于数学计算的属性和方法,常用的属性常量有:Math.PI,即获取π的值:3.141592653589793。

Math.min(num1, ...)Math.max(num1, ...),接收一段数值参数,分别返回其中的最大值和最小值。

  1. var num3 = 10;
  2. var num4 = 5;
  3. console.log(Math.min(num3, num4)); // 输出: 5
  4. console.log(Math.max(num3, num4)); // 输出: 10

舍入方法:

  1. Math.ceil(num):向上舍入;
  2. Math.floor(num):向下舍入;
  3. Math.round(num):四舍五入。

Math.random()用于获取大于等于0小于1的浮点数,获取a到b之间的整数使用语句:Math.floor(Math.random() * (b - a + 1) + a)

  1. var num7 = Math.floor(Math.random() * 10); // 获取大于等于0小于10的任意整数
  2. var num1 = Math.floor(Math.random() * (10 - 5 + 1) + 5); // 获取大于等于5小于11(5到10之间)的任意整数

基本数据类型与引用数据类型的区别

两种类型的区别是:存储位置不同。

具体体现在:

  • 基本数据类型直接存储在栈(stack)中的简单数据段,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储;
  • 引用数据类型存储在堆(heap)中的对象,占据空间大、大小不固定。如果存储在栈中,将会影响程序运行的性能;引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。

深拷贝与浅拷贝

区分深拷贝与浅拷贝的方法,举个例子,A拷贝B,改变A的值,若B也跟着改变,则此拷贝过程为浅拷贝,否则为深拷贝。深拷贝和浅拷贝发生于引用数据类型,拷贝对象通常是 Object 或 Array。

内存存取原理

在进入深拷贝和浅拷贝主题前,先要了解下数据是如何存储到内存中的。

对于基本数据类型,数据名值都是存储于栈内存中,当进行复制操作时,栈内存会新开辟一个内存,正因如此,两者互不干预。示例:

  1. var a = 1;
  2. var b = a;
  3. console.log(a, b); // 1 1
  4. b = 2;
  5. console.log(a, b); // 1 2

可以看到,将 b 赋值为2,a 的值也不会变为2。

引用数据类型则不一样,当定义一个引用类型的数据时,栈内存会提供一个引用的地址指向堆内存中的值,当进行复制操作时,复制拿到的是引用的地址而不是真正的值,如果这个时候改变副本的某个值,直接改变的是堆内存中的值,这就导致引用此地址的另一个数据里的值跟着一起变,这就达不到深拷贝的效果。

如何实现深拷贝

实现深拷贝有以下两种方法:

  1. 使用递归(这里不考虑算法性能),示例代码如下: ```javascript function deepClone(obj) { var clonedObj = obj.constructor === Array ? [] : {}; if (typeof obj === ‘object’) {
    1. for (var key in obj) {
    2. if (obj[key] && typeof obj[key] === 'object') {
    3. // 如果子元素为对象,则递归复制
    4. clonedObj[key] = deepClone(obj[key]);
    5. } else {
    6. // 非引用类型直接复制
    7. clonedObj[key] = obj[key];
    8. }
    9. }
    } return clonedObj; }

var data1 = { name: ‘WJT20’, grade: [100, 95, 90] }; var data2 = deepClone(data1); data2.grade = [60, 70, 80]; console.log(data1, data2); // {name:”WJT20”,grade:[100,95,90]} {name:”WJT20”,grade:[60,70,80]}

  1. 2. 使用`JSON.parse()``JSON.stringify()`方法,这种方法比第一种方法简单的多,但是这种方法也有缺陷,那就是转化不了 FunctionDate 等类型数据:
  2. ```javascript
  3. function deepClone(obj) {
  4. var clonedObj;
  5. var json;
  6. if (obj && typeof obj === 'object') {
  7. json = JSON.stringify(obj);
  8. clonedObj = JSON.parse(json);
  9. }
  10. return clonedObj;
  11. }
  12. var data1 = {
  13. name: 'WJT20',
  14. grade: [100, 95, 90]
  15. };
  16. var data2 = deepClone(data1);
  17. data2.grade = [60, 70, 80];
  18. console.log(data1, data2); // {name:"WJT20",grade:[100,95,90]} {name:"WJT20",grade:[60,70,80]}