ES6中的新特性
本人最近学习es6一些方法,难免有些手痒,想着能不能将这些方法总结下,如下
1、数组的扩展
1)首先什么是伪数组
无法直接调用数组方法或期望length属性有什么特殊的行为,但仍可以对真正数组遍历方法来遍历它们,例如:函数的argument参数,调用getElementsByTagName,document.childNodes等等
2、函数扩展
document.querySelectorAll('元素') 相当于 document.getElementsByTagName('元素') 一样
合并数组
// ES5 [1, 2].concat(more) // ES6 [1, 2, ...more] var arr1 = ['a', 'b']; var arr2 = ['c']; var arr3 = ['d', 'e']; // ES5的合并数组 arr1.concat(arr2, arr3); // [ 'a', 'b', 'c', 'd', 'e' ] // ES6的合并数组 [...arr1, ...arr2, ...arr3] // [ 'a', 'b', 'c', 'd', 'e' ]
字符串
es6默认都是属于严格模式下,“...”为扩展运算符
let str = 'x\uD83D\uDE80y'; str.split('').reverse().join('') // 'y\uDE80\uD83Dx' [...str].reverse().join('') // 'y\uD83D\uDE80x'
name属性
函数的name
属性,返回该函数的函数名。
function fn() {} fn.name // "fn"
箭头函数
// 嵌套的箭头函数
function insert(value) { return {into: function (array) { return {after: function (afterValue) { array.splice(array.indexOf(afterValue) + 1, 0, value); return array; }}; }}; } insert(2).into([1, 3]).after(1); //[1, 2, 3] // 使用箭头函数 let insert = (value) => ({into: (array) => ({after: (afterValue) => { array.splice(array.indexOf(afterValue) + 1, 0, value); return array; }})}); insert(2).into([1, 3]).after(1); //[1, 2, 3]
现在问题来了,什么是链式调用?
解答:其实,上面的代码可以说是一种链式调用,使用面向对象写,里面写几个方法,就像如下代码一样,jquery中ajax使用promise一样,将异步用同步流程表达出来。
var Lianshi = function(){ } Lianshi.prototype = { css:function(){ console.log("设置css样式"); return this; }, show:function(){ console.log("将元素显示"); return this; }, hide:function(){ console.log("将元素隐藏"); } }; var lianshi = new Lianshi(); lianshi.css().css().show().hide();
箭头函数注意的四点:
(1)函数体内的this
对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)不可以当作构造函数,也就是说,不可以使用new
命令,否则会抛出一个错误。
(3)不可以使用arguments
对象,该对象在函数体内不存在。如果要用,可以用Rest参数代替。
(4)不可以使用yield
命令,因此箭头函数不能用作Generator函数。
绑定this
箭头函数可以绑定this,显示绑定this的写法(call、apply、bing),call、apply的作用就是能够改变this的指向
递归函数
// 斐波拉切数列 也是递归的一种 传的值过大,容易造成堆栈溢出 内存泄漏 function Fibonacci (n) { if ( n <= 1 ) {return 1}; return Fibonacci(n - 1) + Fibonacci(n - 2); } Fibonacci(10); // 89
// 尾调递归
function factorial(n, total = 1) { if (n === 1) return total; return factorial(n - 1, n * total); } factorial(5) // 120
只要在严格模式下,才能使尾调递归函数优化,(由于递归就是使用栈太多,造成溢出,想要优化,就只能减少栈的使用,故使用循环来进行递归)
额外说点:造成内存泄漏的原因:1)使用了递归; 2)使用了小数点,如:0.0000000001;3)setTimeout第一个参数是字符串
// 优化尾调递归函数 function trampoline(f) { while (f && f instanceof Function) { f = f(); } return f; } function sum(x, y) { if (y > 0) { return sum.bind(null, x + 1, y - 1); } else { return x; } } trampoline(sum(1, 100000)) // 100001
// 使用蹦床函数也可以优化尾调函数 function tco(f) { var value; var active = false; var accumulated = []; return function accumulator() { accumulated.push(arguments); if (!active) { active = true; while (accumulated.length) { value = f.apply(this, accumulated.shift()); } active = false; return value; } }; } var sum = tco(function(x, y) { if (y > 0) { return sum(x + 1, y - 1) } else { return x } }); sum(1, 100000) // 100001
原理:tco
函数是尾递归优化的实现,它的奥妙就在于状态变量active
。默认情况下,这个变量是不激活的。一旦进入尾递归优化的过程,这个变量就激活了。然后,每一轮递归sum
返回的都是undefined
,所以就避免了递归执行;而accumulated
数组存放每一轮sum
执行的参数,总是有值的,这就保证了accumulator
函数内部的while
循环总是会执行。这样就很巧妙地将“递归”改成了“循环”,而后一轮的参数会取代前一轮的参数,保证了调用栈只有一层。
额外说点:
继承:原型继承、原型链继承、call/apply继承
ES6 模块之中,顶层的this
指向undefined
,即不应该在顶层代码使用this
。
import
是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构。
require
是运行时加载模块,import
命令无法取代require
的动态加载功能。
import
和export
命令只能在模块的顶层,不能在代码块之中(比如,在if
代码块之中,或在函数之中)。