# javascript数据类型

ECMAScript 标准定义了 7 种数据类型:

6种原始类型-基本数据类型(按值访问)

  • Null (js中的数据在底层是以二进制存储,如果前三位为0,那么就会判定为object,而null的所有都为0)
  • Undefined
  • 基本包装类型(自动创建的基本包装类型的对象—非Boolean,Number, String内置函数new出来的,对象只存代码的执行瞬间)
    • Number(基于 IEEE 754 标准的双精度 64 位二进制格式的值——数字、±Infinity、NaN)
    • String
    • Boolean
  • Symbol (ECMAScript 6新定义,实例是唯一且不可改变的)

引用类型: Object(包括Object/Array/Function/RegExp/Date)

# 什么是弱类型语言

  • 变量的类型就是其值的类型,也就是说变量当前的类型由其值所决定
  • 变量类型可以改变,a = 1 类型是number, a= "hello", 类型变为 string

# 类型检测的方式

  1. typeof;不能检测引用类型;
  2. instanceof;不能检测基本类型;只能判断是否是当前类型实例;不能判断到底属于哪种类型;
  3. Object.prototype.toString.call(); 推荐使用;
  4. constructor;易被修改,不能跨iframe;

# 优缺点

不同类型的优缺点 typeof instanceof constructor Object.prototype.toString.call
优点 使用简单 能检测出引用类型 基本能检测所有的类型(除了null和undefined) 检测出所有的类型
缺点 只能检测出基本类型(除了null) 不能检测出基本类型,且不能跨iframe constructor易被修改,也不能跨iframe IE6下,undefined和null均为Object

# 如何准确的判断数组类型

  • a instanceof Array => instanceof和constructor不能跨iframe,所以此方案不行!
  • Object.prototype.toString.call(a) === '[object Array]' => 应选方案

# 数组相关的常用方法

push/pop, shift/unshift, split/join, slice/splice/concat, sort/reverse, map/reduce, forEach, filter

slice: slice是指定在一个数组中的元素创建一个新的数组,即原数组不会变

var color = new Array('red','blue','yellow','black');
var color2 = color.slice(1,2);
alert(color);   //输出   red,blue,yellow,black
alert(color2);   //输出   blue;注意:这里只有第二项一个值
1
2
3
4

splice: splice是JS中数组功能最强大的方法,它能够实现对数组元素的删除、插入、替换操作,返回值为被操作的值

var color = new Array('red','blue','yellow','black');

  • splice删除:  color.splice(1,2) (删除color中的1、2两项);
  • splice插入:  color.splice(1,0,'brown','pink') (在color键值为1的元素前插入两个值);
  • splice替换:  color.splice(1,2,'brown','pink') (在color中替换1、2元素);

# 字符串相关的常用方法

indexOf/lastIndexOf/charAt, split/match/test, slice/substring/substr, toLowerCase/toUpperCase

# 对象的底层数据结构

js一切皆对象,所以,js的一些引用类型是 特殊封装的对象

# Object底层实现

Object => HeapObject => JSReceiver => JSObject

JS Object类图

  • V8里面所有的数据类型的根父类都是Object
  • Object派生HeapObject,提供存储基本功能
  • 往下的JSReceiver用于原型查找
  • 再往下的JSObject就是JS里面的Object
  • Array/Function/Date等继承于JSObject
  • 左边的FixedArray是实际存储数据的地方

# Array底层实现

Object => HeapObject => JSReceiver => JSArray // 看V8的源码

array 是在 object 的基础上继续封装而实现的,

动态数组,动态分配内存,跟java里的ArrayList, C++里的vector比较类似

  • push扩容:原数组长度的1.5倍+16
  • pop减容:容量大于等于length的2倍,容量减为数组长度

# Map

map 和 set

Object => HeapObject => JSReceiver => JSCollection

# symbol类型在实际开发中的应用

  1. 定义不需要对外操作和访问的属性
  2. 替代常量;不需要担心常量名字重复
  3. 定义类的私有属性/方法

# 不需要对外操作和访问的属性使用Symbol来定义

  • Object.keys()或者for...in不能枚举 Symbol 属性
  • JSON.stringify()将对象转换成JSON字符串的时候,Symbol属性也会被排除在输出内容之外:

# 使用Symbol来替代常量

const TYPE_AUDIO = Symbol()
const TYPE_VIDEO = Symbol()
const TYPE_IMAGE = Symbol()
1
2
3

替换成:

const TYPE_AUDIO = Symbol()
const TYPE_VIDEO = Symbol()
const TYPE_IMAGE = Symbol()
1
2
3

好处:不会重复!

# 使用Symbol定义类的私有属性/方法

const PASSWORD = Symbol()

class Login {
  constructor(username, password) {
    this.username = username
    this[PASSWORD] = password
  }

  checkPassword(pwd) {
      return this[PASSWORD] === pwd
  }
}

export default Login
1
2
3
4
5
6
7
8
9
10
11
12
13
14

注册和获取全局Symbol: Symbol.for()

let gs1 = Symbol.for('global_symbol_1')  //注册一个全局Symbol
let gs2 = Symbol.for('global_symbol_1')  //获取全局Symbol

gs1 === gs2  // true
1
2
3
4

# 变量在内存中的存储

基本数据类型在 栈中;对象在堆中,对象的引用在栈中

# 堆和栈的区别

  • 栈:基本数据类型和引用,值访问,存储的值大小固定,系统自动分配内存空间;空间小,运行效率高;后进先出;
  • 堆:存储引用的数据,按引用访问,存储的值大小不定,可动态调节,代码指定分配,空间大,运行效率低,无序存储

# 装箱拆箱操作

  • 装箱:把基本数据类型转化为对应的引用数据类型的操作;基本类型值=>对象,js内部实现;
  • 拆箱:将引用类型对象转换为对应的值类型对象:通过引用类型的valueOf()或者toString()方法来实现

# null和undefined的区别

  • null表示没有对象,即该处不应该有值
  • undefined表示缺少值,即此处应该有值,但没有定义

null和undefined转换成number数据类型时:

  • null 默认转成 0
  • undefined 默认转成 NaN

# 隐式类型转换

# 可能发生隐式类型转换的场景以及转换原则

弱类型语言,会把变量隐式转换成自己需要的类型

  • 自动转换 Boolean
    • if 语句 或者其他需要 Boolean 的地方
    • == 两个等号判断
  • 运算符
    • 在非 Numeber 类型进行数学运算符 - * / 时,会先将非 Number 转换成 Number 类型。
    • 运算符要考虑字符串的情况,在操作数中存在字符串时,优先转换成字符串,

# 应如何避免或巧妙应用

避免:

  • 先进行显示类型转换再应用
  • 判断相等时使用 === 而不是 ==