前端重点之数据处理

1,389 阅读9分钟

前言

本文主要对工作中常用的数据类型的判断遍历转化三方面进行归纳总结,也是面试中经常会遇到的考点,主要有以下几种数据:

  1. Number
  2. String
  3. Symbol
  4. Set/Map
  5. Function
  6. Array(重点)
  7. Object(重点)

一、Number

1. 判断

1.1 Number.isNaN()

判断是否为NaN

Number.isNaN(NaN)      // true
isNaN( 'NaN' )         // true
Number.isNaN( 'NaN' )  // false  已对isNaN方法进行修正

1.2 Number.isInteger()

判断是否为整数

Number.isInteger(25) // true
Number.isInteger(25.0) // true
Number.isInteger(25.1) // false

// 或者
function integer (num) {
    return typeof num === 'number' && num%1 === 0
}
integer(25)    // true
integer(25.1)  // false

2. 转化

2.1 Number.parseInt()/Number.parseFloat()

ES6已将parseInt和parseFloat移植到Number对象上

// ES5的写法
parseInt('12.34')     // 12
parseFloat('123.45#') // 123.45

// ES6的写法
Number.parseInt('12.34')     // 12
Number.parseFloat('123.45#') // 123.45

Number.parseInt === parseInt     // true
Number.parseFloat === parseFloat // true

2.2 toFixed()

保留指定小数位,会四舍五入,注意小数点与点运算符的区分

(5.673).toFixed(1)  // 5.7
(5.673).toFixed(2)  // 5.67

2.3 Math.round/Math.ceil/Math.floor/Math.trunc

// 四舍五入
Math.round(3.2)  // 3
Math.round(5.6) // 6

// 向上取整
Math.ceil(3.2)  // 4
Math.ceil(5.6)  // 6

// 向下取整
Math.floor(3.2)  // 3
Math.floor(5.6)  // 5

// 去除小数部分,返回整数部分
Math.trunc(4.9)     //  4
Math.trunc(-4.9)    // -4
Math.trunc(-0.1234) // -0

二、String

1. 判断

1.1 includes() (数组也有此方法)

'abc'.includes('a')  // true

1.2 indexOf() 参数只能为字符串(数组也有此方法)

'abc'.indexOf('b')  // 1

1.3 search() 参数可为字符串或正则

'abc'.search('b')   // 1

1.4 match()

'abc'.match('b')   // ['b']

2. 转化

3.1 trim()

去除前后空格

' abc  '.trim()   // 'abc'

3.2 split()

以指定符号将字符串拆分为数组

'a b   c'.split(' ');  
// ['a', 'b', '', '', 'c']   b和c之间有三个空格,所以三个空格会得到夹着的两个空格。

3.3 replace()

// 大小写互换
function change (str) {
    return str.replace(/([a-z]*)([A-Z]*)/g, (m, m1, m2) => {
        return m1.toUpperCase() + m2.toLowerCase()
    })
}
let str = 'Aabb_Cc'
change(str)   // "aABB_cC"

3.4 concat() (数组也有此方法)

'a'.concat('bc')  // 'abc'

3.5 slice(位置,位置) (数组也有此方法)

'abcde'.slice(1, 2)   // 'b'  

3.6 substring(位置,位置)

// 参数如果是负数,会变为0,一参大于二参则会互换位置
'abcde'.substring(2, -1)   // 'ab'   相当于substring(0, 2)  

3.7 substr(位置,长度)

'abcde'.substr(1, 2)   // 'bc'  

三、Symbol

独一无二的值,直接调用,不使用new

let s = Symbol()
typeof s  // "symbol"

1. 遍历

1.1 Object.getOwnPropertySymbols()

const obj = {}
let a = Symbol('a')
let b = Symbol('b')

obj[a] = 'Hello'
obj[b] = 'World'

const objectSymbols = Object.getOwnPropertySymbols(obj)

objectSymbols  // [Symbol(a), Symbol(b)]

1.2 Reflect.ownKeys()

返回所有类型的键名

let obj = {
    [Symbol('my_key')]: 1,
    enum: 2,
    nonEnum: 3
};

Reflect.ownKeys(obj)  //  ["enum", "nonEnum", Symbol(my_key)]

四、Set/Map

1. Set

类似于数组,无重复值,参数为具有iterable接口的数据。

1. 判断

  • ①add(value):添加某个值,返回 Set 结构本身。
  • ②delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
  • ③has(value):返回一个布尔值,表示该值是否为Set的成员。
  • ④clear():清除所有成员,没有返回值。

2. 遍历

2.1 返回遍历器

  • ①keys():返回键名的遍历器
  • ②values():返回键值的遍历器
  • ③entries():返回键值对的遍历器
  • ④forEach():使用回调函数遍历每个成员

3. 转化

3.1 去重

// 数组去重
[...new Set([1, 1, 2])]   // [1, 2]

// 字符串去重
[...new Set('ababbc')].join('')  // 'abc'

3.2 并集/交集/差集

let a = new Set([1, 2, 3])
let b = new Set([4, 3, 2])

// 并集
let union = [...new Set([...a, ...b])[]  // [1, 2, 3, 4]
// 或者
function uni (array) {
    let arr = []
    array.forEach(x => {
        if (!arr.includes(x)) {
            arr.push(x)
        } 
    })
    return arr
}
uni([...[1, 2, 3], ...[4, 3, 2]])  //  [1, 2, 3, 4]

// 交集
let intersect = [...new Set([...a].filter(x => b.has(x)))]  // [2, 3]
// 或者
[1, 2, 3].filter(x => [4, 3, 2].includes(x))  // [2, 3]

// 差集
let difference = [...new Set([...a].filter(x => !b.has(x)))] // [1]
// 或者
[1, 2, 3].filter(x => ![4, 3, 2].includes(x))  // [1]

2. Map

类似于对象,也是键值对的集合,但“键”的范围不限于字符串。

const map = new Map([
  ['name', '张三'],
  ['title', 'Author']
]);
map       // {"name" => "张三", "title" => "Author"}

1. 判断

  • ①size属性
  • ②set(key, value)
  • ③get(key)
  • ④has(key)
  • ⑤delete(key)
  • ⑥clear()

2. 遍历

  • keys():返回键名的遍历器。
  • values():返回键值的遍历器。
  • entries():返回所有成员的遍历器。
  • forEach():遍历 Map 的所有成员。

五、Function

1. 判断

1.1 Function.prototype是函数,由JS引擎生成

函数.prototype.__proto__ === Object.prototype

Object instanceof Object // true            Object.__proto__===Function.prototype    Function.prototype.__proto__===Object.prototype
Function instanceof Function // true     Function.__proto__===Function.prototype
Function instanceof Object // true        Function.__proto__===Function.prototype   Function.prototype.__proto__===Object.prototype

1.2 this指向

ES5中this永远指向最后调用它的那个对象,可使用call,apply,bind改变指向

// 手写call
Function.prototype.mycall = function (obj) {
    if (typeof this !== 'function') {
        throw new TypeError('not funciton')
    }
    obj = obj || window
    obj.fn = this
    let arg = [...arguments].slice(1)
    let result = obj.fn(...arg)
    delete obj.fn
    return result
} 

// 手写apply
Function.prototype.myapply = function (context) {
    if (typeof this !== 'function') {
        throw new TypeError('not funciton')
    }
    context = context || window
    context.fn = this
    let result
    if (arguments[1]) {
        result = context.fn(...arguments[1])
    } else {
        result = context.fn()
    }
    delete context.fn
    return result
}

// 手写bind
Function.prototype.mybind = function (context) {
    if (typeof this !== 'function') {
        throw new TypeError('Error')
    }
    let _this = this
    let arg = [...arguments].slice(1)
    // 函数是可以new的,所以要处理这个问题,使用组合继承
    return function F() {
        if (this instanceof F) {
            return new _this(...arg, ...arguments)
        } else {
            return _this.apply(context, arg.concat(...arguments))
        }
    }
}

ES6的箭头函数this指向定义它的对象,或者是箭头函数所在的函数作用域

let obj = {
    a: 1,
    fn: () => {
        console.log(this.a)
    }
}
obj.fn()  // undefined 对象中的箭头函数的this指向的是window,所以不要在对象中使用箭头函数

六、Array

1. 判断

1.1 是否为数组

  1. Array.isArray()
  2. Object.prototype.toString.call() 万能法
  3. instanceof
// 原理:实例的原型链上是否存在右侧变量的原型
function instanceOf(left, right) {
    let leftValue = left.__proto__
    let rightValue = right.prototype
    while(true) {
        if (leftValue === null) {
            return false
        }
        if (leftValue === rightValue) {
            return true
        }
        leftValue = rightValue.__proto__
    }
}

1.2 includes(包含项, 起始位置)

判断数组是否包含某个值,返回的是布尔值

[1, 2, 3].includes(2) // true

2. 遍历

2.1 原数组不变

一参为函数(value, index, arr) => {}, 二参为this指向

  • map() 返回由rerurn值组成的数组,多用于将数据处理为需要的格式

  • filter() 返回由return true的值组成的数组

  • every() 返回布尔值

  • some() 返回布尔值

  • forEach() 同for循环,无返回值

  • reduce() 多值变为单值

// 返回最大值
[1, 2, 3].reduce((x, y) => x > y ? x : y )  // 3

// 返回最长字符串
['a', 'abc', 'ab'].reduce((x, y) => x.length > y.length ? x : y)  // 'abc'

// 求和
[1, 2, 3].reduce((x, y) => x + y)  // 6

多用于拼接拆分

  • join() 指定字符将数组拼接为字符串

  • slice() 截取数组或字符串

  • concat() 连接数组或字符串

for循环

  • for

  • for in 多用于键

  • for of 多用于值,对象不可用

2.2 原数组改变

  • reverse() 反序

  • sort() 传入排序参数

// 升序
[1, 3, 2].sort((x, y) => x - y)    // [1, 2, 3]

// 乱序
[1, 2, 3].sort(() => Math.random() - 0.5)    // [2, 3, 1]
  • splice(位置,个数,增加值) 可删除或增加,返回删除值

  • push() 返回长度

  • pop() 返回删除值

  • unshift() 返回长度

  • shift() 返回删除值

2.1 find((value, index, arr) => {}, this)

返回第一个符合条件的数组成员

[1, 4, -5, 10].find(n => n < 0)
// -5  注意不是返回数组

2.2 findIndex((value, index, arr) => {}, this)

返回第一个符合条件的数组成员的位置

[1, 4, -5, 10].findIndex(n => n < 0)
// 2 

2.3 keys()、values()、entries()

返回数组的遍历器对象,可用for of或扩展运算符将值遍历出来

for (let index of ['a', 'b'].keys()) {
  console.log(index);
}
// 0
// 1

for (let elem of ['a', 'b'].values()) {
  console.log(elem);
}
// 'a'
// 'b'

for (let [index, elem] of ['a', 'b'].entries()) {
  console.log(index, elem);
}
// 0 'a'
// 1 'b'

let arr = [1,2,3]; 
[...arr.values()];     // [1,2,3] 
[...arr.keys()];       // [0,1,2] 
[...arr.entries()];    // [ [0,1], [1,2], [2,3] ] 

3. 转化

3.1 解构赋值

  1. 设置变量和默认值,undefined为无值,null为有值
let [a, b, c = 3] = [1, [2], undefined]
a  // 1
b  // [2]
c  // 3
  1. 交换变量值
let x = 1;
let y = 2;
[x, y] = [y, x];

3.2 类数组转数组

  • DOM对象arguments类数组具有length属性的对象字符串等类数组转为真正的数组。
  1. Array.from()
  • Array.from支持转化具有Iterator接口的对象,同时还支持转化无Iterator接口但类似数组的对象,即带有length属性的对象。
Array.from('hello')   // [ "h", "e", "l", "l", "o" ] 
  1. Array.prototype.slice.call()
Array.prototype.slice.call('hello')  // [ "h", "e", "l", "l", "o" ] 
  1. [...类数组]
  • 扩展运算符内部调用的是数据结构的Iterator接口,因此只有Iterator接口的对象才可以用扩展运算符转为真正的数组。
[...'hello']   // [ "h", "e", "l", "l", "o" ]   
  1. split
'hello'.split('')  // [ "h", "e", "l", "l", "o" ]  

3.3 数组拷贝

  1. [...arr]
let a = [...[1, 2]]   // [1, 2] 
  1. Object.assign()
let a = Object.assign([], [1, 2])  // [1, 2]
  1. slice()
let a = [1, 2].slice()  // [1, 2]
  1. concat()
let a = [].concat([1, 2])  // [1, 2]

3.4 合并数组

  1. concat
// 注意concat的参数如果传的是数组,则会将数组的成员取出来与前者合并,而不是合并这个数组
[1].concat(2)  // [1, 2]
[1].concat([2])  // 得到[1, 2],而不是[1, [2]]
  1. [...arr1, ...arr2]
[...[1, 2], ...[3, 4]]  // [1, 2, 3, 4]

3.5 数组去重

  1. Set方法
[...new Set([1, 2, 3, 3])]  // [1, 2, 3]
  1. 数组遍历法
function uni (array) {
    let arr = []
    array.forEach(x => {
        if (!arr.includes(x)) {
            arr.push(x)
        } 
    })
    return arr
}

uni([1, 2, 3, 3])  // [1, 2, 3]
  1. 数组下标法
function uni (data) {
    return data.filter((value, index, arr) => arr.indexOf(value) === index)
}

uni([1, 2, 3, 3])  // [1, 2, 3]
  1. 对象键值法
function uni (array) {
    let obj = {}
    let arr = []
    array.forEach(x => {
        if (!obj[x]) {
            arr.push(x)
            obj[x] = true
        }
    })
    return arr
}

uni([1, 2, 3, 3])  // [1, 2, 3]

3.6 数组排序

3.7 数组拉平

  1. flat(层数)

将数组拉平

// 可设置拉平层数
[1, 2, [3, [4, 5]]].flat(1)   // [1, 2, 3, [4, 5]]

// 层数不限使用Infinity
[1, [2, [3]]].flat(Infinity)  // [1, 2, 3]
function flat(arr){
    let array = []
    function handle(data) {
        if (Array.isArray(data)) {
            data.map(item => {
                Array.isArray(item) ? handle(item) : array.push(item)
            })
        } else {
            throw new TypeError('should be array')
        } 
    }
    handle(arr)
    return array
} 

flat([1, [2, [3]]])   // [1, 2, 3]

七、Object

1. 判断

1.1 Object.is()

比较两个值是否相等,为===

+0 === -0      //true
NaN === NaN    // false

Object.is(+0, -0)    // false
Object.is(NaN, NaN)  // true

1.2 Reflect.has()

同in作用

Reflect.has(Object, 'toString') 

2. 遍历

2.1 in (可遍历自身及原型上的可枚举和不可枚举属性)

2.2 for in (可遍历自身及原型上的可枚举属性)

2.3 Object.getOwnPropertyNames (可遍历自身可枚举和不可枚举属性)

2.4 Object.hasOwnProperty() (可遍历自身可枚举和不可枚举属性)

2.5 Object.keys/Object.values/Object.entries (可遍历自身可枚举属性)

2.6 Reflect.ownKeys() (等同于Object.getOwnPropertyNames与Object.getOwnPropertySymbols之和)

Object.keys({ foo: 'bar', baz: 42 })   // ["foo", "baz"]

Object.values({100: 'a', 2: 'b', 7: 'c'})  // ["b", "c", "a"]
// 属性名为数值的属性,是按照数值大小,从小到大遍历的,因此返回的顺序是b、c、a

Object.entries({foo: 'bar', baz: 42})   // [ ["foo", "bar"], ["baz", 42] ]

3. 转化

3.1 解构赋值

// 设置变量及别名
let { x, y:m, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x // 1
y // 报错
m // 2
z // { a: 3, b: 4 }

3.2 转为对象

  1. {...}
// 数组转对象
{...['x', 'y']}  // {0: "x", 1: "y"}

// 字符串转对象
{...'hello'}     // {0: "h", 1: "e", 2: "l", 3: "l", 4: "o"}
  1. Object.fromEntries
// 将键值对转化为对象
const entries = new Map([['foo', 'bar'], ['baz', 42]])

Object.fromEntries(entries)   // { foo: "bar", baz: 42 }

3.3 合并对象

  1. {...}
{...{x: 1}, {...{y: 2}}}  // {x: 1, y: 2}
  1. Object.assign()
Object.assign({x: 1}, {y: 2})  // {x: 1, y: 2}

3.4 对象拷贝

1. 浅拷贝

  1. {...obj}
{...{x: 1}}  // {x: 1}
  1. Objcet.assign()
Object.assign({x: 1}, {y :2}}  // {x: 1, y: 2}

2. 深拷贝

  1. JOSN.stringify()/JSON.parse()
let obj = {a: 1, b: {x: 3}}
JSON.parse(JSON.stringify(obj))  // {a: 1, b: {x: 3}}
  1. 递归拷贝
function deepClone(obj) {
    let copy = obj instanceof Array ? [] : {}
    for (let i in obj) {
        if (obj.hasOwnProperty(i)) {
            copy[i] = typeof obj[i] === 'object' ? deepClone(obj[i]) : obj[i]
        }
    }
    return copy
}