变量的解构赋值
-
数组的解构赋值
- ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)
例如:
上面代码中,可以从数组中提取值,按照对应位置,对变量赋值。let[a,b,c] = [1,2,3];
- ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)
-
对象的解构赋值
- 对象的解构与数组的解构有一个重要的不同,数组的元素是按次序排列的,变量的值由它的位置决定,而对象的属性没有次序,变量必须与属性同名,才能取到正确的值
let { foo, bar } = { foo: "aaa", bar: "bbb" }; foo // "aaa" bar // "bbb"
- 对象的解构与数组的解构有一个重要的不同,数组的元素是按次序排列的,变量的值由它的位置决定,而对象的属性没有次序,变量必须与属性同名,才能取到正确的值
-
解构的多种用途
- 交换变量的值
let x = 1; let y = 2; [x, y] = [y, x];
- 从函数返回多个值
function example() { return [1,2,3]; } let [a,b,c] = example(); //返回一个对象 function exm() { return { foo:1, bar:2 } } let {a,b} = exm();
- 解构赋值可以方便的将一组参数和变量名对应起来
//参数是一组有次序的值 function fn([x, y, z]){ ··· }; f([1,2,3]); //参数是一组无次序的值 function fn([x, y, z]){ ··· }; fn([z:3, y:2, x:1]);
- 提取JOSN数据,结构赋值对提取JSON对象的数据,尤其有用
let jsonData = { id: 11, status: "OK", data: [123,123] } //data:number 相当于重新给data命名 let {id, status, data:number} = jsonData;
- 函数参数的默认值
jQuery.ajax = function (url, { async = true, beforeSend = function () {}, cache = true, complete = function () {}, crossDomain = false, global = true, // ... more config } = {}) { // ... do stuff //标记:这里并不是很理解 };
- 交换变量的值
字符串的扩展
- 字符串的遍历器接口
- ES6为字符串添加了遍历接口器,可以使字符串被 for...of 遍历
for(let codePoint of 'foo'){ console.log(codePoint); } // "f" // "o" // "o"
- at()
- ES5对字符串对象提供 charAt 方法,返回字符串给定位置的字符,该方法不能识别码点大于 0xFFFF的字符
- ES6提供 string.at() 方法
函数的拓展
- 函数的默认参数值
- ES6允许给函数设置默认参数值,直接写在参数后面
function log(x, y = "world") { console.log(x , y); }
- 除了简洁,ES6语法还有两个好处:首先,阅读代码的人可以立刻意识到哪些参数是可以省略的,不用查看函数体或文档;其次,有利于将来的代码优化,
尾调用优化
-
尾调用指某个函数的最后一步调用另一个函数
- 函数调用会在内存中形成一个‘调用记录’,又称‘调用帧’,保存调用位置和内部变量等信息。尾调用由于是函数的最后一步操作,不需要保留外层函数的调用帧,因为调用位置、内部变量等信息不会再用到了,只要直接使用内层函数逇调用帧,取代外层函数的调用帧就可以了。
-
尾递归
- 递归非常耗费内存,因为需要同时保存成千上百个调用帧,很容易发生‘栈溢出’错误。但对于尾递归来说,由于只存在一个调用帧,所以永远不会发生‘栈溢出’
- 尾递归 斐波那契数列
function Fib2(n, ac1 = 0, ac2 = 1) { if( n <= 1) return ac2; return Fib2(n-1, ac2, ac1 + ac2); //在返回值的时候已经算好了 此时是尾调用 }
- 递归非常耗费内存,因为需要同时保存成千上百个调用帧,很容易发生‘栈溢出’错误。但对于尾递归来说,由于只存在一个调用帧,所以永远不会发生‘栈溢出’
-
递归函数的改写
- 把所有用到的内部变量改写成函数的参数
- 柯里化(currying) 意思是将多参数的函数转换为单参数的形式
- 递归本质上是一种循环操作,一旦使用递归,最好使用尾递归
-
严格模式
- ES6的尾调用优化只在严格模式下开启,正常模式是无效的,因为正常模式下函数内部有两个变量,可以跟踪函数的调用栈
- - func.arguments: 返回调用时函数的参数
- - func.caller: 返回调用当前函数的那个函数
- 尾调用优化是函数的调用栈会改写,因此上面两个变量就会失真。严格模式禁用这两个变量,所以尾调用模式仅在严格模式下生效
- ES6的尾调用优化只在严格模式下开启,正常模式是无效的,因为正常模式下函数内部有两个变量,可以跟踪函数的调用栈
-
尾递归优化的实现
- 尾递归优化只在严格模式下生效,正常模式下可以采用'循环'换掉'递归'
- 蹦床函数(trampoline)可以将递归执行转为循环执行
function trampoline(f) { while(f && instanceof Function) { f = f(); } return f; }
数组的扩展
扩展运算符
- 扩展运算符(spread)是三个点(...),它好比rest参数的逆运算,将一个数组转换为逗号分隔的参数序列
- 复制数组
- 数组是复杂数据类型,直接复制只是复制了指向底层数据的指针,而不是克隆一个新的数组,扩展运算符提供了复制数组的简写
const a1 = [1,2]; const a2 = [...a1]; //写法1 const [...a2] = a1; //写法2
- 数组合并
const a1 = ['a', 'b']; const a2 = ['c']; const a3 = ['d', 'e']; //ES5的合并数组 a1.concat(a2, a3); //ES6的合并数组 console.log([...a1, ...a2, ...a3]);
- 这两种方法都是浅拷贝
- 字符串
- 扩展运算符可以将字符串转为真正的数组
[...'hello'] //['h','e','l','l','o'] //这种写法一个重要的好处是可以正确识别 Unicode字符
- Map 和 Set 结构,Generator 函数
- 扩展运算符内部调用的是数据结构Iterator接口,只要有Iterator接口的对象,都可以使用扩展运算符
- Array.from()
- Array.from 用于将两类对象转为真正的数组:类似数组的对象和可遍历的对象(包括ES6新增的数据结构 Set 和 Map)
let arrayLike = { '0': 'a', '1': 'b', '2': 'c', length: 3 }; //ES5的写法 var arr1 = [].slice.call(arrayLike); //ES6 let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
- 实际应用中,常见的类似数组的对象是DOM操作返回的NodeList集合,以及函数内部的 arguents 对象。 Array.from都可以将他们转换为真正的数组
- 复制数组
Promise
回调函数的问题
- 回调函数本身没有问题,他的问题出现在多个回调嵌套。假设执行完某个异步函数后在执行另一个函数,出现多次后会发生多重嵌套。这种情况称为'回调地狱'(callback hell)
- Promise就是为了解决这种问题而提出的。采用Promise连续读取多个文件
let readFile = require('fs-readfile-promise'); readFile(filea) .then(function(data){ console.log(data.toString()); }) .then(function() { return readFile(fileB); }) .then(function(data){ console.log(data.toString()); }) .catch(err => console.log(err))
Generator 函数的概念
- Generator 函数是协程在 ES6 的实现,最大的特点是可以交出函数的执行权(即暂停执行)
function* gen(x) { var y = yield x + 2; return y; } //它不同于不同函数,是可以暂停执行的,所以函数名之前要加星号,以示区别
- 整个 Generator 函数就是一个封装的异步任务,或者说异步任务的容器。一步操作需要暂停的地方,都用yield语句注明
Promise 对象
- Promise 的含义
- Promise 就是一个容器,存储着某个未来才会结束的事件(通常是一个异步操作)的结果。
-
Promise
对象有以下两个特点- (1) 对象的状态不受外界影响。
Promise
对象代表一个异步操作,有三种状态: pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果可以决定当前是哪一种状态,任何其他操作都无法改变这个窗台。 - (2) 一旦状态改变,就不会在变,任何时候都可以得到这个结果。
- (1) 对象的状态不受外界影响。
- 基本用法
- ES6规定,
Promise
对象是一个构造函数,用来生成Promise实例 -
Promise
构造函数接收一个函数为参数,该函数的两个参数分别是resolve
和reject
,reslove
函数在异步操作成功时调用,并将异步操作的结果作为参数传递出去;reject
函数的作用是在一部操作失败是调用,并将异步操作报出的错误作为参数传递出去 -
Promise
实例生成之后可以用then
方法分别指定resolved
状态和rejected
状态的回调函数
promise.then(function(value){ //success }, function(error) { //failure })
- ES6规定,
- Promise.prototype.then()
- Promise 实例具有
then
方法,then
方法是定义在原型Promise.prototype
上的。它的作用是作为 Promise 实例添加状态改变时的回调函数。then
方法的第一个参数是resolved
状态的回调函数,第二个参数(可选)是rejected
状态的回调函数 -
then
方法返回的是一个新的Promise
实例(此时返回的实例不是原来的Promise
实例),因此可以采用链式写法,即then
方法后面再调用另一个then
方法
getJSON('/post.json').then(function(json) { return json.post; }).then(function(post){ //... });
- 第一个
then
方法指定的回调函数,返回的是另一个Promise
对象。这时,第二个then
方法指定的回调函数就会等待这个新的Promise
对象状态发生变化。如果变为resolved
就执行下一个then
的第一个参数,箭头函数的写法是:
getJSON('post/.json').then( post => getJSON(params); ).then( //第一个参数是成功后的回调函数 comments => console.log("resolved:", comments), //第二个参数是失败调用的回调函数 err => console.log("rejected", err) //此时函数返回的是一个对象,还可以继续 then ).then( 成功调用的函数 => console.log(resolved), 失败调用的函数(可选) => console.log(rejected) );
- Promise 实例具有
网友评论