JavaScript 数据类型

Posted 知其黑、受其白

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript 数据类型相关的知识,希望对你有一定的参考价值。

阅读目录

javascript 数据类型

JavaScript 一共有7种数据类型:

  • String
  • Numbe
  • Null
  • Undefined
  • Boolean
  • Object
  • Symbol(ES6新增)

他们可以分为两大类:值类型和引用类型

undefined

undefined 未找到的意思,它的值只有一个,也就是 undefined,我们可以通过以下几种方式得到 undefined:

  • 已用一个已声明但未初始化的变量
  • 一个没有返回值的函数
  • 执行 void 表达式
  • 引用未定义的对象属性
  • 全局常量 window.undefined 或 undefined
var a;
var obj = 
  a: 'A',

var fn = function ()
	var b = 2;

​
console.log(a);   // undefined
console.log(void 0);   // undefined
console.log(obj.b);   // undefined
console.log(fn());   // undefined
window.undefined // undefined

如何判断一个变量的值是否为 undefined 呢 ?

前面提到了 undefined 就是未找到的意思,那现在有下面几种方案看是否可行:

(1)逻辑取非转为布尔值值判断

<script type="text/javascript">
	var a;
	if (!a) 
		// 具体操作
		console.log(123)
	
</script>

(2)直接与 undefined 进行比较

<script type="text/javascript">
	var a;
	if (a === undefined) 
		// 具体操作
		console.log(123+'undefined')
	
</script>

(3)判断数据类型是否等于 undefined 字符串

<script type="text/javascript">
	var a;
	if(typeof a === "undefined")
		// 具体操作
		console.log(123+'typeOf-undefined')
	
</script>


上面 3 种方案只有第三种是可行的。

第一种,若 a 的值为 null、空字符串、数字 0 时取非后值都为 true
第二种使用三个 “=” 是可以的,但是如果 a 未定义时会抛出错误。

所以建议使用第三种方案。

Null

Null 与 undefined 类似,它也只有一个唯一的值,那就是 null,表示为空的意思。
如果我们将 null 与 undefined 作弱比较,可以发现他们是相等的。

null == undefined     // true
null === undefined    // false

null 为 JavaScript 的保留字关键字,undefined 是一个常量,因此 null 不能作为变量名。

var undefined = 1   // undefined
var null = 1    //Uncaught SyntaxError: Unexpected token 'null'

Boolean

boolean 只有两个值:true 和 false,常用于判断语句中。

例如下面这个例子,在一个数组中筛选出偶数:

var arr = [2, 4, 5, 1, 6, 7];
var odd = [];
arr.map((item) => 
  if (item % 2 === 0) 
    odd.push(item);
  
)
console.log(odd);   // [2, 4, 6]

他们之间可以通过取非相互转换:

var f = true;
console.log(f);    // true
console.log(!f);   // false
console.log(!!f);  // true

Number

number 表示数字,在 JavaScript 中不会区分一个数字具体是一个整数还是一个小数,又或者是一个浮点数。

他们都属于 number 类型。number 有两个特殊的值:NaNInfinity

NaN(Not a Number)通常在计算失败的时候会得到该值。
要判断一个变量是否为 NaN,则可以通过 Number.isNaN 函数进行判断。

Infinity 是无穷大,加上负号 “-” 会变成无穷小,在某些场景下比较有用,比如通过数值来表示权重或者优先级,Infinity 可以表示最高优先级或最大权重。

在 JavaScript 中对 number 进行一些计算时可能会得到一些非期望的结果,如 0.1 + 0.3

console.log(0.1 + 0.3);   // 0.30000000000000004

这是因为 JavaScript 在计算时,JavaScript 引擎会先将十进制的数转为二进制,计算完成后再转为二进制。

在进制转换过程中,如果小数是循环的话,那么就会出现误差。
再比如把 3 开方后在平方后的结果也不等于 3。

Math.pow(Math.pow(3, 1/2), 2)  // 2.9999999999999996

要想解决精度误差问题,有两种方案

(1)计算前先把小数转为整数,计算后再转为整数

console.log((0.1 * 10 + 0.3 * 10) / 10);   // 0.3

(2)使用 precision 进行四舍五入,以定点表示法或指数表示法表示的一个数值对象的字符串表示,四舍五入到 precision 参数指定的显示数字位数。

parseFloat((0.1 + 0.2).toPrecision(12))   // 0.3

在涉及到 number 计算的场景下,我们常会用到一些方法,对这些方法熟记于心才可以在不同的场景下灵活的运用。

// 取绝对值
Math.abs(x);
// 返回一个数的立方根
Math.cbrt(x)
// 一个数向上取整后的值
Math.ceil(x);
// 一个数向下取整后的值
Math.floor(x)
// 零到多个数值中最大值
Math.max([x[, y[,]]])
// 零到多个数值中最小值 
Math.min([x[, y[,]]])
// 一个 0 到 1 之间的伪随机数
Math.random()
// 四舍五入后的整数 
Math.round(x)
// 一个数的符号,得知一个数是正数、负数还是 
Math.sign(x)
// ......

String

字符串类型,最常见的数据类型之一。
我们可以通过 .length 获取字符串的长度,也可以通过 substr 方法从一段字符串中解决我们所需要的部分,还可以使用 split 方法把字符串转为一个数组。

var str = 'abcdefg'
console.log(str.length)   // 8
console.log(str.substr(2, 5))   // bcdef
console.log(str.split(''))  // ["a", "b", "c", "d", "e", "f", "g"]

Symbol(ES6新增)

Symbol 是 ES6 中引入的新数据类型,它表示一个唯一的常量,通过 Symbol 函数来创建对应的数据类型,创建时可以添加变量描述,该变量描述在传入时会被强行转换成字符串进行存储。

var a = Symbol('1')
var b = Symbol(1)
a.description === b.description // true

var c = Symbol(id: 1)
c.description // [object Object]

var _a = Symbol('1')
_a == a // false

Symbol 的最大特点就是它所定义的值永远都是唯一的,常用与的场景有,避免常量值重复、避免对象属性重复等。

看下面这个例子,有一个函数,对所传入的对象要添加一个 name 属性:

function fn(person) 
  person.name = 'adc';
  console.log(person);  //  name: 'abc' 
fn(
	name: 'aaa',
	age: 22
);

可以看到当传入的对象含有name属性时,会把原有的 name 覆盖掉,这并不是我们想要的结果。

为了避免对象属性的重复覆盖,可以使用 symbol 很好的解决这个问题:

function fn(person) 
	const name = Symbol('name');
	person[name ]= 'adc';
	console.log(person);  //  name: 'aaa', [Symbol(name)]: 'abc' 
fn(
	name: 'aaa',
	age: 22
);

数据类型的检测

我们已经知道了 JavaScript 有这些数据类型,在开发中,往往需要判断某个变量的类型来进行一些后续的操作。

(1)typeof

const a = 1;
const b = '1';
const c = null;
const d = undefined;
const e = false;
const f = [1, 2, 3];
const g =  name: 'abc' ;
const h = new Date();
const i = new RegExp();
​
console.log(typeof a);  // number
console.log(typeof b);  // string
console.log(typeof c);  // object
console.log(typeof d);  // undefined
console.log(typeof e);  // boolean
console.log(typeof f);  // object
console.log(typeof g);  // object
console.log(typeof h);  // object
console.log(typeof i);  // symbol

上面的代码中,我们定义了不同类型的常量,打印出了通过 typeof 方法所检测的值,可以看到对于 number、string、undefined、boolean 和 symbol 数据类型是可以检测数来的,但是对于 null、Array、对象等类型的结果都是 object。

也就是说 typeof 只可以检测出值类型的数据,无法分辨出引用类型的数据。

(2)instanceof

instanceof 用来判断 A 是否是 B 的实例,如果是则返回 true,不是则返回 false。

console.log([] instanceof Array);       //true
console.log( instanceof Object);       //true
console.log(new Date() instanceof Date);       //true
console.log(new Date() instanceof Object);       //true
console.log(new RegExp() instanceof Object);       //true

但是这种方式还是比较有局限性,instanceof 只能用来判断两个对象是否属于实例关系, 而不能判断一个对象实例具体属于哪种类型,而且 instanceof 对于引用类型的支持很好,但他是无法对原始类型进行判断, 所以一般都是在 typeof 判断为 object 时才使用 instanceof。

(3)constructor

该方法可以打印出目标实例所属的类,函数的 constructor 是不稳定的,这个主要体现在自定义对象上,当开发者重写 prototype 后,原有的 constructor引用会丢失,constructor 会默认为 Object。

console.log([].constructor == Array);       //true
console.log(.constructor == Object);      //true
console.log("1".constructor == String);        //true
console.log((1).constructor == Number);       //true
console.log(true.constructor == Boolean);       //true
console.log(Symbol().constructor == Symbol);       //true
console.log(new Date().constructor == Date);       //true
console.log(new RegExp().constructor == RegExp);       //true

但这种方式无法检测 null 和 undefined,他们是无效的对象,因此不会存在 constructor。

(4)* Object.prototype.toString.call()

toString 是 Object 的一个方法,在 Object上调用该方法会返回 [object Object],其中第二个Object 就是他的数据类型。

但是对于其他类型需要使用 call / apply才可以返回正确的数据类型。

const a = 1;
const b = '1';
const c = null;
const d = undefined;
const e = false;
const f = [1, 2, 3];
const g =  name: 'abc' ;
const h = new Date();
const i = new RegExp();
const j = Symbol('name');
const k = function();
​
console.log(Object.prototype.toString.call(a)); //[object Number]
console.log(Object.prototype.toString.call(b)); //[object String]
console.log(Object.prototype.toString.call(c)); //[object Null]
console.log(Object.prototype.toString.call(d)); //[object Undefined]
console.log(Object.prototype.toString.call(e)); //[object Boolean]
console.log(Object.prototype.toString.call(f)); //[object Array]
console.log(Object.prototype.toString.call(g)); //[object Object]
console.log(Object.prototype.toString.call(h)); //[object Date]
console.log(Object.prototype.toString.call(i)); //[object RegExp]
console.log(Object.prototype.toString.call(j)); //[object Symbol]
console.log(Object.prototype.toString.call(k)); //[object Function]

类型的相互转换

JavaScript 是一种弱类型的语言,相对于其他高级语言有一个特点,那就是在处理不同数据类型运算或逻辑操作时会强制转换成同一数据类型。

如果我们处理不好转换问题,在开发过程中可能会引发很多 bug。

(1)强制转换

所谓强制转换,就是我们通过一些方法主动的去转换数据类型。

  • 转为 String:.toString()、String()、JSON.stringify
  • 转为 Number:Number()、parseInt()
  • 转为 Boolean:Boolean()
  • 转为对象:JSON.parse()

(2)隐式转换

所谓隐式转换,是在我们进行一些操作时,针对于不同的情况,JavaScript 会自动的对一些数据的数据类型进行装换。

常发生于一下情况:

  • 运算相关的操作符包括 +、-、+=、++、* 、/、%、<<、& 等。
  • 数据比较相关的操作符包括 >、<、== 、<=、>=、===。
  • 逻辑判断相关的操作符包括 &&、!、||、三目运算符。

下面列举了一下例子,这里就不再一一分讲解了:

console.log(1 + 1 + '1');    //21
console.log(1 + '1' + 1);    //111
console.log(1 + true);    //2
console.log(1 + false);    //1
console.log(1 + null);    //1
console.log(1 + undefined);    //NaN
console.log(null + undefined);    //NaN
console.log('a' + undefined);    //aundefined
console.log('a' + null);    //anull
console.log(undefined + 'b');    //undefinedb
console.log(false == 0);    //true
console.log(false == null);    //false
console.log(false == undefined);    //false
console.log(!null);    //true
console.log(!undefined);    //true
console.log(!12);    //false
console.log(!'false');    //false

值类型与引用类型

在上面提到了很多次值类型和引用类型,那么他们究竟有什么区别 ?

var a = 1;
var b = 
	name: 'abc',
	age: 22
;var c = a;
var d = b;
console.log(c);  // 1
console.log(d);  //  name: 'abc', age: 22 
​
a = 2;
b.name = 'def';
console.log(c);  // 1
console.log(d);  //  name: 'def', age: 22 

在上面的例子中,声明了两个变量 a 和 b,a 为 Number 类型,b 为 Object 类型。

然后把 a 赋值给变量 c,b 赋值给变量 d。对 a 和 b 的值都进行改变,然后打印出 c 和 d,得到的结果是:c 的值还是原来的值,而 d 的值是改变后的b的值。

明明赋值在改变值的前面进行的,为什么会出现这种情况呢?

保存与复制的是指向对象的一个指针,当堆中的值改变后,指向该堆的变量值都会一起改变。


接着上面的那个例子,改变 d 的 age 属性,可以看到 b 的 age 也发生了改变。

d.age = 18;
console.log(b);   //  name: 'def', age: 18 

浅拷贝与深拷贝

由于引用类型在赋值时只传递指针,这种拷贝方式称为浅拷贝。

而创建一个新的与之相同的引用类型数据的过程称之为深拷贝。

实现深拷贝的方式:

(1)通过把一个对象转为一个字符串后再转为对象进行复制的方式

var obj = 
	name: 'a',
	path: 'a',

var copy = JSON.parse(JSON.stringify(obj));
obj.path = 'b';
​
console.log(copy);   //  name: 'a', path: 'a' 

这种方式不能 undefined 、function、RegExp 等类型。

(2) 通过 Object.assign(target, so

以上是关于JavaScript 数据类型的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript学习笔记——数据类型

JavaScript学习笔记——数据类型

输入类型按钮在按下时会改变颜色?

js深复制

Scikit-learn GridSearchCV - 为啥我在执行 grid.fit() 时会收到数据类型错误?

js数据类型