一、6种原始数据类型
基本数据类型(按值访问)
- Null(js中的数据在底层是以二进制存储,如果前三位为0,那么就会判定为object,而null的所有都为0)
- 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
- 注:自动创建的基本包装类型的对象,只存在于代码执行的一瞬间,然后被立即销毁。这意味着我们不能在运行时为基本类型值添加属性和方法。
-
Number
-
Boolean
-
String
-
引用类型:Object(包括Object/Array/RegExp/Date/null)
使用new 操作符创建的引用类型的实例,在执行流离开当前作用域之前都一直保存在内存中。
二、ES6新定义的Symbol:(唯一标识符、可以作为对象的稀有属性,防止被重名改写)
- Symbol值通过Symbol函数生成,typeOf的运算结果也是symbol
let s = Symbol();
typeof s
// "symbol"
- Symbol函数前不能使用new命令,否则会报错。这是因为生成的Symbol是一个原始类型的值,不是对象。也就是说,由于Symbol值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。
- 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)"
- 如果 Symbol 的参数是一个对象,就会调用该对象的toString方法,将其转为字符串,然后才生成一个 Symbol 值。
const obj = {
toString() {
return 'abc';
}
};
const sym = Symbol(obj);
sym // Symbol(abc)
- 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
- 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!"
- 常量使用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');
}
}
- 消除魔术字符串。
魔术字符串指的是,在代码之中多次出现、与代码形成强耦合的某一个具体的字符串或者数值。风格良好的代码,应该尽量消除魔术字符串,该由含义清晰的变量代替。
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()
};
- 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']
- 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
- 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']
三、类型判断
- 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'
- 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进行比较。
}
}
- 构造函数 constructor,null undefined 没有构造函数,无法判断
var a = [];
a.constructor === Array
// true
- Object.prototype.toString.call()
通过 Object.prototype.toString.call(a) 可返回 “[object Type]” 的字符串,可以获得正确的变量类型,如
let a = [];
Object.prototype.toString.call(a); // "[object Array]"