最新的 ECMAScript 标准定义了 7 种数据类型以及检测类型方式

一、6种原始数据类型

基本数据类型(按值访问)

  1. Null(js中的数据在底层是以二进制存储,如果前三位为0,那么就会判定为object,而null的所有都为0)
  2. Undefined

null和undefined的区别:
null是js的关键字,用于空对象,空指针;表示该处不应该有值
undefined不是js的关键字,表示此处应该有一个值,但是还没定义。

5+null // 5
5+undefined //NaN

var x;
console.log(x) //undefined

基本包装类型(当读取基本类型的某一个值时,后台会为该创建 String/Boolean/Number 类的一个实例类,在实例上调用指定的方法。如str.substring(2),然后销毁)
vascript之基本包装类型(Boolean,Number,String)基础篇

var s1='some text';
var s2=s1.substring(2);//'me text'

后台的处理:

  • 创建 String/Boolean/Number 类的一个实例。var s1=new String(‘some text’);
  • 在实例上调用指定的方法。var s2=s1.substring(2);//‘me text’
  • 销毁这个实例。s1=null
  • 注:自动创建的基本包装类型的对象,只存在于代码执行的一瞬间,然后被立即销毁。这意味着我们不能在运行时为基本类型值添加属性和方法。
  1. Number

  2. Boolean

  3. String

  4. 引用类型:Object(包括Object/Array/RegExp/Date/null)
    使用new 操作符创建的引用类型的实例,在执行流离开当前作用域之前都一直保存在内存中。

二、ES6新定义的Symbol:(唯一标识符、可以作为对象的稀有属性,防止被重名改写)

  1. Symbol值通过Symbol函数生成,typeOf的运算结果也是symbol
let s = Symbol();

typeof s
// "symbol"
  1. Symbol函数前不能使用new命令,否则会报错。这是因为生成的Symbol是一个原始类型的值,不是对象。也就是说,由于Symbol值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。
  2. Symbol函数可以接受一个字符串作为参数,表示对Symbol实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。即使参数是一样的,返回值都是不相等的。
var s1 = Symbol('foo');
var s2 = Symbol('bar');
var s3 = Symbol('foo')

s1 // Symbol(foo)
s2 // Symbol(bar)
s3 // Symbol(foo)

s1===s3 //false

s1.toString() // "Symbol(foo)"
s2.toString() // "Symbol(bar)"
  1. 如果 Symbol 的参数是一个对象,就会调用该对象的toString方法,将其转为字符串,然后才生成一个 Symbol 值。
 const obj = {
  toString() {
    return 'abc';
  }
};
const sym = Symbol(obj);
sym // Symbol(abc)
  1. Symbol值可以显式转为字符串、布尔值,但是不能进行运算、不能转换成数值。
var sym = Symbol('My symbol');

String(sym) // 'Symbol(My symbol)'
sym.toString() // 'Symbol(My symbol)'

Boolean(sym) // true
!sym  // false

Number(sym) // TypeError
sym + 2 // TypeError


"your symbol is " + sym
// TypeError: can't convert symbol to string
`your symbol is ${sym}`
// TypeError: can't convert symbol to string
  1. Symbol作为属性名时,不能重名、不能用点运算符。
var mySymbol = Symbol();

// 第一种写法
var a = {};
a[mySymbol] = 'Hello!';
a.mySymbol2 = 'Hello2!';//用点运算符的话,实际上对象的这个属性名就会变成mySymbol这个字符串,而不是Symbol值。
a[mySymbol2] // undefined
a['mySymbol2'] // "Hello2!"

// 第二种写法
var a = {
  [mySymbol]: 'Hello!'
};

// 第三种写法
var a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });

// 以上写法都得到同样结果
a[mySymbol] // "Hello!"
  1. 常量使用Symbol值最大的好处,就是其他任何值都不可能有相同的值了,因此可以保证上面的switch语句会按设计的方式工作。
    还有一点需要注意,Symbol值作为属性名时,该属性还是公开属性,不是私有属性。
const COLOR_RED    = Symbol();
const COLOR_GREEN  = Symbol();

function getComplement(color) {
  switch (color) {
    case COLOR_RED:
      return COLOR_GREEN;
    case COLOR_GREEN:
      return COLOR_RED;
    default:
      throw new Error('Undefined color');
    }
}
  1. 消除魔术字符串。
    魔术字符串指的是,在代码之中多次出现、与代码形成强耦合的某一个具体的字符串或者数值。风格良好的代码,应该尽量消除魔术字符串,该由含义清晰的变量代替。
function getArea(shape, options) {
  var area = 0;

  switch (shape) {
    case 'Triangle': // 魔术字符串
      area = .5 * options.width * options.height;
      break;
    /* ... more code ... */
  }

  return area;
}

getArea('Triangle', { width: 100, height: 100 }); // 魔术字符串

上面代码中,字符串“Triangle”就是一个魔术字符串。它多次出现,与代码形成“强耦合”,不利于将来的修改和维护。

常用的消除魔术字符串的方法,就是把它写成一个变量。

var shapeType = {
  triangle: 'Triangle'
};

function getArea(shape, options) {
  var area = 0;
  switch (shape) {
    case shapeType.triangle:
      area = .5 * options.width * options.height;
      break;
  }
  return area;
}

getArea(shapeType.triangle, { width: 100, height: 100 });

如果仔细分析,可以发现shapeType.triangle等于哪个值并不重要,只要确保不会跟其他shapeType属性的值冲突即可。因此,这里就很适合改用Symbol值。

const shapeType = {
  triangle: Symbol()
};
  1. Symbol 作为属性名,该属性不会出现在for…in、for…of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。但是,它也不是私有属性,有一个Object.getOwnPropertySymbols方法,可以获取指定对象的所有 Symbol 属性名。
    Object.getOwnPropertySymbols方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。
var obj = {};
var a = Symbol('a');
var b = Symbol('b');

obj[a] = 'Hello';
obj[b] = 'World';

var objectSymbols = Object.getOwnPropertySymbols(obj);

objectSymbols
// [Symbol(a), Symbol(b)]

另一个新的API,Reflect.ownKeys方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。

let obj = {
  [Symbol('my_key')]: 1,
  enum: 2,
  nonEnum: 3
};

Reflect.ownKeys(obj)
// [Symbol(my_key), 'enum', 'nonEnum']
  1. Symbol.for()重新使用一个相同的Symbol值,它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的Symbol值。如果有,就返回这个Symbol值,否则就新建并返回一个以该字符串为名称的Symbol值。Symbol.for()与Symbol()都会生成新的Symbol值;Symbol.for()先检查给定的key是否存在,存在就返回相应的值,否则新建一个Symbol值,而Symbol每次都是新建一个的。
var s1 = Symbol.for('foo');
var s2 = Symbol.for('foo');
s1===s2   //true
Symbol("bar") === Symbol("bar")
// false
Symbol.keyFor方法返回一个已登记的 Symbol 类型值的key。

var s1 = Symbol.for("foo");//Symbol.for为Symbol值登记的名字,是全局环境的
Symbol.keyFor(s1) // "foo"

var s2 = Symbol("foo");//Symbol()这样子是未登记的名字,所以报错
Symbol.keyFor(s2) // undefined
  1. ES6还提供了11个内置的Symbol值,指向语言内部使用的方法。
  • Symbol.hasInstance:
    foo instanceof Foo在语言内部,实际调用的是FooSymbol.hasInstance
  • Symbol.isConcatSpreadable:
    对象的Symbol.isConcatSpreadable属性等于一个布尔值,表示该对象使用Array.prototype.concat()时,是否可以展开。
     let arr1 = ['c', 'd'];
['a', 'b'].concat(arr1, 'e') // ['a', 'b', 'c', 'd', 'e']
arr1[Symbol.isConcatSpreadable] // undefined

let arr2 = ['c', 'd'];
arr2[Symbol.isConcatSpreadable] = false;
['a', 'b'].concat(arr2, 'e') // ['a', 'b', ['c','d'], 'e']

三、类型判断

  1. typeof检测基本类型比较准确。
  typeof 1 // 'number'
	typeof '1' // 'string'
	typeof undefined // 'undefined'
	typeof true // 'boolean'
	typeof Symbol() // 'symbol'
	typeof b // b 没有声明,但是还会显示 undefined
	
	typeof null // 'object'
	
	typeof [] // 'object'
	typeof {} // 'object'
	typeof console.log // 'function'
  1. instanceof 检测引用类型比较准确。但原型可被手动修改,会导致检测结果错误
var arr = [1,2,3];
arr instanceof Array   //true  检测arr是不是Array构造函数的实例

手动实现instanceof函数:根据每个实例都有一个__proto__属性指向构造函数的原型prototype还要实现原型链继承。

var arr =[];
var arr2 = '2';
console.log(_instanceof(arr,Array))  //true
console.log(_instanceof(arr,Array))  //false
console.log(_instanceof(null,Array))  //false
function _instanceof(A, B) {
    var O = B.prototype;
    A = A.__proto__; //先注释这句,网上好多都有这句,但是当A是null的时候就会报错,因为null是原型链顶端了,没有__proto__属性
    while (true) {
        //Object.prototype.__proto__ === null
        if (A === null)
            return false;
        if (O === A)// 这里重点:当 O 严格等于 A 时,返回 true
            return true;
        A = A.__proto__; //加上这句是为了当O不等于A的时候,再向A的上级去找(Object)再来和O进行比较。
    }
}
  1. 构造函数 constructor,null undefined 没有构造函数,无法判断
var a = [];
a.constructor === Array
// true
  1. Object.prototype.toString.call()
    通过 Object.prototype.toString.call(a) 可返回 “[object Type]” 的字符串,可以获得正确的变量类型,如
let a = []; 
Object.prototype.toString.call(a); // "[object Array]"
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值