当前位置:   article > 正文

第一节 数据类型与结构

第一节 数据类型与结构

代码,是前端工程师的“武器”,也是他们的“面包和黄油”。


一、数据类型

ECMAScript 标准定义了原始数据类型和引用数据类型,共七种内置类型:

原始数据类型(基本类型):按值访问,可以操作保存在变量中实际的值。

  • 空值(null)
  • 未定义(undefined)
  • 布尔值(boolean)
  • 数字(number)
  • 字符串(string)
  • 符号(symbol)

引用类型(复杂数据类型)引用类型的值是保存在内存中的对象。

  • 对象(Object)
    • 布尔对象(Boolean)
    • 数字对象(Number)
    • 字符串对象(String)
    • 函数对象(Function)
    • 数组对象(Array)
    • 日期对象(Date)
    • 正则对象(RegExp)
    • 错误对象(Error)
    • 映射(Map),以[键,值]的形式存储,是一些元素的集合。每个元素有一个称作key 的域,不同元素的key 各不相同
    • 集合(Set),以[值,值]的形式存储元素。由一堆无序的、相关联的,且不重复的内存结构【数学中称为元素】组成的组合

⚠️ 注意:与其他语言不同的是,JavaScript 不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。所以引用类型的值是按引用访问的。

原始数据类型

Null 空值

Null 类型只有一个唯一的字面值 null,表示一个指针对象,这也是在使用 typeof 运算符检测 null 值时会返回“object”的原因。

在定义将来要保存对象值的变量时,建议使用 null 来初始化,不要使用其他值。原因:只要检查这个变量的值是不是 null 就可以知道这个变量是否在后来被重新赋予了一个对象的引用。

null 是表示缺少的标识,指示变量未指向任何对象。把 null 作为尚未创建的对象,也许更好理解。

常见出现 null 的场景:

  1. 一般情况下,如果申明的变量时是为了以后保存某个值,则应该在申明是就将其复制为“null”。
let returnObj = null;

function foo(){
  return {
    name: 'king'
  };
}
returnObj = foo();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  1. JavaScript 在获取 DOM 元素时,如果没有获取到指定的元素对象,就会返回“null”。
document.querySelector('#id');  // null
  • 1
  1. 在使用正则表达式进行捕获时,如果没有捕获结果,就会返回“null”。
'test'.match(/a/);  // null
  • 1

Undefined 未定义

Undefined 类型只有一个唯一的字面值 undefined,表示的是一个变量不存在。任何变量在赋值前都是 Undefined 类型、值为 undefined。同时也是全局对象的一个属性(window.undefined)。

常见出现 undefined 的场景:

  1. 使用只申明而未初始化的变量时,会返回“undefined”。
var foo;
console.log(foo);  // undefined
  • 1
  • 2
  1. 获取一个对象的某个不存在的属性(自身属性和原型链继承属性)时,会返回“undefined”。
var obj = {
  name: "king"
};
console.log(obj.address)  // undefined
  • 1
  • 2
  • 3
  • 4
  1. 函数没有明确的返回值时,却在其他地方使用了返回值,会返回“undefined”。
function foo(){}
console.log(foo());  // undefined
  • 1
  • 2
  1. 函数定义时使用了多个形式参数,而在调用时传递的参数数量少于形参数量,那么未匹配上的参数就为“undefined”。
function foo(param1, param2, param3){
  console.log(param3);
}
foo(1, 2)  // undefined
  • 1
  • 2
  • 3
  • 4

Boolean 布尔值

布尔类型表示一个逻辑实体,可以有两个值:true 和 false

如果变量不是 Boolean 类型的值,那么JavaScript 解释器会自动调用 Boolean() 函数对变量进行类型转换,返回最终符合 if 语句判断的 true 或者 false 值。

new Boolean()
nullundefinedfalse0-0NaN""  // false
true"false"、Object、{}Array[]Function   // true
  • 1
  • 2
  • 3

Number 数字

decimals-小数

intergers-整数

进制数

  • 二进制:计算机的最基础语言——0 和 1
  • 八进制:基数8,每列使用0-7,第一位必须是 0
  • 十进制:JavaScript 中默认的进制数
  • 十六进制:基数16,每列使用0-9,然后使用A-F(字母不区分大小写),前两位必须是 0x
// 八进制的56
var num2 = 070;

// 十进制
var num1 = 10;

// 十进制,因为有数字超过了7,这里是79
var num3 = 079;

// 十六进制的31
var num4 = 0x1f;

// 进制转换(带参数)
Number.toString(2/8/16)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

⚠️ 注意: 八进制在严格模式下 “use strict” 是无效的,会导致 JavaScript 报错,避免使用。

浮点数-Float

var num = 0.1 + 0.2;
var sum = '2.3' * 100;

console.log(num);
// 0.30000000000000000004

console.log(sum);
// 229.99999999999997
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

上面例子表达的就是 JavaScript 的浮点型数据在计算时容易丢失精度,这一点并不仅在 JavaScript 存在,建议处理这方面问题使用专用的数字处理类,比如 Java 里的 BigDecima 类来处理。

双精度

双精度是一种特定类型的浮点数,它们具有比标准浮点数更高的精度(这意味着它们精确到更大的小数位数)。

Infinity - 数字的范围

JavaScript 中数值的范围是有效位数的,基本上够我们使用,我们仅需要知道以下几个知识点:

  • Number.MIN_VALUE 或 Number.NEGATIVE_INFINITY:表示 JavaScript 中的最小值
  • Number.MAX_VALUE 或 Number.POSITIVE_INFINITY:表示 JavaScript 中的最大值
  • Infinity:表示无穷大
  • -Infinity:表示无穷小
    NaN - 非数值

NaN 属于 JavaScript 保留词,指示某个数不是合法数,用于表述本来要返回数值的操作失败了(而不是抛出错误)

let x = 100 / "Apple";  // x 将是 NaN(Not a Number)

// 不过,假如字符串包含数值,则结果将是数:
let x = 100 / "10";     // x 将是 10
  • 1
  • 2
  • 3
  • 4

NaN (Not a number)的含义是本该返回数值的操作未返回数值,返回了 NaN 就不会抛出异常影响语句流畅性。

NaN 属性的初始值就是 NaN,和 Number.NaN 的值一样。

在现代浏览器中(ES5 环境), NaN 属性是一个不可配置(non-configurable)、不可写(non-writable)的属性。但在 ES3 中,这个属性的值是可以被更改的,但是也应该避免覆盖。

编码中很少直接使用到 NaN。通常都是在计算失败时,作为 Math 的某个方法的返回值出现的(例如:Math.sqrt(-1))或者尝试将一个字符串解析成数字但失败了的时候(例如:parseInt(“blabla”))。

类型转换

Nmuber() 函数

1-Boolean 类型
true => 1
false => 0

2- Null 类型
null => 0

3- Unedfined 类型
undefined => NaN

4- String 类型
只包含数字 => 十进制数
只包含有效的浮点数 => 十进制数
包含有效的十六进制数 => 十六进制数
空字符串 => 0
包含除以上格式以外而定字符串 => NaN

5- Object 类型
先调用 valueOf()函数—返回值按照以上规则,如果结果是NaN,则调用 toString()函数
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

常见应用

  1. JavaScript 数值始终是 64 位的浮点数
  2. 精度:
  • 整数(不使用指数或科学计数法)会被精确到 15 位。
let x = 999999999999999;   // x 将是 999999999999999
let y = 9999999999999999;  // y 将是 10000000000000000 被四舍五入
  • 1
  • 2
  • 小数的最大数是 17 位,但是浮点的算数并不总是 100% 精准
let x = 0.2 + 0.1;         // x 将是 0.30000000000000004
  • 1
  1. 数字和字符串相加
    JavaScript 的加法和级联(concatenation)都使用 + 运算符。数字用加法。字符串用级联。
let x = 10, y = "20";
let z = x + y;           // z 将是 1020(一个字符串)

let x = 10, y = 20, z = "30";
let result = x + y + z;  // 3030
  • 1
  • 2
  • 3
  • 4
  • 5

JavaScript 从左向右进行编译。因为 x 和 y 都是数,10 + 20 将被相加。因为 z 是字符串,30 + “30” 被级联。
4. 数字字符串
在所有数字运算中,JavaScript 会尝试将字符串转换为数字:

let x = "100", y = "10";
let z = x / y;       // z 将是 10
  • 1
  • 2
let x = "100", y = "10";
let z = x - y;      // z 将是 90
  • 1
  • 2
let x = "100", y = "10";
let z = x + y;       // z 不会是 110(而是 10010)
  • 1
  • 2

浮点数计算

String 字符串

JavaScript 的字符串类型用于表示文本数据。它是一组 16 位的无符号整数值的元素。在字符串中的每个元素占据了字符串的位置。第一个元素的索引为 0,下一个是索引 1,依此类推。字符串的长度是它的元素的数量。

'foo';
'bar';
'1234';
'one line \n another line';
"John's cat";
  • 1
  • 2
  • 3
  • 4
  • 5

Symbol 符号

符号(Symbols)是 ECMAScript 第 6 版新定义的数据类型。符号是原始值,且符合实例是唯一、不可变的。符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险。

let genericSymbol = Symbol();
let otherGenericSymbol = Symbol();
let fooSymbol = Symbol('foo');
let otherFooSymbol = Symbol('foo');

console.log(genericSymbol == otherGenericSymbol )  // false
console.log(fooSymbol == otherFooSymbol)  // false
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

该类型的性质在于这个类型的值可以用来创建匿名的对象属性。该数据类型通常被用作一个对象属性的键值,当这个属性是用于类或对象类型的内部使用的时候。

var myPrivateMethod = Symbol();

this[myPrivateMethod] = function () {
  // ...
};
  • 1
  • 2
  • 3
  • 4
  • 5
let s1 = Symbol('foo'),
    s2 = Symbol('bar');

let o = {
  [s1]: 'foo val',
  [s2]: 'bar val',
  baz: 'baz val'
}
console.log(o)
// {Symbol(foo): foo val, ..., }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

引用数据类型

引用类型通常叫做类(Class),也就是说,遇到引用值,所处理的就是对象。

在 ECMA-262 标准中根本没有出现 类 这个词,而是定义了 对象定义,逻辑上等价于其他程序设计语言中的类。

对象是由 new 运算符加上要实例化的对象的名字创建的。

例如,下面的代码创建 Object 对象的实例:

var o = new Object();
  • 1

这种语法与 Java 语言的相似,不过当有不止一个参数时,ECMAScript 要求使用括号。

如果没有参数,如以下代码所示,括号可以省略:

var o = new Object();
  • 1

尽管括号不是必需的,但是为了避免混乱,最好使用括号。

Object 对象

对象定义为一组属性的无序集合。严格来说,这意味着对象就是一组没有特定顺序的值。

属性和方法

  • constructor:用于创建当前对象的函数。
  • hasObjectProperty:用于潘队当前对象实例上是否存在给定的属性,要检查的属性名必须是字符串或符号
  • isPropertyOf:用于潘队当前对象是否为另一个对象的原型
  • propertyIsEnumerable:用于判断给定的属性是否可以使用 for-in 语句枚举,属性名必须是字符串或符号
  • toLocalString:返回对象的字符串表示,该字符串反映对象所在的本地化执行环境
  • toString:返回对象的字符串表示
  • valueOf:返回对象对应的字符串、数值或布尔值表示。通常与 toString 返回值相同
属性的类型

属性分为两种:数据属性和访问器属性。

数据属性

数据属性包含一个保存数据值的位置。值会从这个位置读取,也会写入到这个位置。数据属性有4个特性描述它们的行为。

  • [[Configurable]]:表述属性是否可以通过 delete 删除并重新定义,是否可以修改它的特性,以及是否可以把它改为访问器属性。默认情况下,所有直接定义在对象的属性的这个特性都是 true。
  • [[Enumerable]]:表述属性是否可以通过 for-in 循环返回。默认情况下,所有直接定义在对象的属性的这个特性都是为 true。
  • [[Writable]]:表述属性的值是否可以被修改。默认情况下,所有直接定义在对象的属性的这个特性都是为 true。
  • [[Value]]:包含属性实际的值。读取和写入属性值的位置。这个特性的默认值为 undefined。
    属性默认特性的修改
Object.defineProperty(obj, key, descriptor)
  • 1

访问器属性

访问器属性不包含数据值。相反,它们包含一个获取(getter)函数和一个设置(setter)函数,不过这个两个函数不是必需的。

在读取访问器属性时,会调用获取函数,这个函数的责任就是返回一个有效值。在写入访问器属性时,会调用设置函数并传入新值,这个函数必须决定对数据做出什么修改。

  • [[Configurable]]:表述属性是否可以通过 delete 删除并重新定义,是否可以修改它的特性,以及是否可以把它改为访问器属性。默认情况下,所有直接定义在对象的属性的这个特性都是 true。
  • [[Enumerable]]:表述属性是否可以通过 for-in 循环返回。默认情况下,所有直接定义在对象的属性的这个特性都是为 true。
  • [[Get]]:获取函数,在读取属性时调用。默认值为undefined。
  • [[Set]]:设置函数,在写入属性时调用。默认值为undefined。
    访问器属性是不能直接定义的,必须使用 Object.defineProperty()

合并对象

方法接收一个目标对象和一个或多个源对象作为参数,然后将每个源对象中可枚举和自有属性赋值到目标对象。以字符串和符号为键的属性会被复制。

Object.assign(targetObject, sourceObject, ...)
  • 1

Object.assign() 实际上对每个源对象执行的是浅复制。如果多个源对象都有相同的属性,则使用最后一个复制的值。

对象标识及相等判定

Object.is(Obj1, Obj2)
  • 1

增强的对象语法

  • 属性值简写
  • 简写方法名
    对象解构

使用与对象匹配的解构来实现对属性的赋值。

Array 数组

数组检测

ECMAScript5 将 Array.isArray() 正式引入 JavaScript,该方法能准确检测一个变量是否为数组类型。

Array.isArray(variable);
  • 1
数组方法

filter() 过滤

filter() 函数接收一个函数作为其参数,返回值为“true”的元素会被添加至新的数组中,返回值为“false”的元素则不会添加至新的数组中,最后返回这个新数组。如果没有符合条件的值则返回空数组。

针对简单类型的数组

let filterFn = function(x){
  return x % 2;
}

let arr = [1, 2, 3, 5, 6]
let result = arr.filter(filterFn)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

针对复杂类型而定数组

let filterFn = function(obj){
  return obj.age > 18 && obj.gender === "男";
}

let arrObj = [
  {
    gender: '男',
    age: 17
  },
  {
    gender: '女',
    age: 19
  }
]
let result = arrObj.filter(filterFn)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

reduce() 累加

reduce() 函数最主要的作用是做累加处理,即接收一个函数作为累加器,将数组中的每一个元素从左到右依次执行累加器,返回最终处理结果。

arr.reduce(callback(accumulator, currentValue, currentIndex?, array?)[, initialValue]);
  • 1
  • accumulator: 表示上一次调用累加器的返回值,或者设置的 initialValue 值。如果设置了 initialValue,则accumulator=initialValue;否则accumulator=数组的第一个元素值。
  • currentIndex:表示当前正在处理值而定索引。如果设置了 initialValue,则 currentInedx 从 0 开始,否则从 1 开始。
    常见应用场景
  1. 求数组每个元素相加的和
  2. 统计数组中每个元素出现的次数
let countOccurrences = function(arr){
  return arr.reduce(function(accumulator, currentValue){
    accumulator[currentValue]? accumulator[currentValue]++ : accumulator[currenValue]=1;
    return accumulator;
  },{});
};

countOccurences([1, 2, 3, 4, 5, 1])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  1. 多维统计数据
    问题描述:不同币值汇率的情况下,将一组人民币的值分别换算成美元和欧元的等量值。
  • 美元汇率:1: 0.1478
  • 欧元汇率为:1: 0.1265
let items = [{price: 10}, {price: 50}, {price: 100}];

let reducers = {
  totalInEuros: function(state, item){
    return state.euros += item.price*0.1265;
  },
  totalInDollars: function(state, item){
    return state.euros += item.price*0.1478;  
  },
};

let manageReducers = function(reducers){
  return function(state, item){
    function(nextState, key){
      reducers[key](state, item);
      return state;
    }
  }
}
let bigTotalPriceReducer = manageReducers(reucers);
let initialState = {euros: 0, dollars: 0};
let totals = items.reduce(bigTotalPriceReducer, initialState);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

Date 日期

let now = new Data()
  • 1

Map 映射

Map 是一种新的集合类型,实现“键/值”式存储机制,键可以为任意数据类型。

与 Object 类型的一个主要差异是,Map 实例会维护键值对的插入顺序,因此可以根据插入顺序执行迭代操作。

const m = new Map();

// 使用嵌套数组初始化映射
const m1 = new Map([
  ["key1", "val1"];
  ["key2", "val2"];
  ["key3", "val3"];
]);

// 使用自定义迭代器初始化映射
const m2 = new Map({
  [Symbol.iterator]: function*(){
    yield ["key1", "val1"];
    yield ["key2", "val2"];
    yield ["key3", "val3"];
  }
})

// 实例方法
set("key", "val");  // 返回映射实例
get("key");
has("key");
delete("key");
clear("key");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

WeakMap 弱映射

弱映射中的键只能是 Object 或者继承自 Object 的类型,尝试使用非对象设置键会抛出 TypeError。值的类型没有限制。

const key1 = {id:1},
      key2 = {id:2},
      key3 = {id:3},
const wm1 = new WeakMap([
  ["key1", "val1"];
  ["key2", "val2"];
  ["key3", "val3"];
]);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Set 集合

Set 会维护值插入时的顺序,因此支持按准许迭代。集合

// 使用数组初始化集合
const s1 = new Set(["val1", "val2", "val3"])

// 使用自定义迭代器初始化集合
const s2 = new Set({
  [Symbol.iterator]: function*(){
    yield "val1";
    yield "val2";
    yield "val3";
  }
})

// 实例方法
add()
has()
delete()
clear()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

WeakSet 弱集合

Arrary、Map、Set 定义了默认迭代器,因此都兼容扩展操作符。扩展操作符在对可迭代对象执行浅复制时特别有用。

let arr1 = [1, 2, 3];
let arr2 = [...arr1]
console.log(arr1 === arr2);  // false
  • 1
  • 2
  • 3

二、类型检测

类型检测的方法:

  1. typeof
  2. instanceof
  3. Object.prototype.toString.call(xx)
  4. constructor

typeof

typeof 操作符返回一个字符串,表示未经计算的操作数的类型。

// 原始数据类型
typeof 'foo';  // "string"
typeof 100;  // "number"
typeof NaN;  // "number"
typeof true;  // "boolean"
typeof undefined; // "undefined"
typeof myCar; // "undefined" (如果 myCar 没有声明)
typeof null;  // "object"
typeof Symbol();   // "symbol"

// 引用数据类型
typeof {}; // "object"
typeof new Object();  // "object"
typeof {name:'John', age:34};  // "object"
typeof []; // "object"
typeof [1, 2];  // "object"
typeof function () {};  // "function"
typeof new Date();  // "object"
typeof new RegExp();  // "object"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

typeof 操作符适合对 基本类型(除 null 之外)及 function 的检测使用,而对引用数据类型(如 Array)等不适合使用。

instanceof

① instanceof 运算符用于检测一个对象在其 原型链 中是否存在一个构造函数的 prototype 属性,返回一个布尔值。

左操作数为对象,不是就返回 false,右操作数必须是 函数对象 或者 函数构造器,不是就返回 TypeError 异常。

obj instanceof constructor;
  • 1

instanceof 左边值的__proto__ 是否能找到 instanceof 右边值的 prototype,如果能找到就返回 true 。

function Animal() {}
var ani = new Animal();
console.log(ani instanceof Animal); // true
console.log(ani.__proto__ === Animal.prototype); // true
console.log(ani.constructor); // ƒ Animal() {}
console.log(Animal.constructor); // ƒ Function() { [native code] }
      
console.log(ani);
console.log(Animal.prototype);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

② instanceof 操作符可以正确的判断对象的类型:instanceof 操作符只能正确判断引用数据类型,而不能判断基本数据类型。

console.log(2 instanceof Number);                    // false
console.log(true instanceof Boolean);                // false 
console.log('str' instanceof String);                // false 
 
console.log([] instanceof Array);                    // true
console.log(function(){} instanceof Function);       // true
console.log({} instanceof Object);                   // true
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

➂ instanceof 运算符用于测试构造函数的 prototype 属性是否出现在对象的原型链中的任何位置。

  • 可以检测某个对象是否是另一个对象的 实例
  • 可以检测父类型。
function Person() {}
function Student() {}
Student.prototype = new Person();
Student.prototype.constructor = Student;

const ben = new Student();
ben instanceof Student;
// true

const one = new Person();
one instanceof Person;
// true
one instanceof Student;
// false
ben instanceof Person;
// true
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

任何一个构造函数都有一个 prototype 对象属性,这个对象属性将用作 new 实例化对象的原型对象。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/468185
推荐阅读
相关标签