JS基础01--数据类型及检测

137 阅读5分钟

JS数据类型

简单的分为原始值类型对象类型

原始值类型

number

  • 包含以下几种值
  1. 有效数:0、正负数、Infinity
  2. 非有效:NaN,NaN与任何值都不等,包括NaN
// isNaN([value])用于检测某变量是否为有效数字,@return:Boolean
// isNaN会将传入的[value]均隐式的转化为数字类型,再判断是否为有效数字
  • js数字类型的精度wenti --加链接

string

js没有char类型,在js中,用单引号/双引号/反引号,包起来的都是字符串

null

undefined

boolean

symbol

唯一值,执行Symbol创建处理的值永不相等。

  • 注意点:
  1. symbol不能进行运算,Symbol是构造函数,但不能new Symbol(…)。
  2. Symbol()返回的是基本类型值,不是Symbol类型所以不是Symbol实例,要产生实例必须Object(Symbol())。
  3. Symbol值可以强制转换为字符串,调toString()。
  4. 深克隆一个含有Symbol键的对象不会把Symbol键值对克隆。
  5. Object.getOwnPropertySymbols(obj)返回所有Symbol键名的数组。
  • symbol用途
  1. symbol用于对象唯一键名
//给对象添加属性
let obj = {...}
/**
 * 此处想给obj对象添加属性或方法
 * 但obj比较庞大,结构复杂,添加属性或方法的命名可能冲突
 * 借助Symbol添加一定不会重复
 */
let nameSpace = {
  up: Symbol('up方法'),
  down: Symbol('down方法')
}
obj[nameSpace.up] = function() {
  console.log('up');
}
//调用:
obj[nameSpace.up]()

// 这种方式创建symbol键值对是错误的,obj的symbol属性将永远取不到
let obj = {
   [symbol]: 'the value' 
}
  1. 对象属性保护
//对象属性保护
const SITE = Symbol('这是一个symbol')
class User {
    constructor (name, age) {
        this.name = name;
        this.age = age;
        this[SITE] = '**私有属性**'
    }
}
let user = new User('lisi',22)
for (const key in user) {
  //site属性遍历不出
  console.log(key);           
}
  1. 宏观标识管理(Vuex/Redux宏唯一)
  2. 排除字符串耦合
let grade = {
    jam: {js: 100, css: 60},
    jam: {js: 90, css: 70}
}
//后面的会被覆盖掉
console.log(grade.jam);     

//使用对象封装,并添加symbol唯一标识
let user1 = {
    name: 'jam',
    key: Symbol()
}
let user2 = {
    name: 'jam',
    key: Symbol()
} 
let grade = {
    //Symbol()作为键时,必须加中括号
    [user1.key]: {js: 100, css: 60},
    [user2.key]: {js: 90, css: 70}
}
console.log(grade[user1.key]);

  1. 消除魔术字符串
//如果有静态常量字符串作为判断标识,则每次都手动输入容易错误
//应该用Symbol宏变量将其包装成唯一值

function foo(param) {
    switch (param) {
        case 'param1': 
            ...
            break;
    }
}
//传参时进入函数的switch进行字符串判断
//程序是否能正常执行完全取决于字符串的拼写正确与否,属于强耦合
//若有不一致,不会报错,只是不能进入相应逻辑判断体,排错困难                                     
foo('param1')

//应该提取为唯一宏变量
const nameSpace = {
    param1: Symbol('param1')
}
function foo(param) {
    switch (param) {
        case nameSpace.param1: 
            ...
            break;
    }
}
// 若变量名书写错误会报错,很好排错
foo(nameSpace.param1)
  • Symbol常量用作底层接口 --加链接
  1. Symbol.hasInstance 在Function.prototype上,所有函数都能调用这个方法。是instanceof的底层原理。

  2. Symbol.iterator 迭代接口的实现,所有可迭代类和类的实例都有这个迭代接口。

  3. Symbol.toPrimitive 是一个函数,执行它可以干扰一个对象隐式转换为原始值时输出的结果。

  4. Symbol.toStringTag: 调用Object原型上的toString方法时返回指定的[Object xxx类型] (eg: [object Array])。自定类设置标签需要用到toStringTag属性。

bigint

Number.MAX/MIN_SAFE_INTEGER 之外的所有数字进行计算均需要加n(eg:xxxn-xxxn)

对象类型

标准对象(Object)

标准特殊对象

Array数组

RegExp正则

Date日期

Error通用错误

Math数学库

ArrayBuffer流

Map集合

Set集合

非标准特殊对象

各种Number、String、Boolean等包装类

可调用对象

对象上实现了call方法,即Function

数据类型检测

typeof

操作符检测

  • typeof [value]可以是变量、表达式。@return:String。取值可以是以下几种 'undefined' –未定义的变量或值

'boolean' –布尔类型的变量或值

'string' –字符串类型的变量或值

'number' –数字类型的变量或值

'object' –对象类型的变量或值,或null

'function' –函数类型的变量或值

'symbol' –唯一标识之的引用变量

'bigint' –大数变量或值,数字n

  • 弊端
  1. 不能检测null,typeof null = 'object'
  2. 除了可调用对象,其他对象均返回'object'
  3. typeof检测一个未被声明的变量,不会报错,返回'undefined'

instanceof

操作符检测 检测当前实例是否属于这个类(或者检测当前值是否为某个类的实例)。

用法: 值 instanceof 类(当前类的原型只要出现在了实例的原型链上就返回true)。

原理:构造函数就近调用Function.prototype上的Symbol.hasInstance方法(es5无法重写,但可以基于es6重写到构造函数的函数对象上),传入实例对象,返回查看实例对象是否在构造函数上的结果。若没有Symbol.hasInstance方法,则实例寻折原型链,对比查找的原型对象与构造函数的原型对象,一旦有则true;找到Object.prototype还没有,则false。原始值类型无法使用instanceof,统一为false,必须进行装箱操作后使用

function P() {}
let p = new P()
// 一下两种检测一样
p instanceof P;
P[Symbol.hasInstance](p)


// es6 重写Symbol.hasInstance
class Fn{
    static [Symbol.hasInstace](value) {
        ...
        return true
    }
}
// Fn[Symbol.hasInstance]优先调用Fn自己的
xx instanceof Fn     // true

它有很多缺陷

  • 缺陷1:不能识别原始值
// 原始值与包装值的转化称为 装箱 对象=Object(原始值)/拆箱 原始值=Symbol.toPrimitive/valueOf(对象)
let num1 = 10;
let num2 = Number(10);
num1 instanceof Number    // false,而原始值10是Number的一个实例。
num2 instanceof Number    // true

constructor

constructor属性值就是可以得到构造函数本身。proto和prototype上都存在constructor。在constructor没有被修改(重定向prototype等)的情况下,始终会是精确的类型检测

Object.prototype.toString.call

专门用于数据类型检测,没有瑕疵。几乎所有类的prototype上都有自己的toString方法,它们的toString都是用于实现将其实例转化为字符串的功能。而Object.prototype的toString并不是用于转化为对象的描述字符串的 普通自定义类的实例调用toString是检测数据类型,在没有设置[Symbol.toStringTag]时,始终是[object Object]。如果有的话,取有的那个值 Object.prototype.toString.call(任意内置类的实例),就是使得任意类的实例均调用Object.prototype上的toString进行类型检测,返回[object 任意类.constructor(内置类不受修改影响)]字符串 Object.prototype.toString.call(基本类型值),返回[object 对应的包装类.constructor(不受修改影响)]