1 课程介绍
1.1简介
ES6 既是一个历史名词(特指2015年发布的标准ES2015),也是一个泛指,含义是 5.1 版以后的 JavaScript 的下一代标准,涵盖了ES2015、ES2016、ES2017等。
1.2环境安装
Node.js是JavaScript语言的服务器运行环境,对ES6的支持度比浏览器更高。
默认安装
1.2.1NPM使用
本地安装
npm install 使用:require('模块名')
全局安装
安装模块 npm install <模块名> -g 卸载模块 npm uninstall <模块名> 更新模块 npm update <模块名> 搜索模块 npm search <模块名>
2 声明和表达式
2.1解构赋值
ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构赋值。
如果解构不成功,变量的值就等于undefined。
如果等号的右边不是数组(不是可遍历的结构),将会报错。
解构赋值不仅适用于var命令,也适用于let和const命令。
(1)设置默认值
解构赋值允许指定默认值。当等式右边没值或是undefined时,使用默认值。
语法 let [变量=默认值]=[赋值]
(2)对象的解构赋值
解构不仅可以用于数组,还可以用于对象。
var { name, age } = { name: "sss", bar: "12" }; // name:sss ,age:12
注意变量必须与属性同名,才能取到正确的值,位置顺序没关系。
如果变量名与属性名不一致,必须写成下面这样:
var { foo: baz } = { foo: 'aaa', bar: 'bbb' }; // baz :"aaa"
对象的解构赋值的内部机制是:先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。
(3)字符串的解构赋值
此时的字符串被转换成了一个类似数组的对象。
let [a, b, c, d] = 'nice'; // a为n,b为i,c为c,d为e
(4)函数参数的解构赋值
function add([x, y]){ return x + y; } add([1, 2]); // 3 //设置默认值 function move({x = 1, y = 2} = {}) { // 默认参数 x=1,y=2 return [x, y]; } move({x: 3, y: 8}); // [3, 8] move({x: 3}); // [3, 2] move({}); // [1, 2] move(); // [1, 2]
(5)不能使用圆括号的情况
建议只要有可能,就不要在解构中放置圆括号。
<1>变量声明语句中,不能带有圆括号。
<2>函数参数中,模式不能带有圆括号。
<3>赋值语句中,不能将整个模式或嵌套模式中的一层,放在圆括号之中。
总结:解构赋值的使用场景
交换变量的值
[x, y] = [y, x];
方便从函数取值
function func() { return [1, 2, 3]; } var [a, b, c] = func();
函数参数的定义
// 参数是一组无次序的值 function f({x, y, z}) { ... } f({z: 1, y: 2, x: 3});
提取JSON数据
var jsonData = { id: 12, name: "sss", data: [867, 5309] }; let { id, name, data: number } = jsonData;
- 函数默认参数值
function move({x = 1, y = 2} = {}) { // 默认值 return [x, y]; } move({x: 3}); // y使用默认值,x:3, y:2
2.2 let和const
2.2.1 let命令
只在let命令所在的代码块有效
let不存在变量提升
变量的提升:javascript并不是严格的自上而下执行的语言,它会将当前作用域的所有变量的声明提升到程序的顶部。
console.log(aa) // 输出undefined var aa=2222; // 实际上是分两步执行 var aa;aa=2222;
let暂时性死区
ES6明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量就会报错。
总之,在代码块内使用let命令声明变量之前,该变量都是不可用的。
let不能重复声明同个变量
let不允许在相同作用域内,重复声明同一个变量。
let num=111; let num=222; // 报错内容:Identifier 'num' has already been declared
2.2.2const命令
const 声明一个只读的常量。一旦声明,常量的值就不能改变。
const一旦声明变量就必须立即初始化,不能留到以后赋值。
只声明不赋值同样会报错。
const声明的常量不能重新赋值。
const aa= []; aa.push('firstNumber'); // 可执行 aa.length = 0; // 可执行 aa= ['secondNumber']; // 报错
2.3 Symbol数据类型
2.3.0概述
JavaScript的7中数据类型:Undefined、Null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)、Symbol
Symbol
是一种基础数据类型功能类似于一种标识唯一性的ID
对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的Symbol类型。
通常情况下,我们可以通过调用Symbol()
函数来创建一个Symbol实例
let s1 = Symbol()
- Symbol函数可以接受一个字符串作为参数,表示对Symbol实例的描述。这主要是为了在控制台显示或者转为字符串时,比较容易区分。
传入一个可选的字符串参数,相当于给你创建的Symbol实例一个描述信息:
let s2 = Symbol('another symbol')
2.3.1作为属性名的Symbol
由于每一个Symbol值都是不相等的,这意味着Symbol值可以作为标识符用于对象的属性名,这就能保证不会出现同名的属性。
var name= Symbol(); // 第一种写法 var student= {}; student[name] = 'jack!'; // Symbol值name作为student的属性 // 第二种写法 var student= { [name]: 'jack!' //注意要使用方括号包裹,不放在方括号中,该属性的键名就是字符串 };
注意,Symbol值作为对象属性名时,不能用点运算符,要使用方括号。
取值对象的Symbol类型的属性也要用方括号。普通属性是直接用点运算符。
因为点运算符后面总是字符串
2.3.2 Symbol类型定义常量
- 用于定义一组常量,保证这组常量的值都是不相等的。 - 常量使用Symbol值最大的好处,就是其他任何值**都不可能有相同的值**了。
levels = { DEBUG: Symbol('debug'), INFO: Symbol('info'), WARN: Symbol('warn') }; console.log(levels.DEBUG) // Symbol(debug) console.log(levels.INFO) // Symbol(info) console.log(levels.WARN) // Symbol(warn)
2.3.3 属性名的遍历
Symbol 作为属性名,该属性不会出现在for...in、for...of循环中,也不会被Object.keys()、JSON.stringify()等返回。
- ES6中有一个Object.getOwnPropertySymbols方法,可以获取指定对象的所有 Symbol 属性名,返回的是一个数组。
var obj = {}; var name = Symbol('name'); var age = Symbol('age'); obj[name] = 'huang'; obj[age] = '23'; var objectSymbols = Object.getOwnPropertySymbols(obj); // 是个数组,可以用for循环遍历 console.log(objectSymbols) // [ Symbol(name), Symbol(age) ] console.log(obj[name]) // huang 取出Symbol类型的属性要用方括号
- Reflect.ownKeys方法可以返回对象的属性所有类型的键名,包括常规键名和 Symbol 键名。
var name = Symbol('name'); var age = Symbol('age'); var obj = { [name] : 'huang', // Symbol类型属性 [age] : '23', // Symbol类型属性 num : 1 // 普通属性 }; console.log( Reflect.ownKeys(obj)) // [ 'num', Symbol(name), Symbol(age) ]返回值是数组
2.3.4Symbol.for(),Symbol.keyFor()
Symbol.for方法可以重新使用同一个Symbol值。
- 它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的Symbol值。如果有,就返回这个Symbol值,否则就新建并返回一个以该字符串为名称的Symbol值。
- Symbol.for方法和Symbol是不一样的,Symbol每次都是创建一个新的,Symbol.for只有不存在时才创建新的。
- Symbol.for为Symbol值登记的名字,是全局环境的。
Symbol.for("foo") === Symbol.for("foo") // true Symbol("foo") === Symbol("foo") // false
Symbol.keyFor方法返回一个已登记的 Symbol 类型值的key
var s1 = Symbol.for("foo"); Symbol.keyFor(s1) // "foo" 使用Symbol.for有登记名字 var s2 = Symbol("foo"); Symbol.keyFor(s2) // undefined 使用Symbol未登记名字
2.3.5 单例中使用Symbol
- 单例(Singleton)指的是调用一个类,任何时候返回的都是同一个实例。
const FOO_KEY = Symbol.for('foo'); // 使FOO_KEY变成Symbol类型 function A() { this.foo = 'hello'; } if (!global[FOO_KEY]) { // 如果不存这个实例创建新的,存在就跳过 global[FOO_KEY] = new A(); // 创建一个新的实例,把实例放到顶层对象global } module.exports = global[FOO_KEY]; // 暴露这个实例,可以被其他文件引用
3 内置对象及扩展
3.1字符串的扩展
3.1.1字符的Unicode表示法
- 允许采用 \uxxxx 形式表示一个字符,其中“xxxx”表示字符的码点
- 这种表示法只限于\u0000——\uFFFF之间的字符。
- 超出这个范围的字符,必须用两个双字节的形式表达
- ES6改进:只要将码点放入大括号就能正确解读该字符
console.log("\u20BB7"); // JavaScript会理解成 \u20BB+7 ,所以只会显示一个空格,后面跟着一个7 console.log("\u{20BB7}"); //输出:告
3.1.2遍历字符串
for of与for in
for in遍历数组的毛病
1.index索引为字符串型数字,不能直接进行几何运算
2.遍历顺序有可能不是按照实际数组的内部顺序
3.使用for in会遍历数组所有的可枚举属性,包括原型。结论:for in更适合遍历对象,不要使用for in遍历数组
for of
for of遍历的只是数组内的元素,而不包括数组的原型属性method和索引name
结论:for in遍历的是数组的索引(即键名),而for of遍历的是数组元素值。
ES6为字符串添加了遍历器接口,使得字符串可以被for...of循环遍历
- 这个遍历器最大的优点是可以识别大于0xFFFF的码点,传统的for循环无法识别这样的码点。
for (let i of 'abcd') { console.log(i) } //a //b //c //d
3.1.3其它方法
- 用来确定一个字符串是否包含在另一个字符串中
- indexOf 返回参数字符串的位置,如果找到返回位置(第一个字符的位置);未找到,返回-1
- includes() 返回布尔值,表示是否找到了参数字符串。
- startsWith() 返回布尔值,表示参数字符串是否在源字符串的头部。
- endWith() 返回布尔值,表示参数字符串是否在源字符串的尾部。
- repeat()方法
- 返回一个新字符串,表示将原字符串重复n次。
- 语法 字符串.repeat(重复次数的参数)
'a'.repeat(3) // "aaa" 'ok'.repeat(2) // "okok" 'ok'.repeat(0) // "" 'ok'.repeat(3.9) // "okokok" 参数为小数只取整数部分,参数为负数会报错,参数为NaN就是0
- codePointAt()方法
- 正确处理4个字节储存的字符,返回一个字符的码点。
3.1.4模板字符串
传统方式:使用加号拼接字符串和变量
$('.op').append( "I am '+info.age+'years old" )
ES6使用:模板字符串(template string)是增强版的字符串,用反引号(`)标识
变量用 ${变量} 来表示。大括号内部可以放入任意的JavaScript表达式,可以进行运算,引用对象属性,调用函数,甚至还能嵌套。
如果在模板字符串中需要使用反引号,则前面要用反斜杠转义。
模板字符串中所有的空格和缩进都会被保留在输出之中,可以用来定义多行字符串。
$('.op').append(` I am ${info.age} years old `)
3.15标签模板
- 模板字符串可以紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串。这被称为“标签模板”功能(tagged template)
- 标签模板其实不是模板,而是函数调用的一种特殊形式。
- “标签”指的就是函数,紧跟在后面的模板字符串就是它的参数。
- 如果模板字符里面有变量,就不是简单的调用了,而是会将模板字符串先处理成多个参数,再调用函数。
alert`123` // 字符串123跟在 alert函数后面 // 等同于 alert(123) var a = 5; var b = 10; tag`Hello ${ a + b } world ${ a * b }`; // tag表示一个函数 // 等同于 tag(['Hello ', ' world ', ''], 15, 50);
- 应用场景:
- 过滤 HTML 字符串,防止用户输入恶意内容
- 多语言转换(国际化处理)
3.2正则的扩展
3.2.1正则(RegExp)构造函数
ES5中,RegExp构造函数参数
- 1.第一个参数是字符串,第二个参数表示正则表达式的修饰符(flag)
var regex = new RegExp('xyz', 'i'); // 等价于 var regex = /xyz/i;
- 参数是一个正则表示式
var regex = new RegExp(/xyz/i); // 等价于 var regex = /xyz/i; //★ES5不允许此时使用第二个参数,添加修饰符,否则会报错。 var regex = new RegExp(/xyz/, 'i'); // 报错 Uncaught TypeError: Cannot supply flags when constructing one RegExp from another
ES6
如果RegExp构造函数第一个参数是一个正则对象,那么可以使用第二个参数指定修饰符。
而且,返回的正则表达式会忽略原有的正则表达式的修饰符,只使用新指定的修饰符。
new RegExp(/xyz/ig, 'i').flags // 返回值 i flags属性,会返回正则表达式的修饰符。
原有正则对象的修饰符是ig,它会被第二个参数 i 覆盖
3.2.2 u修饰符
ES6对正则表达式添加了u修饰符,含义为“Unicode模式”,用来正确处理大于\uFFFF的Unicode字符。也就是说,会正确处理四个字节的UTF-16编码。
/^\uD83D/u.test('\uD83D\uDC2A') // 打印值为false //★结果说明:加了u修饰符以后,ES6就会识别其为一个字符,所以第一行代码结果为false。
Unicode字符表示法
S6新增了使用大括号表示Unicode字符,这种表示法在正则表达式中必须加上u修饰符,才能识别。
/\u{61}/.test('a') // 打印结果 false /\u{61}/u.test('a') // 打印结果true //★如果不加u修饰符,正则表达式无法识别\u{61}这种表示法,只会认为这匹配61个连续的u。
3.2.3y 修饰符
y修饰符,叫做“粘连”(sticky)修饰符
y修饰符与g修饰符的区别
- y修饰符 全局匹配,后一次匹配从剩余字符的头部开始匹配
- g修饰符 全局匹配,后一次匹配 只要剩余位置中存在匹配即可
var s = 'aaa_aa_a'; // 字符串 // 正则声明,此处是字面量方式,还有一种是构造函数方式 var r1 = /a+/g; // 带g修饰符 var r2 = /a+/y; // 带y修饰符 //第一次匹配 r1.exec(s) // 匹配结果 ["aaa"] , 剩余字符串_aa_a r2.exec(s) // ["aaa"] 剩余字符串_aa_a //第二次匹配 r1.exec(s) // ["aa"] r2.exec(s) // null 剩余字符串_aa_a头部 是 _ 不符合,所以返回null
sticky属性
var r = /abc\d/y; r.sticky // true
flags属性
ES6为正则表达式新增了flags属性,会返回正则表达式的修饰符。
// ES5的source属性,返回正则表达式的正文 /abc/ig.source // "abc" // ES6的flags属性,返回正则表达式的修饰符 /abc/ig.flags // 'gi'
3.3数值的扩展
3.3.1二进制和八进制表示法
二进制和八进制数值的新的写法,分别用前缀0b(或0B)和0o(或0O)表示
// 十进制456转换为二进制111001000 ,二进制使用0b开头 0b111001000 === 456 // true // 十进制456转换为二进制710,八进制使用0o开头 0o710 === 456 // true
将0b和0o前缀的字符串数值转为十进制,要使用Number方法
Number('0b110') // 6 二进制 Number('0o11') // 9 八进制
3.3.2Number对象
ES6新提供了Number.isFinite()和Number.isNaN()两个方法,只对数值有效,非数值一律返回false。
Number.isFinite()
用来检查一个数值是否为有限的(finite)。有限返回值为true,不是有限的返回false。
Number.isNaN()
用来检查一个值是否为NaN。是NaN返回值为true,不是NaN返回值为false。
Number.isInteger()
用来判断一个值是否为整数。需要注意的是,在JavaScript内部,整数和浮点数是同样的储存方法,所以3和3.0被视为同一个值。
Number.isFinite(0.545877); // true Number.isFinite('aaaa'); // false 非数值返回false Number.isFinite(NaN); // false Number.isFinite(Infinity); // false Infinity 属性用于存放表示正无穷大的数值。 Number.isNaN(NaN) // true Number.isNaN(15) // false Number.isNaN('15') // false Number.isNaN(true) // false Number.isNaN(9/NaN) // true Number.isNaN('true'/0) // true Number.isNaN('true'/'true') // true Number.isInteger(10) // true Number.isInteger(10.0) // true Number.isInteger(10.1) // false Number.isInteger("10") // false Number.isInteger(true) // false
Number.parseInt(), Number.parseFloat()
- 将全局方法parseInt()和parseFloat(),移植到Number对象上面,行为完全保持不变。
- parseInt()函数可解析一个字符串,并返回一个整数。
- parseFloat() 函数可解析一个字符串,并返回一个浮点数。
Number.EPSILON 常量
浮点数计算是不精确的。
ES6在Number对象上面,作为一个误差范围。
当这个误差能够小于Number.EPSILON,我们就可以认为得到了正确结果。
Number.isSafeInteger() 安全整数
- JavaScript能够准确表示的整数范围在-2^53到2^53之间(不含两个端点),超过这个范围,无法精确表示这个值。
- 用来判断一个整数是否落在这个范围之内。
// ES5的写法 parseInt('12.34') // 12 parseFloat('12.34#') // 12.34 // ES6的写法 Number.parseInt('12.34') // 12 Number.parseFloat('12.34abcd这里是字符串') // 12.34 var num=0.1+0.2-0.3 // 5.551115123125783e-17 num< Number.EPSILON //true 表示小于误差范围 var num=Math.pow(2, 53) // 9007199254740992 var num2=num+1 // num2超过 -2^53到2^53这个范围,打印出来和num值相等,这是错误的 num==num2 // true ☆结果分析:超出2的53次方之后,一个数就不精确了。num和num2本来应该是不相等,但是返回值是相等。 Number.isSafeInteger(num2) // false
3.3.3Math对象的扩展
Math.trunc 用于去除一个数的小数部分,返回整数部分
Math.sign 判断一个数到底是正数、负数还是零
返回五种值:
- 参数为正数,返回+1;
- 参数为负数,返回-1;
- 参数为0,返回0;
- 参数为-0,返回-0;
- 其他值,返回NaN。
Math.cbrt 计算一个数的立方根
Math.trunc(4.1) // 4 Math.trunc('123.456') //123 对于非数值,Math.trunc内部使用Number方法将其先转为数值 Math.trunc(NaN); // NaN 对于空值和无法截取整数的值,返回NaN Math.cbrt('8') // 2 2的三次方为8
Math.expm1(x) 返回ex - 1,即Math.exp(x) - 1。
Math.log1p(x) 方法返回1 + x的自然对数,即Math.log(1 + x)。如果x小于-1,返回NaN。
Math.log10(x) 返回以10为底的x的对数。如果x小于0,则返回NaN。
Math.log2(x) 返回以2为底的x的对数。如果x小于0,则返回NaN。
- Math.sinh(x) 返回x的双曲正弦
- Math.cosh(x) 返回x的双曲余弦
- Math.tanh(x) 返回x的双曲正切
- Math.asinh(x) 返回x的反双曲正弦
- Math.acosh(x) 返回x的反双曲余弦
Math.atanh(x) 返回x的反双曲正切
3.4数组的扩展
3.4.1Array.from方法
用于将两类对象转为真正的数组:类似数组的对象和可遍历(iterable)的对象(包括ES6新增的数据结构Set和Map)。
- 只要是部署了Iterator接口的数据结构,Array.from都能将其转为数组,如字符串。
- 接受第二个参数,用来对每个元素进行处理,将处理后的值放入返回的数组。
// 类似数组的对象 var arr= { '0': 'a', '1': 'b', '2': 'c', length: 3 }; // ES6的写法 var arr2 = Array.from(arr); // ['a', 'b', 'c'] Array.from('hello') // ['h', 'e', 'l', 'l', 'o'] Array.from([1, 2, 3], (x) => x * x) // [1, 4, 9] =>是箭头函数
3.4.2Array.of方法
- Array.of方法用于将一组值,转换为数组。如果没有参数,就返回一个空数组。
- Array.of基本上可以用来替代Array()或new Array(),并且Array.of不会因为参数不同而导致重载。
- 如果使用Array()方法,当参数只有一个时,实际上是指定数组的长度
Array.of(7, 8, 9) // [7,8,9] Array.of(13) // [13] Array.of() // [] Array.of(13).length // 1 var num = Array(2); // 使用Array 方法 var num2 = Array.of(2); // 使用Array.of 方法 num.length // 2 num2.length // 1
3.4.3数组实例copyWithin()方法
在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组。
语法 Array.prototype.copyWithin(target, start = 0, end = this.length)
其中有三个参数(应该是数值类型,不是的话会自动转为数值):
- target(必须):从该位置开始替换数据。
- start(可选):从该位置开始读取数据,默认为0。如果为负值,表示倒数。
- end(可选): 到该位置前停止读取数据,没取到这个数,默认等于数组长度。如果为负值,表示倒着数。
//将从3号位直到数组结束的成员(4和5),复制到从0号位开始的位置,结果覆盖了原来的1和2。 [1, 2, 3, 4, 5].copyWithin(0, 3) // 替换后的结果 [4, 5, 3, 4, 5] //将下标为3号和4号位复制到下标为0号位和1号位 [1, 2, 3, 4, 5].copyWithin(0, 3, 5) // [4, 5, 3, 4, 5] // -2相当于3号位,-1相当于4号位 [1, 2, 3, 4, 5].copyWithin(0, -2, -1) // [4, 2, 3, 4, 5] /从下标为2开始替换(也就是数值9),后两个参数没写,替换的内容默认从下标为0开始(数值8,5,9) [8,5,9,4,3].copyWithin(2) // [ 8, 5, 8, 5, 9 ]
3.4.4数组实例的find()和findIndex()
数组实例的find方法,用于找出第一个符合条件的数组成员。
参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。
find方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组
数组实例的findIndex方法,返回第一个符合条件的数组成员的位置(下标),如果所有成员都不符合条件,则返回-1。
// 查找数组中小于0的数 [1, 2, -3, 4].find( (n) => n < 0 ) // -3 => 是箭头函数,等于 function(n) { return n<0; } [1, 2, 3, 4].find(function(value, index, arr) { return value > 2; }) // 返回值为3 [1, 2, 3, 4].findIndex(function(value, index, arr) { return value > 2; // 返回值为2 }
3.4.5其他数组实例的方法
fill
使用给定值,填充一个数组
还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置
['a', 'b', 'c'].fill(7, 1, 2) // ['a', 7, 'c'] //fill方法从1号位开始,向原数组填充7,到2号位之前结束。 ['a', 'b', 'c'].fill(7, 1, 3) // ['a', 7, 7]
entries(),keys()和values() 用于遍历数组
- keys()是对键名的遍历
- values()是对键值的遍历
- entries()是对键值对的遍历
console.log("----------keys方法---------") for (let index of ['hello', 'world'].keys()) { console.log("输出结果:", index); } console.log("----------values方法发---------") for (let elem of ['hello', 'world'].values()) { console.log("输出结果:",elem); } console.log("----------entries方法发---------") for (let [index, elem] of ['hello', 'world'].entries()) { console.log("输出结果:",index, elem); }
includes()
返回一个布尔值,表示某个数组是否包含给定的值
[1, 2, 3].includes(2); // true [1, 2, 3].includes(4); // false
3.5函数的扩展
3.5.1箭头函数
ES6允许使用“箭头”(=>
)定义函数。箭头函数使得表达更加简洁。
- 由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号。
var f = (参数)=>{ 代码块} var ff = (x,y)=>{ return x+y } // 相当于 var f = function(参数){ 代码块 } var ff = function(x,y){ return x+y } const full = ({ first, last }) => first + ' ' + last; // 等同于 var person={ first, last }; function full(person) { return person.first + ' ' + person.last; }
3.5.2箭头函数this对象指向
普通函数this 的指向
- js中的this是执行上下文,在普通函数中,this指向它的直接调用者;
- 在默认情况下(非严格模式),在全局环境下,js中的this指向window;
箭头函数的this指向
- 箭头函数体内的this对象就是定义时所在的对象,而不是使用时所在的对象。
- 简单而言,箭头函数使用时,不绑定this对象,箭头函数没有自己的this,它的this是继承而来的,默认指向在定义箭头函数时所处的对象。
function foo() { return () => { return () => { return () => { console.log('id:', this.id); }; }; }; } var f = foo.call({id: 1}); // 设置foo的id为1 var t1 = f.call({id: 2})()(); // id: 1 var t2 = f().call({id: 3})(); // id: 1 var t3 = f()().call({id: 4}); // id: 1 //★结果分析:因为所有的内层函数都是箭头函数,都没有自己的this,它们的this其实都是最外层foo函数的this。所以箭头函数的this指向是创建它所在的对象,不会改变。
箭头函数有几个使用注意点:
(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用Rest参数代替。
(4)不可以使用yield命令,因此箭头函数不能用作Generator函数。
3.5.3函数的默认值
- ES6之前,不能直接为函数的参数指定默认值,只能采用变通的方法
- ES6允许为函数的参数设置默认值,即直接写在参数定义的后面。
- 与解构赋值默认值结合使用
//ES6之前 function log(x, y) { y = y || 'aaa'; // 设置y的默认值为aaa console.log(x, y); } //ES6 function log(x, y = 'aaa') { console.log(x, y); } //对象的解构赋值 function foo({x, y = 5}) { console.log(x, y); } foo({}) // undefined, 5 foo({x: 1}) // 1, 5 foo({x: 1, y: 2}) // 1, 2 foo() // 报错内容:TypeError: Cannot read property 'x' of undefined
3.5.4函数的length属性
- 指定了默认值以后,函数的length属性,返回的是没有默认值的参数个数。
- 如果设置了默认值的参数不是尾参数(排在最右边),那么length属性也不再计入后面的参数了(就是说,以第一个默认参数位置开始计数,只计算第一个默认参数前边的个数,不计算后边的)
(function (a) {}).length // 1 (function (a = 5) {}).length // 0 (function (a, b, c = 5) {}).length // 2 (function (a = 0, b, c) { }).length // 0 (function (a, b = 1, c) { }).length // 1
3.5.5函数默认值的作用域
var x=1; // 全局变量x function f(x, y = x) { console.log(y); } f(5); //5 //★:结果说明:调用时,由于函数作用域内部的变量x已经生成,所以y等于参数x,而不是全局变量x。 let x = 1; // 全局变量 x function f(y = x) { let x = 2; // 局部变量 x console.log(y); } f() // 1 //★结果说明:函数调用时,y的默认值变量x尚未在函数内部生成,所以x指向全局变量。
3.5.6rest参数
- rest参数(形式为“...变量名”),用于获取函数的多余参数
- rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
- rest参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。
- 函数的length属性,不包括rest参数。
function add(...values) { let sum = 0; for (var num of values) { sum += num; } return sum; } add(4, 5, 6,7) // 22
3.5.7扩展运算符
- 扩展运算符(spread)是三个点(...)。
- 它好比rest参数的逆运算,将一个数组转为用逗号分隔的参数序列。
- 运算符主要用在函数调用
console.log( ...[1, 2, 3] ) // 1 2 3 console.log( ...[1, 2, ...[1,2,5],3] ) // 1 2 1 2 5 3 console.log( ...[1, 2, [1,2,5],3] ) // 1 2 [ 1, 2, 5 ] 3 function add(x, y) { return x + y; } var numbers = [4, 38]; add(...numbers) // 42
3.5.8尾调用和尾递归
- 尾调用(Tail Call)指某个函数的最后一步是调用另一个函数,它是函数式编程的一个重要概念。
- 函数调用自身,称为递归。
- 如果尾调用自身称为尾递归。
//**尾调用** function f(x){ return g(x); } //递归** function foo(n) { if (n === 1) return 1; return n * foo(n - 1); } foo(5) // 120
来源:https://www.cnblogs.com/xuzhengguo/p/12036959.html