对象
<!--
* @Author: jingliang.duan@hand-china.com
* @Date: 2021-04-26 15:45:03
* @LastEditTime: 2021-04-26 15:45:04
* @LastEditors: jingliang.duan@hand-china.com
* @Description:
-->
函数
函数的默认参数
在ES5中,我们给函数传参数,然后在函数体内设置默认值,如下面这种方式。
function a(num, callback) {
num = num || 6
callback = callback || function (data) {console.log('ES5: ', data)}
callback(num * num)
}
a() //ES5: 36,不传参输出默认值
//你还可以这样使用callback
a(10, function(data) {
console.log(data * 10) // 1000, 传参输出新数值
})
而在ES6中,我们使用新的默认值写法。
function a(num = 6, callback = function (data) {console.log('ES6: ', data)}) {
callback(num * num)
}
a() //ES6: 36, 不传参输出默认值
a(10, function(data) {
console.log(data * 10) // 1000,传参输出新数值
})
使用ES6的默认值写法可以让函数体内部的代码更加简洁优雅
默认值对arguments对象的影响
我们先要了解arguments对象是什么?准确一点来说它是一个类数组对象,它存在函数内部,它将当前函数的所有参数组成了一个类数组对象。
function a(num, b){
console.log(arguments) // {"0": 6, "1": 10}
console.log(arguments.length) // 2
}
a(6, 10)
上面的输出结果看起来很正常,那么,如果我们加上参数默认值会怎样呢?
function a(num = 1, b = 1){
console.log(arguments)
}
a() // {} 默认值不能被arguments识别。
a(6, 10) // {"0":6,"1":10}
下面我们看一下修改参数默认值对arguments的影响。
1、在ES5的非严格模式下,一开始输入的参数是1,那么可以获取到arguments[0](表示第一个参数)全等于num,修改num = 2之后,arguments[0]也能更新到2。
function a(num){
console.log(num === arguments[0]) //true
num = 2 //修改参数默认值
console.log(num === arguments[0]) //true
}
a(1)
2、在ES5的严格模式下,arguments就不能在函数内修改默认值后跟随着跟新了。
"use strict"; //严格模式
function a(num) {
console.log(num === arguments[0]); // true
num = 2;
console.log(num === arguments[0]); // false
}
a(1);
在ES6环境下,默认值对arguments的影响和ES5严格模式是同样的标准。
默认参数表达式
参数不仅可以设置默认值为字符串,数字,数组或者对象,还可以是一个函数。
function add() {
return 10
}
function a(num = add()){
console.log(num)
}
a() // 10
默认参数的临时死区
第一章我们提到了let和const什么变量的临时死区(TDZ),默认参数既然是参数,那么也同样有临时死区,函数的作用域是独立的,a函数不能共享b函数的作用域参数。
//这是个默认参数临时死区的例子,当初始化a时,b还没有声明,所以第一个参数对b来说就是临时死区。
function add(a = b, b){
console.log(a + b)
}
add(undefined, 2) // b is not define
无命名参数
上面说的参数都是命名参数,而无命名参数也是函数传参时经常用到的。当传入的参数是一个对象,不是一个具体的参数名,则是无命名参数。
function add(object){ console.log(object.a + object.b) } let obj = { a: 1, b: 2 } add(obj) // 3
**不定参数的使用:**使用…(展开运算符)的参数就是不定参数,它表示一个数组。
function add(...arr){ console.log(a + b) } let a = 1,b = 2 add(a, b) // 3
**不定参数的使用限制:**必须放在所有参数的末尾,不能用于对象字面量setter中。
//错误的写法1 function add(...arr, c){ console.log(a + b) } let a = 1,b = 2,c = 3 add(a, b, c) //错误的写法2 let obj = { set add(...arr) { } }
ES6中的构造函数Function新增了支持默认参数和不定参数。
块级函数
**严格模式下:**在ES6中,你可以在块级作用域内声明函数,该函数的作用域只限于当前块,不能在块的外部访问。
"use strict"; if(true) { const a = function(){ } }
**非严格模式:**即使在ES6中,非严格模式下的块级函数,他的作用域也会被提升到父级函数的顶部。所以大家写代码尽量使用严格模式,避免这些奇葩情况。
箭头函数(=>)
如果看到你这里,你发现你还没有在项目中使用过箭头函数,没关系,你并不low,而是学习不够努力。
const arr = [5, 10] const s = arr.reduce((sum, item) => sum + item) console.log(s) // 15
箭头函数和普通函数的区别是:
1、箭头函数没有this,函数内部的this来自于父级最近的非箭头函数,并且不能改变this的指向。
2、箭头函数没有super
3、箭头函数没有arguments
4、箭头函数没有new.target绑定。
5、不能使用new
6、没有原型
7、不支持重复的命名参数。
箭头函数的简单理解
1、箭头函数的左边表示输入的参数,右边表示输出的结果。
const s = a => a console.log(s(2)) // 2
2、箭头函数中,最重要的this报错将不再成为你每天都担心的bug。
3、箭头函数还可以输出对象,在react的action中就推荐这种写法。
const action = (type, a) => ({ type: "TYPE", a })
4、支持立即执行函数表达式写法
const test = ((id) => { return { getId() { console.log(id) } } })(18) test.getId() // 18
5、箭头函数给数组排序
const arr = [10, 50, 30, 40, 20] const s = arr.sort((a, b) => a - b) console.log(s) // [10,20,30,40,50]
尾调用优化
尾调用是什么鬼?
尾调用是指在函数return的时候调用一个新的函数,由于尾调用的实现需要存储到内存中,在一个循环体中,如果存在函数的尾调用,你的内存可能爆满或溢出。
ES6中,引擎会帮你做好尾调用的优化工作,你不需要自己优化,但需要满足下面3个要求:
1、函数不是闭包
2、尾调用是函数最后一条语句
3、尾调用结果作为函数返回
一个满足以上要求的函数如下所示:
"use strict"; function a() { return b(); }
下面的都是不满足的写法:
//没有return不优化 "use strict"; function a() { b(); } //不是直接返回函数不优化 "use strict"; function a() { return 1 + b(); } //尾调用是函数不是最后一条语句不优化 "use strict"; function a() { const s = b(); return s } //闭包不优化 "use strict"; function a() { const num = 1 function b() { return num } return b }
尾调用实际用途——递归函数优化
在ES5时代,我们不推荐使用递归,因为递归会影响性能。
但是有了尾调用优化之后,递归函数的性能有了提升。
//新型尾优化写法 "use strict"; function a(n, p = 1) { if(n <= 1) { return 1 * p } let s = n * p return a(n - 1, s) } //求 1 x 2 x 3的阶乘 let sum = a(3) console.log(sum) // 6
总结
函数这一章涉及到的知识点比较多,默认参数,命名参数,不定参数,展开运算符,箭头函数,尾调用优化。
第一次学习这些知识的人可以关注箭头函数和展开运算符的使用,这是最重要也最常用的知识,如果你已经在项目中使用过这些知识,那么作为巩固也是有帮助的,俗话说温故知新。
数组
ES5提供的数组已经很强大,但是ES6中继续改进了一些,主要是增加了新的数组方法:
创建数组
ES5中创建数组的方式:数组字面量、new一个数组。
const arr1 = [] //数组字面量
const arr2 = new Array() //new构建
ES6创建数组:Array.of()、Array.from()
Array.of()
ES5中new一个人数组的时候,会存在一个令人困惑的情况。当new一个数字的时候,生成的是一个长度为该数字的数组,当new一个字符串的时候,生成的是该字符串为元素的数组。
const a = new Array(2)
const b = new Array("2")
console.log(a, b) //[undefined, undefined] ["2"]
这样一来,导致new Array的行为是不可预测的,Array.of()出现为的就是解决这个情况。
const c = Array.of(2)
const d = Array.of("2")
console.log(c, d) // [2] ["2"]
使用Array.of()创建的数组传入的参数都是作为数组的元素,而不在是数组长度,这样就避免了使用上的歧义。
Array.from()
如果说Array.of()是创建一个新数组,而Array.from()是将类数组转换成数组。
下面的例子讲的是将arguments转换成数组。arguments是类数组对象,他表示的是当前函数的所有参数,如果函数没有参数,那么arguments就为空。
function test(a, b) {
let arr = Array.from(arguments)
console.log(arr)
}
test(1, 2) //[1, 2]
**映射转换:**Array.from(arg1, arg2),我们可以给该方法提供2个参数,第二个参数作为第一个参数的转换。看个简单例子你就懂了。
function test(a, b) {
let arr = Array.from(arguments, value => value + 2)
console.log(arr)
}
test(1, 2) //[3, 4]
Array.from还可以设置第三个参数,指定this。
**Array.from()转换可迭代对象:**这个用法只需要一个例子,数组去重。
function test() {
return Array.from(new Set(...arguments))
}
const s = test([1, "2", 3, 3, "2"])
console.log(s) // [1,"2",3]
给数组添加新方法
ES6给数组添加了几个新方法:find()、findIndex()、fill()、copyWithin()。
1、find():传入一个回调函数,找到数组中符合当前搜索规则的第一个元素,返回它,并且终止搜索。
const arr = [1, "2", 3, 3, "2"]
console.log(arr.find(n => typeof n === "number")) // 1
2、findIndex():传入一个回调函数,找到数组中符合当前搜索规则的第一个元素,返回它的下标,终止搜索。
const arr = [1, "2", 3, 3, "2"]
console.log(arr.findIndex(n => typeof n === "number")) // 0
3、fill():用新元素替换掉数组内的元素,可以指定替换下标范围。
arr.fill(value, start, end)
测试一下
const arr = [1, 2, 3] console.log(arr.fill(4)) // [4, 4, 4] 不指定开始和结束,全部替换 const arr1 = [1, 2, 3] console.log(arr1.fill(4, 1)) // [1, 4, 4] 指定开始位置,从开始位置全部替换 const arr2 = [1, 2, 3] console.log(arr2.fill(4, 0, 2)) // [4, 4, 3] 指定开始和结束位置,替换当前范围的元素
4、copyWithin():选择数组的某个下标,从该位置开始复制数组元素,默认从0开始复制。也可以指定要复制的元素范围。
arr.copyWithin(target, start, end)
测试一下
const arr = [1, 2, 3, 4, 5] console.log(arr.copyWithin(3)) // [1,2,3,1,2] 从下标为3的元素开始,复制数组,所以4, 5被替换成1, 2 const arr1 = [1, 2, 3, 4, 5] console.log(arr1.copyWithin(3, 1)) // [1,2,3,2,3] 从下标为3的元素开始,复制数组,指定复制的第一个元素下标为1,所以4, 5被替换成2, 3 const arr2 = [1, 2, 3, 4, 5] console.log(arr2.copyWithin(3, 1, 2)) // [1,2,3,2,5] 从下标为3的元素开始,复制数组,指定复制的第一个元素下标为1,结束位置为2,所以4被替换成2
其他新增方法
其他还有定型数组、数组缓冲区的概念,你可以详细查看书上教程。
总结
掌握新的创建数组的方式,以及数组新增的几个方法,就够你使用了。定型数组和数组缓冲区一般人可以不用了解的太详细。
字符串
字符串
字符串(String)是JavaScript6大原始数据类型。其他几个分别是Boolean、Null、Undefined、Number、Symbol(es6新增)。
字符串类型在前端开发者,是使用最频繁的类型之一,网站上可见的各种文案,几乎都是字符串类型的数据。我们经常需要使用的操作无非是这么几点:读取字符串、转换字符串、清空字符串、拼接字符串、截取字符串。
在ES5中,字符串类型已经有了非常丰富的应用能力,那么,在ES6中,ECMA的专家们对字符串做了什么更新呢?
当Unicode引入扩展字符集之后,16位的字符已经不足以满足字符串的发展,所以才在ES6中更新了Unicode的支持。
我们看看ES6字符串新增的方法
**UTF-16码位:**ES6强制使用UTF-16字符串编码。关于UTF-16的解释请自行百度了解。
codePointAt():
该方法支持UTF-16,接受编码单元的位置而非字符串位置作为参数,返回与字符串中给定位置对应的码位,即一个整数值。
**String.fromCodePoiont():**作用与codePointAt相反,检索字符串中某个字符的码位,也可以根据指定的码位生成一个字符。
normalize():提供Unicode的标准形式,接受一个可选的字符串参数,指明应用某种Unicode标准形式。
正则表达式
正则表达式u修饰符:
当给正则表达式添加u字符时,它就从编码单元操作模式切换为字符模式。
其他新增的方法
上面提到的字符串和正则的新增方法只有在国际化的时候才用的到,我想,国内的很多网站还是不需要考虑国际化的问题,看不懂就先丢掉。下面讲到的新增的方法是实际开发中需求比较频繁的方法。
字符串中的子串识别:
以前我们经常使用indexOf()来检测字符串中是否包含另外一段字符串。
let t = 'abcdefg'
if(t.indexOf('cde') > -1) {
console.log(2)
}
//输出2,因为t字符串中包含cde字符串。
在ES6中,新增了3个新方法。每个方法都接收2个参数,需要检测的子字符串,以及开始匹配的索引位置。
**includes(str, index):**如果在字符串中检测到指定文本,返回true,否则false。
let t = 'abcdefg'
if(t.includes('cde')) {
console.log(2)
}
//true
startsWith(str, index):如果在字符串起始部分检测到指定文本,返回true,否则返回false。
let t = 'abcdefg'
if(t.startsWith('ab')) {
console.log(2)
}
//true
endsWith(str, index):如果在字符串的结束部分检测到指定文本,返回true,否则返回false。
let t = 'abcdefg'
if(t.endsWith('fg')) {
console.log(2)
}
//true
如果你只是需要匹配字符串中是否包含某子字符串,那么推荐使用新增的方法,如果需要找到匹配字符串的位置,使用indexOf()。
repeat(number)
这个方法挺有意思的,接收一个Number类型的数据,返回一个重复N次的新字符串。即使这个字符串是空字符,也你能返回N个空字符的新字符串。
console.log('ba'.repeat(3)) //bababa
正则表达式的其他更新
正则表达式y修饰符、正则表达式的复制、flags属性…
由于这一块知识没用过,就不打算去研究实际用途。
模板字面量
以前,我们用单引号或双引号表示字符串。
let a = '123' //单引号
let b = "123" //双引号
现在,使用模板字面量反撇号``。在实际开发中,这是经常都要用到的方法。
let c = `123` //反撇号
在字符串中使用反撇号,只需要加上转义符。
let d = `12\`3` //字符串内插入反撇号的方式。
在多行字符串的使用价值:
模板字面量为解决多行字符串的一系列问题提供了一个非常好的机制。
如果不使用模板字面量,实现多行字符串,你可能会使用换行符。
let a = '123\n456' console.log(a) // 123 // 456
使用模板字面量,就可以非常简单的实现需求。
let a = `123 456 ` console.log(a) // 123 // 456
在模板字面量插入变量的方法。
我们不再需要使用 +(加号)来向字符串插入变量,而是使用${params}直接插入你需要添加到字符串的位置。
let t = 'haha' let a = `123${t}456` console.log(a) //123haha456
这种方式也叫作字符串占位符。占位符支持互相嵌套模板字面量,强大吧。有了它,我们终于可以抛弃 + 拼接字符串的恶心做法了。
模板字面量的终极用法
tag是一个方法,方法名你可以任意命名,这种写法被称作标签模板。
function tag(literals, ...substitutions) { //literals是数组,第一个位置是"",第二个位置是占位符之间的字符串,在本例中是haha //substitutions是字符串中的模板字面量,可能多个 //函数最终返回字符串 } let a = 4 let t = tag`${a} haha` console.log(t) //4 haha
总结
本章讲到了ES6中新增的Unicode方法,但是对于不需要做国际化处理的同学,这个功能就不会用到,需要**关注的新增的字符串匹配的3个方法,以及模板字面量的使用。**这2个知识点非常非常常用!
链接