重学es6 - 第三部分 | 8月更文挑战

378 阅读7分钟

函数

函数是javascript的一等公民,类似react和vue3框架,都往偏函数式编程方向去发展. 函数式编程在前端还是有很多好处的. 除去它自己的优点, 让代码高内聚,低耦合,语义清晰,可维护程度高等,再结合现代化的构建工具,可以有效的去做tree-shaking,让项目代码压缩后,体积变小很多. 好了,那我们先复习一下es6后给我们的函数加了多少好用的东西吧~

默认参数

function hello (name = '小明') {
    console.log(`你好, 我是${name}`)
}
hello() // 你好, 我是小明

默认参数, 其实和我们之前复习的解构的默认参数的写法是一致的. 之前我们在没有默认参数的时候, 写起来代码还是很累的. 得去判断一遍参数是否有值,没有值的话,则给参数赋一下值

function hello (name, age, sex) {
    name = name || '小明'
    age = age || '18'
    sex = sex || '男'
}

展开操作符

将一个数组转为用逗号分隔的参数序列

合并数组

将两个数组合并成一个数组的话, 之前可以用contain

[].concat(one).concat(two)

es6之后我们有另外一种方法合并了. 把两个数组用...的形式在一个新数组里边展开,就得到了一个合并的数组了. 展开操作符是有浅拷贝的作用的.

const one = [1, 2, 3]
const two = [4, 5, 6]
const merge = [...one, ...two]
console.log(merge) // [1, 2, 3, 4, 5, 6]

如果你做过vue项目的话, 在vue项目中写一个路由,一般都是有一个默认路由,路由里边包括登录,注册页面等不需要权限的页面路由, 另外可能需要从后台传递一个权限过来, 然后动态加载另外一些路由. 那么就像这样

export default [
  {
    path: '/login',
    name: 'login',
    meta: {
      title: 'Login - 登录',
      hideInMenu: true
    },
    component: () => import('@/view/login/login.vue')
  },
  {
    path: '/home',
    name: 'home',
    meta: {
      title: '主页',
    },
    component: () => import('@/view/home.vue')
  },
  ...business
];

具有Iterator接口的对象转成数组

function test(a, b, c) {
    console.log(arguments) // Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
    console.log([...arguments]) // [1 2 3]
}
test(1, 2, 3)

// nodeList转成数组
var nodeList = document.querySelectorAll('div')
console.log([...nodeList])

剩余操作符

剩余操作符和展开操作符的用法是一致的, 也是通过...去找到它剩余的所有数据. 区别的话,是展开运算符用在实参上, 也就是把已知的数据展开成一个参数序列. 而剩余操作符是用在形参上. 下边举几个例子看看

// 当函数参数不确定的时候, 可以用剩余操作符
function rest(...args) {
    console.log(args) // [1, 2, 3, 4]
}
rest(1, 2, 3, 4)

剩余操作符也可以用在数组解构上

const [one, ...restArgs] = [1, 2, 3, 4]
console.log(restArgs) // [2, 3, 4]

箭头函数

// 一般箭头函数
setTimeout(() => {
    console.log('===')
}, 1000)
// 简洁写法
state => state

一般情况下,箭头函数和一般函数是一样的,相比一般函数来说,箭头函数更简洁. 如果参数只有一个的话,可以把外边括号去掉, 如果是直接return出去一个值的话,可以去掉花括号,去掉return. 相比一般的函数来说, 箭头函数的this指向是当前上下文. 所以用的时候需要比较注意一点. 大多数情况,用的会更舒服一些. 因为很多情况我们需要的this指向就是当前上下文,我们以前在没有箭头函数的时候,需要在上下文写一个var self = this, 来存储上下文的this. 有了箭头函数后,就可以直接使用this了. 但是也有例外, 比如在vue中

new Vue({
    el: '#app',
    data () {
        return {
            val: 1,
            flag: false
        }
    },
    watch: {
        flag: (val) => {
            if (val) {
                this.val = 1
            } else {
                this.val = 2
            }
        }
    }
})

如果这样写的话, 就有问题了, 因为我们在watch里边用箭头函数的话,this指向就不是vue实例了. 所以this.val就不是data里边的val了. 所以赋值后,会发现没有效果. 这里我们需要改回一般的函数.

数组的新方法

下边我们看一下es6下数组的一些新方法

Array.from

Array.from() ` 方法从一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例 比如以下

// 将类数组转成数组
function test(a, b, c) {
    console.log(Array.from(arguments))
}
// 后边还可以接一个回调函数
Array.from([1,2,3], (it) => it * 2) // [2, 4, 6]
// 结合Set做数组去重
let arr = [1, 2, 2, 3, 4, 5, 6, 5]
console.log(Array.from(new Set(arr))) // [1, 2, 3, 4, 5, 6]

Array.of

Array.of()  方法创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型

Array.of(1) // [1]
Array.of(1, 2, 3) // [1, 2, 3]

其实这个方法和我们之前的这种形式转成数组 Array.prototype.slice.call(1, 2, 3) 是一样的.

find

find() 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined

const array1 = [5, 12, 8, 130, 44];
const found = array1.find(element => element > 10);
console.log(found) // 12
找到了匹配的,则不会再往下找了.

fill

fill() 方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引。

const arr = [1,2,3,4,5]
arr.fill(0, 1, 5) // [1, 0, 0, 0, 0]
  • 第一个参数: 填充的值
  • 第二个参数: 需要填充的起点位置
  • 第三个参数: 需要填充的终点位置

map

map() 方法创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。

map这个方法,我在项目里边用到最多的地方就是下拉数据转换. 有时候后台给的数据有我们需要的数据的值,但是可能我们已经封装了一个组件了,它虽然给了我们需要的值,但是并不是我们需要的字段名. 所有我们需要做一层转换.

[{id: 1, name: '一'}, {id: 2, name: '二'}].map((o) => {
    return {
        text: o.name,
        value: o.id
    }
})
// [{text: "一", value: 1},{text: "二", value: 2}]

reduce

reduce是汇总的意思, 我们可以用它去汇总一个数组的值的总和

/**
 * @param { init } 上一次回调的累计的值
 * @param { curr } 数组中正在处理的元素
 * @param { index } 当前的索引
 * @param { arr } 源数组
 **/
[1, 2, 3].reduce((init, curr, index, arr) => {
    return init + curr
}, 0) // 6

reduce中第二个参数是传递的初始值,也就是我们这个例子里边的0, 它会在第一次遍历的时候,赋值给init, 然后return出来的值就会赋值给下一次遍历的init,curr就是每次遍历的值. 让我们分析一下他是怎么遍历的.

  • 0 + 1
  • 1 + 2
  • 3 + 3 这种方式之所以可以遍历出来综合,就是因为从第一次遍历开始,数据就在累加. 每次累加的值都会保存在init里边,最后遍历玩了.return出来的值自然就是所有数据累计的值. 下面我们去实现一个reduce大家就知道它大概是怎么一个运行的方式了.
Object.prototype.reduceCopy = function (callback, initVal) {
    let val = initVal
    for (let i = 0; i < this.length; i++) {
        val = callback(val, this[i], i, this)
    }
    return val
}
[1, 2, 3].reduceCopy((init, curr, index, arr) => {
    return init + curr
}, 0) // 6

filter

filter 顾名思义就是根据给的条件,过滤出来需要的数据.

// 例如过滤出来偶数
[1, 2, 3, 4, 5, 6].filter((o) => {
    return o % 2 == 0
}) // [2, 4, 6]

forEach

forEach其实算是es6给数组提供的一种声明式的用法, 它可以直接对数组进行遍历. 比for (let i = 0; i < arr.lengt; i++) 这种形式要来的更方便一点.

[1,2,3].forEach((o, index) => { console.log(o, index) })
// 1 0
// 2 1
// 3 2

但是有一点不太相同. 我们用for (let i = 0; i < arr.lengt; i++)这种循环的时候,如果不满足某些条件的话,我们可以用break去跳出循环,但是forEach循环不能break,必须全部走完一遍.