js中有七种数据类型,包括基本数据类型 Number、String、Boolean、Undefined、Null、Symbol(ES6 新增,创建后独一无二且不可变的数据类型),一种复杂数据类型( Object )。
由于js中的变量是松散类型多的,所以它提供了一种检测当前变量的数据类型的方法,也就是 typeof 关键字。
typeof 123 // Number
typeof 'abc' // String
typeof true //Boolean
typeof undefined //Undefined
typeof null //Object
typeof {} //Object
typeof [] //Object
let a = function() {
console.log(1)
}
typeof a //function
let s = Symbol();
typeof s //symbol
null类型进行typeof操作符后,结果是object,原因在于,null类型被当做一个空对象引用。
1.Number
Number类型包含整数和浮点数两种值。
NaN:非数字类型。特点:涉及到的任何关于NaN的操作,都会返回NaN;NaN不等于自身。
isNaN()函数用于检查其参数是否是非数字值。
isNaN(123) //false
isNaN("hello") //true
2.String
字符串有length属性。
字符串转换:
String():适合于任何数据类型(null,undefined转换后为null和undefined);
toString()方法:括号中的可以写一个数字,代表进制,对应进制字符串,null、defined没有toString()方法。
3.Boolean
该类型只有两个值,true和false。
4.Undefined
只有一个值,即undefined值。使用var声明了变量,但未给变量初始化值,那么这个变量的值就是undefined。
5.Null
null类型被看做空对象指针,前文的null类型也是空的对象引用。
6.Symbol
Symbol是ES6新增的原始数据类型,是为了解决在对象中对属性名滥用而导致的冲突问题。
ps:既然是数据类型,不是对象,那么就不能用new命令,因此不能添加属性。
let a = Symbol('a');
console.log(a); //Symbol(a)
console.log(typeof a) //symbol
简单来说:一旦声明一个变量为symbol类型,说明这个变量是唯一的,独一无二的,覆盖不了的。
let a = Symbol('abc');
let b = Symbol('abc');
let c = 'abc';
let d = 'abc';
console.log(a === b) //false
console.log(c === d) //true
现在,在对象的属性名有两种类型,一个是以字符串的形式,另一个是以Symbol的形式。
若以symbol的形式去写,那么就要按照规范去写,加上中括号[],来表明使用了symbol类型,获取时而不是按照字符串类型的属性名去获取,而以中括号获取。
let name = Symbol();
let obj = {
[name]: 'black',
age: 25,
name: 'red'
}
console.log(obj) //{age: 25,name: "red",Symbol(): "black"}
通过例子可知:获取对象时,得到是两个不一样的name属性名,说明symbol和字符串时两种不一样的值,获取方式也不一样,按照点获取的是字符串,中括号获取的是symbol类型的值。
关于在对象声明时对symbol类型的写法有三种:
let sym = Symbol();
//第一种写法:
let a = {};
a[sym] = 'hello';
console.log(a); //{Symbol(): "hello"}
//第二种写法:
let a = {
[sym]: 'hello'
}
console.log(a); //{Symbol(): "hello"}
//第三种写法:
let a = {};
Object.defineProperty(a, sym, {value: "hello"});
console.log(a);
实例:
const shapeType = {
triangle: Symbol(),
rectangle: Symbol(),
};
function getArea(shape, options) {
let area = 0;
switch (shape) {
case shapeType.triangle:
area = .5 * options.width * options.height;
break;
case shapeType.rectangle:
area = options.width * options.height;
break;
}
return area;
}
let a = getArea(shapeType.triangle, { width: 100, height: 100 });
let b = getArea(shapeType.rectangle, { width: 100, height: 100 });
console.log(a) // 5000
console.log(b) // 10000
Symbol方法:
Object.getOwnPropertySymbols()返回一个数组,成员是当前对象的所有用作属性名的Symbol值。
symbol在对象上作为一个属性名,不会被循环的方法所识别,不会出现在for...in、for...of循环中,不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回,所以它有自己获取symbol值的方法。
let name = Symbol('name');
let age = Symbol('age')
let obj = {
[name]: 'peter',
[age]: 25
}
let a = Object.getOwnPropertySymbols(obj)
console.log(a) // [Symbol(name), Symbol(age)]
Reflect.ownKeys() 返回所有类型的键名,包括常规键名和 Symbol 键名
let name = Symbol('name');
let obj = {
[name]: 'peter',
age: 25,
enum: 2
}
console.log(Reflect.ownKeys(obj)) // ["age", "enum", Symbol(name)]
Symbol.for() 接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建并返回一个以该字符串为名称的 Symbol 值。
这个方法就是重新使用已有symbol的值,在遍历判断时常用 。
let a = Symbol.for('abc');
let b = Symbol.for('abc');
let c = Symbol.for('ab');
console.log(a === b) // true
let a = Symbol.for('abc')
let b = Symbol('abc')
console.log(a) // Symbol(abc)
console.log(a === b) // false
由此可知:a虽然用Symbol.for声明了symbol类型,但是却和symbol声明不一样,说明两者不是同一个值。
Symbol函数可以接受一个字符串作为参数,表示对Symbol实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。
let s1 = Symbol('foo');
let s2 = Symbol('bar');
s1 //Symbol(foo)
s2 //Symbol(bar)
s1.toString(); //"Symbol(foo)"
s2.toString(); //"Symbol(bar)"
上面代码中,s1和s2是两个Symbol值。如果不加参数,他们在控制台输出都是Symbol(),不利于区分。有了参数以后,就等于为它们加上了描述,输出的时候就能够分清,到底是哪一个值。
如果Symbol的参数是一个对象,就会调用该对象的toString方法,将其转为字符串,然后生成一个Symbol。
const obj = {
toString() {
return 'abc'
}
}
const sym = Symbol(obj);
sym //Symbol(abc)
注意,Symbol函数的参数只是表示对当前Symbol值的描述,因此相同参数的Symbol函数的返回值是不相等的。
// 没有参数的情况
let s1 = Symbol();
let s2 = Symbol();
s1 === s2 //false
//有参数的情况
let s1 = Symbol('foo');
let s2 = Symbol('foo');
s1 === s2 //false
上面代码中,s1和s2都是Symbol函数的返回值,而且参数相同,但是它们是不相等的。
Symbol 值不能与其他类型的值进行运算,会报错。
let sym = Symbol('My symbol');
"you symbol is"+sym
// Uncaught TypeError: Cannot convert a Symbol value to a string
`your symbol is ${sym}`
// TypeError: can't convert symbol to string
但是,Symbol 值可以显式转为字符串。
let sym = Symbol('My symbol');
String(sym) // 'Symbol(My symbol)'
sym.toString() // 'Symbol(My symbol)'
另外,Symbol值可以转变为布尔值,但是不能转为数值。
let sym = Symbol();
Boolean(sym) //true
!sym //false
if(sym){
//...
}
Number(sym) //TypeError
sym+2 //TypeError
7.Object
js中对象是一组属性与方法的集合。这里就要说到引用类型了,引用类型是一种数据结构,用于将数据和功能组织在一起。引用类型有时候也被称为对象定义,因为它们描述的是一类对象所具有的属性和方法。
三大引用类型:
1.Object类型
我们看到的大多数类型值都是Object类型的实例,创建Object实例的方式有两种:
第一种是使用new操作符后跟Object构造函数,如下所示:
var person = new Object();
person.name = "托尼老师";
person.age = 24;
第二种方式是使用对象字面量表示法,如下所示:
var person = {
name: '托尼老师',
age: 24
}
2.Array类型
数组的每一项都可以用来保存任何类型的数据,也就是说,可以用数组的第一个位置来保存字符串,第二个位置保存数值,第三个位置保存对象...另外,数组的大小是可以动态调整的。
创建数组的基本方式有两种:
第一种是使用Array构造函数,如下所示:
var colors = new Array("red","blue","yellow");
第二种是使用数组字面量表示方法,如下所示:
var colors = ["red","blue","yellow"];
3.function类型
每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法。函数通常是使用函数声明语法定义的,如下所示:
function sum(num1,num2){
return num1 + num2;
}
这和使用函数表达式定义函数的方式相差无几。
var sun = function(){
return sum1 + sum2;
}
也就是说,js按照存储方式分为值类型和引用类型。他们计算有什么区别呢?
题目1:
var a = 100;
var b = a;
a =200;
console.log(b); //100
题目2:
var a = {age : 20};
var b = a;
b.age = 21;
console.log(a.age); //21
题目一是简单的值类型,在从一个变量向另一个变量赋值基本类型时,会在该变量上创建一个新值,然后再把该值复制到为新变量分配的位置上。
此时,a中保存的值为100,当使用a来初始化b时,b中保存的值也为100,但b中的100与a中的是完全独立的,该值只是a中的值的一个副本,此后,这两个变量可以参加任何操作而不受影响。就是说基本类型在赋值操作后,两个类型是相互不受影响的。
题目2是引用类型,当从一个变量向另一个变量赋值引用类型的值时,同样也会将存储在变量中的对象的值赋值一份放到为新变量分配的空间中。
这时保存在变量中的对象是堆内存中的地址,所以,与简单赋值不同,这个值的副本实际上是一个指针,而这个指针指向存储在堆内存的一个对象。那么赋值操作后,两个变量都保存了同一对象地址,则这两个变量指向了同一个对象,因此,改变其中任何一个变量,都会相互影响。
因此,引用类型的赋值其实是对象保存在栈区地址指针的赋值,所以两个变量指向同一个对象,任何的操作都会相互影响。