跳到主要内容

数据类型

数据类型

前言

JavaScript中,我们可以分成两种类型:

  • 基本类型
  • 复杂类型

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

一、基本类型

基本类型主要为以下6种:

  • Boolean
  • Number
  • String
  • Undefined
  • Null
  • Symbol: 表示一个独一无二的值,常用作对象的key具有唯一性

Undefined、Null、NaN

  1. Undefined:未申明、未赋值、没有返回值的函数(返回值为undefined)
// 未申明
console.log(msg1) // 报错
console.log(typeof msg1); // "undefined"
// 未赋值
var msg;
console.log(msg); // undefined
console.log(typeof msg); // "undefined"
  1. Null:空对象
var msg = null;
console.log(typeof msg); // "object"
console.log(null == undefined); // true

Number

浮点数值精度问题,最高精度为17位小数

0.1 + 0.2 = 0.300000000000004   // 而不是0.3

NaN:not a number 任何数与NaN都不相等,包括NaN本身,isNaN()方法

console.log(typeof NaN)  // "number"
console.log(0/0) // NaN
console.log(111/0) // Infinity
console.log(-11/0) // -Infinity

console.log(NaN/10); // NaN
console.log(NaN*10); // NaN
console.log(NaN-10); // NaN
console.log(NaN+10); // NaN

console.log(Number(undefined)); // NaN
console.log(Number(null)); // 0
console.log(Number('')); // 0
console.log(parseInt('')); // NaN
console.log(Number('123abc')); // NaN
console.log(parseInt('abc123')); // NaN
console.log(parseInt('123abc')); // 123

++、--操作符

const num = 1;
const a = num++; // a = num; num = num + 1;
const a = num--; // a = num; num = num - 1;
const a = ++num; // num = num + 1; a = num;
const a = --num; // num = num - 1; a = num;

常用方法

parseInt(1111, 2); // 15
parseInt('1111', 2); // 15

parseFloat(1111); // 1111, 同parseInt(1111, 10)
parseFloat('1111'); // 1111, 同parseInt('1111', 10)

(1111).toString(); // "1111"
(1111).toString(2); // "10001010111"

(112).toFixed(2); // "112.00"

位运算符在JS中的妙用

  1. 使用&运算符判断一个数的奇偶(&:对应的位都为1,则为1,否则为0)
// 偶数 & 1 = 0
// 奇数 & 1 = 1
// 1的二进制表示为: 00000000 00000000 00000000 00000001
console.log(2 & 1) // 0
console.log(3 & 1) // 1
  1. 使用 ~, >>, <<, >>>, |来取整
  • ~按位非:补码 = 负数 - 1;
  • <<左移
  • >>右移
  • |按位或:只要有一位为1,则为1
console.log(~6.8);  // -6-1 = -7
console.log(~~ 6.83) // 6; -(-7) - 1 = 6;
console.log(6.83 >> 0) // 6
console.log(6.83 << 0) // 6
console.log(6.83 | 0) // 6
// >>>不可对负数取整
console.log(6.83 >>> 0) // 6
  1. 使用^来完成值交换:(按位异或^:对应的位有且仅有一个1,则为1,否则为0)
// 原理:
// a 按位异或 自己 的结果为 0,即:a ^ a = 0;
// a 按位异或 0 的结果为 a ,即:a ^ 0 = a;
var a = 5
var b = 8
a ^= b // a = a ^ b;
b ^= a // b = a ^ b; => b = (a ^ b) ^ b; => b = a ^ (b ^ b); => b = a ^ 0; => b = a; => a = a ^ a = 0;
a ^= b // a = a ^ b; => a = 0 ^ b; => a = b;
console.log(a) // 8
console.log(b) // 5
  1. 使用&, >>, |来完成rgb值和16进制颜色值之间的转换
/**
* 16进制颜色值转RGB
* @param {String} hex 16进制颜色字符串
* @return {String} RGB颜色字符串
*/
function hexToRGB(hex) {
var hexx = hex.replace('#', '0x')
var r = hexx >> 16
var g = hexx >> 8 & 0xff
var b = hexx & 0xff
return `rgb(${r}, ${g}, ${b})`
}

/**
* RGB颜色转16进制颜色
* @param {String} rgb RGB进制颜色字符串
* @return {String} 16进制颜色字符串
*/
function RGBToHex(rgb) {
var rgbArr = rgb.split(/[^\d]+/)
var color = rgbArr[1]<<16 | rgbArr[2]<<8 | rgbArr[3]
return '#'+ color.toString(16)
}
// -------------------------------------------------
hexToRGB('#ffffff') // 'rgb(255,255,255)'
RGBToHex('rgb(255,255,255)') // '#ffffff'

String

转换方法

  • split():字符串转数组。(反过来, join(字符串):数组转字符串,并用指定字符串连接,默认以连接)

操作方法:(concat和slice是String和Array都有的方法)

  • concat(n个字符串):拼接n个字符串并返回,原字符串不变
  • slice(开始位置index1, 结束位置index2):返回从index1到index2(不含index2)的新的字符串,原字符串不变
    • index2不存在时,返回从index1到最后一位的新字符串;
    • index1/index2为负数时,数组长度为5,slice(-2,-3) = slice(-2+5, -3+5) = slice(3, 2)
    • index1/index2不为负数,且 index2 < index1时,返回 空字符串 ;
  • substring(index1, index2):与slice的区别:index1和index2中哪个比较小,哪个就是开始位置
  • substr(开始位置index, 字符个数n):返回从index开始之后的n个字符串,原字符串不变
  • trim():删除前后空格
  • toUpperCase()、toLowerCase()
  • replace(匹配字符/正则表达式, 替换字符):

查询/匹配方法:

  • charAt(index):指定index位置上的字符
  • charCodeAt(index):指定index位置上的字符的字符编码(对应 String.fromCharCode(n个字符串: 将n个字符编码转换成字符串)
  • match()
  • search()

replace()

使用正则
var str = 'Twas the night before Xmas...';
var newstr = str.replace(/xmas/i, 'Christmas');
console.log(newstr); // Twas the night before Christmas...

var re = /(\w+)\s(\w+)/;
var str = "John Smith";
var newstr = str.replace(re, "$2, $1");
// Smith, John
console.log(newstr);

Boolean

Boolean (布尔值)类型有两个字面值: true false

通过Boolean可以将其他类型的数据转化成布尔值

规则如下:

数据类型转换为 true 的值转换为 false 的值
String非空字符串""
Number非零数值(包括无穷值)0 、 NaN
Object任意对象null
UndefinedN/A (不存在)undefined

Symbol

Symbol (符号)是原始值,且符号实例是唯一、不可变的。符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险

let genericSymbol = Symbol();
let otherGenericSymbol = Symbol();
console.log(genericSymbol == otherGenericSymbol); // false

let fooSymbol = Symbol('foo');
let otherFooSymbol = Symbol('foo');
console.log(fooSymbol == otherFooSymbol); // false

二、引用数据类型

  • Object
  • Array
  • Function
  • Date
  • Math
  • 正则RegExp
  • Set: ES6,类似数组,但成员值都是唯一的。
    • 添加成员:const s = new Set(); s.add('a'),返回Set结构本身
    • delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
    • has(value):返回一个布尔值,表示该值是否为Set的成员。
    • clear():清除所有成员,没有返回值。
    • 长度:const s = new Set([1,2,3]); s.size
    • Set对象的key和value是一样的,都是成员值,所以keys()values()返回一样
    • WeakSet: Set的一种衍生,区别在于:成员只能是对象;是个弱引用,无法遍历
  • Map:ES6,类似对象,区别在于key值不限于字符串,可以是任意类型的值,比如对象,函数等等
    • set(key, value)
    • get(key): key为对象时,由于对象永远不等,所以看着key值一样,但也是不同的
    • delete(key)
    • has(key)
    • clear()
    • size属性
    • WeakMap: Map的一种衍生,区别在于:是个弱引用,作用于DOM操作,防止

Object

常用:

  • Object.assign()
  • Object.create(): 相当于new
  • Object.keys()
  • Object.values()
  • Object.entries()
  • Object.defineProperty()
  • Object.prototype.hasOwnProperty()

申明

var person = {name:'cly'}; // 对象字面量申明
var person1 = new Object(); // new一个构造函数申明
person1.name = 'cly';

内存指向

对象申明指向的是内存(堆内存)中的一个地址:两个对象就算值相同,也不相等

其他见<面向对象>

Array

JavaScript数组是一组有序的数据,但跟其他语言不同的是,数组中每个槽位可以存储任意类型的数据。并且,数组也是动态大小的,会随着数据添加而自动增长

let colors = ["red", 2, {age: 20 }]
colors.push(2)

一、属性:length —— 可以通过修改length改变数组

二、方法:

join(字符串)

数组转字符串,并用指定字符串连接,默认以  ,  连接

sort()

没有参数时,按ASCII码重排序; 有参数时,参数为一个函数(带两个参数的函数),返回负数目标排序

// 升序, v1小于v2, 返回负数
function compare(v1, v2) {
// return v1 - v2;
if(v1 < v2) {
return -1;
} else if(v1 > v2) {
return 1;
} else {
return 0;
}
}
reverse():反转数组
push(): 末尾插入,数组改变
pop(): 末尾移除,数组改变
unshift(): 开头插入,数组改变
shift(): 开头移除,数组改变
slice(起始位置index1, [ 结束位置index2 ])
  • 原数组不变,返回一个从index1到index2(不包括index2)的新数组
  • index2不存在时,返回从index1到最后一位的新数组;
  • index1/index2为负数时,数组长度为5,slice(-2,-3) = slice(-2+5, -3+5) = slice(3, 2)
  • index1/index2不为负数,且 index2 < index1时,返回 空数组 ;
splice(起始位置index, [ 要删除的项的个数n ], [ 要插入的任意个项value以逗号隔开 ]):
  • 数组改变,返回被删除的项组成的新数组(没有被删除的项,则返回 空数组)
  • 插入:n 为 0 时,splice(index, 0 , 任意个项),在index位置插入任意个项
  • 删除:n 不为0 ,value不存在时,删除从index开始之后的n个项
  • 替换:n 不为0,value存在时,用任意个项替换从index开始之后的n个项
concat(字符或数组):合并数组,返回新数组,原数组不变 
查找方法
  • indexOf(要查找的项value, [ 查找的起始位置index ]):从index位置开始向 后 查找value项,返回value在数组中的位置,不存在,则返回-1
  • lastIndexOf(要查找的项value, [ 查找的起始位置index ]):从index位置开始向 前 查找value项,返回value在数组中的位置,不存在,则返回-1
遍历方法(ES5, IE9+)
  • filter(function(item, index, array) { return 条件}):过滤出满足条件的项,组成新数组并返回
  • forEach(function(item, index, array) { // doSomething}):没有返回值,每项都doSomething
  • map(function(item, index, array) { // return doSomething}):返回每项doSomething后的结果
  • some(function(item, index, array) { return 条件}):数组中任一项返回true,则返回true
  • every(function(item, index, array) { return 条件}):数组中每一项都返回true,则返回true
归并(递归+合并)方法:原数组不变    (ES5, IE9+)
  • reduce(callback, [initialValue])

    callback: function(previous,  current,  index,  array) { // return newItem}

    callback的四个参数:前一个值,当前值,索引值,数组本身 initialValue:可选参数,初始值 initialValue存在时,第一个previous为initialValue,第一个current为数组第一个元素; initialValue不存在时,第一个previous是数组第一个元素,第一个current为数组第二个元素 将callback的返回值作为下一次递归的previous

var arr=[1,[[[7,2],8,9],3]];
var newArr = [];
// 普通递归
function fn(arr) {
for (var i = 0; i < arr.length; i++) {
if(arr[i].length) {
fn(arr[i]);
} else {
newArr.push(arr[i]);
}
}
}
fn(arr) // [1, 7, 2, 8, 9, 3]

// reduce递归
var flatD = args => args.reduce((r, n) => r.concat(Array.isArray(n) ? flatD(n) : n),[])
flatD(arr) // [1, 7, 2, 8, 9, 3]
  • reduceRight(callback, [initialValue]),同reduce,区别在于从最后一项开始往前遍历/递归

Function

函数实际上是对象,每个函数都是 Function类型的实例,而 Function 也有属性和方法,跟其他引用类型一样

函数存在三种常见的表达方式:

  • 函数声明
// 函数声明
function sum (num1, num2) {
return num1 + num2;
}
  • 函数表达式
let sum = function(num1, num2) {
return num1 + num2;
};
  • 箭头函数

函数声明和函数表达式两种方式

let sum = (num1, num2) => {
return num1 + num2;
};

函数申明、赋值

function a() {}

const a = function(){}

函数内部属性:arguments、this

  • arguments:包含函数所有参数的类数组对象,要转换成数组 => Array.prototype.slice.call(arguments,0)
  • arguments.callee:一个指针,指向arguments对象所属的函数(常用于递归的解耦合)

函数方法:

  • func.apply(thisArg, [argsArray]);

    后面跟一个参数:实参组成的数组 返回:调用的方法的返回值,若该方法没有返回值,则返回undefined

  • fun.call(thisArg, arg1, arg2, ...);

    后面跟 n 个参数:实参 返回:调用的方法的返回值,若该方法没有返回值,则返回undefined

  • fun.bind(thisArg[, arg1[, arg2[, ...]]]);

    后面跟 n 个参数:实参 返回:原函数的拷贝函数

立即执行函数

递归

RegExp

限字符(量词/贪婪量词):

  • *:0次或多次
  • +:1次或多次
  • ?:0次或1次
  • {n}:n次
  • {n,}:至少n次
  • {n,m}:n到m次
  • 限字符后加?:变成非贪婪模式,尽可能少的匹配字符

其他元字符:

  • ^a:以a开头的字符串
  • a$:以 a结尾的字符串
  • \d:数字                         (\D:非数字)
  • \s:空格                          (\S:非空格)
  • \w:[A-Za-z0-9_]            (\W:A-Za-z0-9_
  • \b:单词边界                    (\B:非单词边界)
  • a|b:a或b
  • [abc]:a、b、c三个中的任意一个字符
  • abc:非a、b、c三个中的任意一个字符
  • [a-z]:a到z的所有字母
  • a-z:非a到z的所有字母

实例属性:

  • lastIndex:下一个匹配项的字符位置
  • source:返回正则表达式的字符串,相当于toString()方法(valueOf(),返回正则表达式本身 - RegExp类型)
  • 其他:global、ignoreCase、multiline

实例方法:

test(字符串):指定字符串是否匹配正则
/\d{2}/.test('153455abc');   // true,含有2个数字
/\d{2}$/.test('153455abc'); // false,不是以2个数字结尾
exec(字符串):返回 捕获组 组成的数组
var text = 'mom and dad and baby';
var pattern = /mom( and dad( and boby)?)?/gi

var matches = pattern.exec(text); // ['mom and dad', ' and dad', undefined]
matches.index; // 0,匹配项在字符串中第一次出现的位置
matches.input; // mom and dad and baby,应用了这个正则的字符串text

Date

常用

Date.now()
new Date().getTime()
时间格式转换
/**
* 日期时间格式化
* @param {Date} time 时间
* @param {String} format 格式化内容 'yyyy-MM-dd hh:mm:ss'
* @return {String} 格式化后的时间 '2016-05-04 12:12:00'
*/
const dateFormat = (time, format = 'yyyy-MM-dd hh:mm:ss') => {
if (!time) return 'null';

let value = time;

if (typeof value === 'number') {
if (String(time).length < 10) {
value = Date.now();
} else if (String(time).length === 10) {
value = time * 1000;
}
}

const date = new Date(value);

const map = {
M: date.getMonth() + 1, // 月份
d: date.getDate(), // 日
h: date.getHours(), // 小时
m: date.getMinutes(), // 分
s: date.getSeconds(), // 秒
q: Math.floor((date.getMonth() + 3) / 3), // 季度
S: date.getMilliseconds(), // 毫秒
};

return format.replace(/(y+|M+|d+|h+|m+|s+|q+|S+)/g, (all, t) => {
t = t.length > 1 ? t[0] : t;
let v = map[t];
if (v !== undefined) {
if (all.length > 1) {
v = `0${v}`;
v = v.substr(v.length - 2);
}
return v;
} if (t === 'y') {
return (String(date.getFullYear())).substr(4 - all.length);
}
return all;
});
};

export default dateFormat;

Math

取整

  • Math.ceil():向上取整
  • Math.floor():向下取整
  • Math.round():四舍五入取整

取随机数

  • Math.random():随机取得0到1(不含1)的小数
Math.ceil(Math.random()*10);      // 获取从1到10的随机整数 ,取0的概率极小。
Math.round(Math.random()); //可均衡获取0到1的随机整数。
Math.floor(Math.random()*10); //可均衡获取0到9的随机整数。
Math.round(Math.random()*10); //基本均衡获取0到10的随机整数,其中获取最小值0和最大值10的几率少一半,因为0只能是0~0.5取得,10只能是9.5~10取得

// 取n到m之间的随机 整 数,!!!包含m
function getRandom(n, m) {
return Math.floor(Math.random() * (m - n + 1)) + n // 或 parseInt(Math.random() * (m - n + 1)) + n
}

其他

  • Math.abs():取绝对值
  • Math.sqrt():取平方根
  • Math.sign():正负数,正数为1,负数为-1
  • Math.pow(n, m):n的m次方

Global全局对象

  • encodeURIComponent()
  • decodeURIComponent()
  • eval()

三、存储区别

基本数据类型和引用数据类型存储在内存中的位置不同:

  • 基本数据类型存储在栈中

  • 引用类型的对象存储于堆中

当我们把变量赋值给一个变量时,解析器首先要确认的就是这个值是基本类型值还是引用类型值

下面来举个例子

基本类型

let a = 10;
let b = a; // 赋值操作
b = 20;
console.log(a); // 10值

a的值为一个基本类型,是存储在栈中,将a的值赋给b,虽然两个变量的值相等,但是两个变量保存了两个不同的内存地址

下图演示了基本类型赋值的过程:

)

引用类型

var obj1 = {}
var obj2 = obj1;
obj2.name = "Xxx";
console.log(obj1.name); // xxx

引用类型数据存放在堆中,每个堆内存对象都有对应的引用地址指向它,引用地址存放在栈中。

obj1是一个引用类型,在赋值操作过程汇总,实际是将堆内存对象在栈内存的引用地址复制了一份给了obj2,实际上他们共同指向了同一个堆内存对象,所以更改obj2会对obj1产生影响

下图演示这个引用类型赋值过程

小结

  • 声明变量时不同的内存地址分配:
    • 简单类型的值存放在栈中,在栈中存放的是对应的值
    • 引用类型对应的值存储在堆中,在栈中存放的是指向堆内存的地址
  • 不同的类型数据导致赋值变量时的不同:
    • 简单类型赋值,是生成相同的值,两个对象对应不同的地址
    • 复杂类型赋值,是将保存对象的内存地址赋值给另一个变量。也就是两个变量指向堆内存中同一个对象