js复习基本概念(数据类型)

一、 什么是数据类型

数据:未经过处理的原始记录,在计算机系统中,数据以二进制信息单元0,1的形式表示。

数据类型: 是用来约束数据的解释规约。

二、基础数据类型、引用数据类型

1. typeof操作符

(1) typeof的所有返回值都是字符串

(2) 六种返回值类型

  • undefined

  • number

  • string

  • boolean

  • Object

  • Function

(3) 建议使用()

1
(type of a);

原因:原因操作符优先级,js中有15种优先级,为避免混乱建议使用()包裹。

(4) typrof null

  • 返回值为”Obejct”
  • 原因

  • (1)从逻辑角度来看,null值表示一个空对象指针,而这正是使用typeof操作符检测null值时会返回“object”的原因。摘自《JAVASCRIPT高级程序设计 第三版 p25》

  • (2)null不是一个空引用, 而是一个原始值, 参考ECMAScript5.1中文版 4.3.11节; 它只是期望此处将引用一个对象, 注意是”期望”, 参考 null - JavaScript.

  • (3)typeof null结果是object, 这是个历史遗留bug, 参考 typeof - JavaScript

  • (4)在ECMA6中, 曾经有提案为历史平凡, 将type null的值纠正为null, 但最后提案被拒了. 理由是历史遗留代码太多, 不想得罪人, 不如继续将错就错当和事老, 参考 harmony:typeof_null [ES Wiki]

以上内容作者:克荷林
链接:https://www.zhihu.com/question/21691758/answer/98782260
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2. undefined

  • undefined类型只有一个值,即特殊的undefined
  • 在使用var关键字声明变量,但未对其初始化时,这个变量就是undefined

3. null

  • null类型只有一个值,这个特殊值是null,从逻辑角度来看,null表示一个空对象指针,而这也是typeof操作符检测null时会返回“obeject”的原因
  • 在使用var关键字声明变量,但未对其初始化时,这个变量就是undefined

4. null 和 undefined

(1)相同点

(1)参与判断 返回false

1
2
3
4
5
6
7
8
9
// if判断为false的情况

if (false)
if (null)
if (undefined)
if (0)
if (NaN)
if ('')
if ("")

(2)没有方法

(3)都是falsy,不加!比较都是false

(2)不同点

  • (1) 语义上不同,null是有值,但值为空,undefined是值未定义

  • (2) typeof

    null typeof 是obejct undefined typeof undefined

  • (3) 数字转化

    undefined =>NAN null => 0

  • (4) null可以json化

  • *(5)通常情况下,undefined代表未初始化,null已经初始化

  • *(6)在浏览器环境中,undefined是windows的一个属性,只不过属性对应的值是undefined

  • *(7) null不可以作为变量名 undefined可以作为变量名

    原因:null不能做变量名的原因应该是因为null是字面量

(3)为什么不同

JavaScript的最初版本是这样区分的:null是一个表示”无”的对象,转为数值时为0;undefined是一个表示”无”的原始值,转为数值时为NaN。

原因有如下两个:

  • null像在Java里一样,被当成一个对象。但是,JavaScript的数据类型分成原始类型(primitive)和合成类型(complex)两大类,Brendan Eich觉得表示”无”的值最好不是对象。

  • null像在Java里一样,被当成一个对象。但是,JavaScript的数据类型分成原始类型(primitive)和合成类型(complex)两大类,Brendan Eich觉得表示”无”的值最好不是对象。

因此,Brendan Eich又设计了一个undefined。

如果不深究,简单理解可以归纳为历史原因

(4)使用场景

[1] null

null表示”没有对象”,即该处不应该有值

典型用法:

  • null表示”没有对象”,即该处不应该有值
1
var test = null;
  • 作为对象原型链的终点
1
2
Object.getPrototypeOf(Object.prototype)
// null
[2] undefined
  • undefined表示”缺少值”,就是此处应该有一个值,但是还没有定义

  • 变量被声明了,但没有赋值时,就等于undefined

  • 调用函数时,应该提供的参数没有提供,该参数等于undefined

  • 对象没有赋值的属性,该属性的值为undefined

  • 函数没有返回值时,默认返回undefined

1
2
3
4
5
6
7
8
9
10
11
var i;
i // undefined

function f(x){console.log(x)}
f() // undefined

var o = new Object();
o.p // undefined

var x = f();
x // undefined

有些框架会定义

1
var undefined

建立undefined的引用是为了提高性能。

[3] == && ===
  • undeined是派生自null的值,因此ECMA-262规定他们的相等性测试要返回true
  • 原因:由于 == 会进行转换操作
1
2
console.log(null == undeinded) // true
console.log(null === undeinded) // false

5.boolean

名称源自数学家乔治布尔。

(1) 特点

[1] 该类型只有2个字面值,true和false

这两个值与数字不是一回事,true不一定等于1, false也不一定等于0

[2] 所有数据类型都可以转换为 boolean , js提供了Boolean方法转换

js中只有6个数据会被转化为fasle(fasly)

  • null

  • undefined

  • 0

  • -0

  • NaN

  • 空字符串

(2) 转换boolean

  • Boolean()

  • !!转换为boolean

5.Number

(1) 标准

  • ECMAScript使用的是IEEE 754标准(浮点数算术标准)来定义Number类型,ECMAScript中所有的数字,无论是整数还是小数,其类型均为Number。

  • 在程序内部,Number类型的实质是一个64位的浮点数,这与Java中double类型的浮点数是一致的。

(2)数字表示方式:

  • 十进制
1
var intNum = 12;
  • 八进制

建议不使用(严格模式、ES5下无效,会导致Javascript引擎抛出错误)

1
var octalNum1 = 070
  • 十六制
1
var hexNum = 0xA

(3)数值范围:

[1] JavaScript所能表示的数值范围为:
正负1.7976931348623157乘以10的308次方,其最小所能表示的小数为正负5乘以10的负324次方
[2] JavaScript能表示并进行精确算术运算的整数范围为:
正负2的53次方,也即从最小值-9007199254740992到最大值+9007199254740992之间的范围

对于超过这个范围的整数,JavaScript依旧可以进行运算,但却不保证运算结果的精度。值得注意的是,对于整数的位运算(比如移位等操作),JavaScript仅支持32位整型数,也即从-2147483648到+2147483647之间的整数。

获取边界值:

两个边界值可以分别通过访问Number对象的MAX_VALUE属性和MIN_VALUE属性来获取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Number.MAX_VALUE // 1.7976931348623157e+308
Number.MIN_VALUE // 5e-324
```

**超过边界值:**

```javascript
Infinity // 如果超过Number.MAX_VALUE
-Infinity // 如果小于Number.MIN_VALUE

var a = 9007199254740992; // 对正负2的53次方范围以外的整数

console.log(a+3); // 正确的运算结果应该是9007199254740995,但JavaScript给出的计算结果却是9007199254740996
```


#### (4) float

##### [1] 所谓浮点数值,就是该数值中必须包含一个小数点,并且小数点后至少有一位数字

```javascript
var floatNum1 = 1.1;
[2] 虽然小数点前面可以没有整数,但不推荐:
1
var floatNum2 = .1;
[3] 由于保存浮点数值需要的内存是保存整数的两倍,因此ECMAscript会不失时机地将浮点数值转换为整数
  • 小数点后没有任何数字,那么数值就可以作为为整数值来保存
1
var floatNum1 = 1.; // 解析为1
  • 浮点数本身表示就是整数,那么该值会被转换为整数
1
var floatNum2 = 10.0; // 解析为10

(5)科学计数法:

  • 当数字极大或极小,可以用e表示法(即科学计数法)表示。

    1
    var floatNum = 3.125e7; // 等于31250000 3.125乘以10的7次方
  • ECMAscript会将那些小数点后面带有6个0以上的浮点数值转化为科学计数表示法。

    1
    var floatNum = 3e-7 // 等于0.0000003
  • 浮点数值的最高精度是17位小数,但在进行算术计算时其精度远远不如整数。建议永远不要测试某个特定的浮点数值。

(6) Infinity

1
11 / 0 // Infinity

(7) NaN

Not a Number

1
0 / 0 // NaN
1
Nan === NaN // false
  • isNaN()

  • 返回结果只有 true false

  • 使用isNaN()判断传入值是不是NaN
  • 但实际使用中会判断传入值是否能被转化为数字
1
alert(isNaN(NaN)); // true
alert(isNaN(10)); // false
alert(isNaN("10")); // false
alert(isNaN("blue")); // true
alert(isNaN(true)); // false

(7) 转换方法

目的:将数据转换成数字、整数、浮点数。

[1] Number()

作用:任何数据类型转Number

- 数字

- NaN

[2] parseFloat

作用:处理字符串

建议:传第二个参数10

[3] parseInt

作用:处理字符串

建议:传第二个参数10 (表示第一个参数是什么进制,返回值结果始终十进制)

(8) 精度损失 (0.1 + 0.2 != 0.3)

1
2
3
4
5
6
7
var a = 0.1;

var b = 0.2;

if (a + b === 0.3) {
console.log('You got 0.3'); // 无法到达
}
[1] 原因

关于浮点数值计算误差的问题,是因为基于IEEE754标准。

[2] 解决方案

(1) 基数

1
2
3
4
5
6
7
var a = 0.1;

var b = 0.2;

var res = ((a * 10) + (b * 10)) / 10;

console.log(res); // 0.3

(2) toFixed

1
2
3
4
5
6
7
8

var a = 0.1;

var b = 0.2;

var res = 0.1 + 0.2;

console.log(res.toFixed(1));

6.String

String类型用于表示由零或多个16位unicode字符组成的字符序列,即字符串。

(1) 字符串特性

ECMAScript中字符串是不可变的

1
2
3
4
'abcd'
'abcd'.length = 5

console.log('abcd'.length); // 4

(2) 转义符

(3) unicode 编码

\uxxxx

(4) 字符串类型转换

toString

null undefined 不可用

String()

可以转换传进去所有类型

6.基础数据类型转换归纳

相同点:

(1) 都有String Boolean

(2) 首字母大写

(3) 都可以穿入任何类型

(4) 得到的结果都是和自己对应的

(5) 传入对象[Object,Object]

8. Object

定义:属性构成对象的无序的集合

对象的比较是引用的比较

(1) Object对象的分类

[1] 内部对象 (17个)

a. 内置对象 (3)

  • Math
  • Global
  • JSON

b. 常用对象 (8)

  • Boolean

  • String

  • Number

  • Object

  • Array

  • Function

  • Date

  • RegExp

c. 错误对象 (6)

  • Error

  • EvalError

  • RangeError

  • ReferenceError

  • SyntaxError

  • TypeError

  • URIError

[2] 宿主对象

  • window (浏览器)
  • global (node)
  • document

[3] 自定义对象

(2)基础数据类型转对象

[1] boolean => Object
1
2
3
4
5
Object(false);

Boolean {false}
__proto__: Boolean
[[PrimitiveValue]]:false
[2] null => Object
1
2
3
Object(null);

{}
[3] undefined => Object
1
2
3
Object(undefined);

{}
[4] string => Object
1
2
3
4
5
6
7
8
9
Object('abc');

String {"abc"}
0: "a"
1: "b"
2: "c"
length: 3
__proto__: String
[[PrimitiveValue]]: "abc"
[5] number => Object
1
2
3
4
5
Object(123);

Number {123}
__proto__: Number
[[PrimitiveValue]]: 123

(3) 对象转基础数据类型

[1] Object => boolean

任何对象转换boolean类型,都是true

1
2
3
Boolean(Object); // true
Boolean({}); // true
Boolean(new Boolean(false)); // true
[2] Object => number

先调用valueOf(),后调用toString()

调用valueOf(),返回原始值,若无原始值则返回对象本身

[1] date对象特殊,当前系统时间与1970年1月1日差额毫秒

1
2


[2] 对象

[3] Object => string

先调用toString(),后调用valueOf()

调用toString(),当前对象通过字符串表示

  • 数组
1
2
3
4

String([1,2,3])

[1,2,3].toString() // 等同于调用了 结果:"1,2,3"
  • 函数
1
2
3
4
5
function a() {
console.log('test');
}

a.toString()
  • 日期
1
2
3
4
5
6
7
var a = new Date();

String(a);

a.toString(); // 相当于

"Sat Jun 30 2018 23:43:59 GMT+0800 (China Standard Time)"
  • 正则
1
2
3
4
5
var a = new RegExp();

String(a); // 输出"/(?:)/"

a.toString() // 相当于

(4). 创建Object

[1] 对象字面量
1
var o1 = {test: 1};
[2] new Object()
1
var o1 = new Object({test: 1111});
[3] Object.create()
  • 语法

Object.create(proto, [propertiesObject])

  • 参数

proto:

新创建对象的原型对象。

propertiesObject:

可选。如果没有指定为 undefined,则是要添加到新创建对象的可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性)对象的属性描述符以及相应的属性名称。这些属性对应Object.defineProperties()的第二个参数

  • 返回值

一个新对象,带着指定的原型对象和属性

1
var a = Object.create(Object.prototype,{foo: {value: '111'}});
[4] new Object() 和 Object.create()区别

new操作符——完成四件事

  1. 创建一个新的空对象;

  2. 绑定this到这个空对象,即切换上下文;

  3. 给这个新对象添加proto,并指向构造函数的原型;

  4. 如果构造函数未返回对象(如返回对象,则替换return this),则将该新对象返回,即return this(第2条:this指向新对象);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

var a = Object.create(null,{foo: {value: '111'}});

{foo: "111"}
foo: "111"

var a = Object.create(Object.prototype,{foo: {value: '111'}});

{foo: "111"}

foo: "111"

__proto__:
Objectconstructor: ƒ Object()
hasOwnProperty: ƒ hasOwnProperty()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
valueOf: ƒ valueOf()
__defineGetter__: ƒ __defineGetter__()
__defineSetter__: ƒ __defineSetter__()
__lookupGetter__: ƒ __lookupGetter__()
__lookupSetter__: ƒ __lookupSetter__()
get __proto__: ƒ __proto__()
set __proto__: ƒ __proto__()

Object.create

  1. Object.create传入对象将作为新建对象的原型。

  2. Object.create传入构造函数原型时,无论构造函数是否返回对象,Object.create只会return 以构造函数原型来新建的对象(参见下面的polyfill),而new则参见上节粗体描述。

[5] Object.assign vs new

Object.assign 只拷贝对象的属性和函数,丢失原型链,proto指向Object.prototype

1
2
3
4
5
6
7
8
9
10
11
function Dog(name){
this.name = name;
}
Dog.prototype.getName = function(){
return this.name;
};
const pet1 = new Dog('rocky');
console.log(pet1.getName()); //rocky
const pet2 = Object.assign({},pet1);
console.log(pet2.name) //rocky
console.log(pet2.getName()); //error!!!!! getName undefined

(5). 对象属性查找

[1] .

解释器解析步骤:

  • 判断.之前是否为null 或 undefined
  • 判断.前时候为对象,若不是转化为对象
  • .后名字匹配
  • 返回值或undefined
[2] []

解释器解析步骤

  • 判断[]之前是否为null 或 undefined
  • 判断[]前时候为对象,若不是转化为对象
  • 计算[],转换为字符串,匹配值
  • 返回值或undefined

7.symbol

ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。

[1] Symbol值通过Symbol函数生成的

Symbol 值通过Symbol函数生成,Symbol函数前不能使用new命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象。也就是说,由于 Symbol 值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。基本上,它是一种类似于字符串的数据类型。

  • 创建
1
let s = Symbol();
  • 不能使用new操作符
1
let s = new Symbol(); //报错
  • 不能添加属性
1
let s = Symbol(); // s.name 等 '123' 不可行

[2] typeof

typeof运算符的结果,表明变量s是 Symbol 数据类型

1
typeof s // "symbol"

[3] 参数

Symbol函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。

1
2
3
4
5
6
7
8
let s1 = Symbol('foo');
let s2 = Symbol('bar');

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

s1.toString() // "Symbol(foo)"
s2.toString() // "Symbol(bar)"

如果 Symbol 的参数是一个对象,就会调用该对象的toString方法,将其转为字符串,然后才生成一个 Symbol值

1
2
3
4
5
6
7
const obj = {
toString() {
return 'abc';
}
};
const sym = Symbol(obj);
sym // Symbol(abc)

[4] 比较

Symbol函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的Symbol函数的返回值是不相等的

1
2
3
4
5
6
7
8
9
10
11
// 没有参数的情况
let s1 = Symbol();
let s2 = Symbol();

s1 === s2 // false

// 有参数的情况
let s1 = Symbol('foo');
let s2 = Symbol('foo');

s1 === s2 // false

[5] 运算

  • Symbol 值不能与其他类型的值进行运算,会报错
1
2
3
4
5
6
let sym = Symbol('My symbol');

"your symbol is " + sym
// TypeError: can't convert symbol to string
`your symbol is ${sym}`
// TypeError: can't convert symbol to string
  • Symbol 值可以显式转为字符串
1
2
3
4
let sym = Symbol('My symbol');

String(sym) // 'Symbol(My symbol)'
sym.toString() // 'Symbol(My symbol)'
  • Symbol 值也可以转为布尔值,但是不能转为数值
1
2
3
4
5
6
7
8
9
10
let sym = Symbol();
Boolean(sym) // true
!sym // false

if (sym) {
// ...
}

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

[6] 作为属性名的 Symbol

  • 定义

由于每一个 Symbol 值都是不相等的,这意味着 Symbol 值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let mySymbol = Symbol();

// 第一种写法
let a = {};
a[mySymbol] = 'Hello!';

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

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

// 以上写法都得到同样结果
a[mySymbol] // "Hello!"
  • Symbol 值作为对象属性名时,不能用点运算符
1
2
3
4
5
6
const mySymbol = Symbol();
const a = {};

a.mySymbol = 'Hello!';
a[mySymbol] // undefined
a['mySymbol'] // "Hello!"

上面代码中,因为点运算符后面总是字符串,所以不会读取mySymbol作为标识名所指代的那个值,导致a的属性名实际上是一个字符串,而不是一个 Symbol 值。

同理,在对象的内部,使用 Symbol 值定义属性时,Symbol 值必须放在方括号之中。

1
2
3
4
5
6
7
let s = Symbol();

let obj = {
[s]: function (arg) { ... }
};

obj[s](123);

上面代码中,如果s不放在方括号中,该属性的键名就是字符串s,而不是s所代表的那个 Symbol 值。

  • 增强的对象写法
1
2
3
let obj = {
[s](arg) { ... }
};
  • 增强的对象写法

Symbol 类型还可以用于定义一组常量,保证这组常量的值都是不相等的。

1
2
3
4
5
6
7
8
9
const log = {};

log.levels = {
DEBUG: Symbol('debug'),
INFO: Symbol('info'),
WARN: Symbol('warn')
};
console.log(log.levels.DEBUG, 'debug message');
console.log(log.levels.INFO, 'info message');

下面是另外一个例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
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');
}
}

常量使用 Symbol 值最大的好处,就是其他任何值都不可能有相同的值了,因此可以保证上面的switch语句会按设计的方式工作。

还有一点需要注意,Symbol 值作为属性名时,该属性还是公开属性,不是私有属性。

8.可变类型与不可类型

注:数据类型动态性 定义变量只是赋值,但过程中没有确定数据类型,在使用时在确定数据类型

(1) 基础数据类型是不可变:

[1] 值比较
  • 基础数据类型,是值的比较
1
2
3
4
5
var a = 1;

var b = 1;

a === b // true
[2] 动态的属性
  • 不可以添加属性和方法,也不能改变和删除属性和方法
1
2
3
4
5
var a = 'test';

a.name = 'helloworld';

console.log(a.name); // undefined
[3] 复制变量值

从一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值复制为新变量分配的位置上。

1
2
3
4
5
var num1 = 1;

var num2 = num1; // num1: 1 num2: 1

num2 = 3; // num1: 1 num2: 3
[4] 传递参数
  • 传递参数的方式为值传递

  • 向参数传递基本类型,被传递的值会复制给一个局部变量,因此这个局部变量的变化会反应在函数外部。

1
2
3
4
5
6
7
8
9
10
11
12
function addTen(num) {
num += 10;
return num;
}

var count = 20;

var result = addTen(count);

console.log(count); // 20

console.log(result); // 30

(2) 引用数据类型可变

[1] 值比较
  • 引用比较,独立对象不相等
1
2
3
4
5
var obj1 = {a: 1};

var obj2 = {a:1};

obj1 === obj2 // false
[2] 动态的属性
  • 可以添加属性和方法,也不能改变和删除属性和方法
1
2
3
4
5
var a = {};

a.name = 'helloworld';

a.name; // 'helloworld'
[3] 复制变量值

从一个变量向另一个变量复制引用类型的值,同样会将存储在变量对象中的值复制一份放到新变量分配的空间中。不同的是,这个值的副本实际上是一个指针,复制结束后,两个变量实际上将引用同一个对象。
因此,改变其中一个,会影响另一个。

1
2
3
4
5
6
7
8
9
var obj1 = {a: 1};

var obj2 = obj1

obj2.a = 2;

obj1.a // 2

obj2.b // 2
[4] 传递参数
  • 传递参数的方式为值传递

  • 向参数传递引用类型,被传递的对象的引用会复制给一个局部变量,因此外部和函数内部的变量指向同一引用,因此内部会影响外部:

1
2
3
4
5
6
7
8
9
function setName(obj) {
obj.name = "Neo";
}

var person = new Object();

setName(person);

console.log(person.name); // Neo
Share