ES6 标准入门
Posted lu0511
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ES6 标准入门相关的知识,希望对你有一定的参考价值。
1、let所声明的变量只在let命令所在的代码块有效
2、不存在变量提升
在代码块内,使用let命令声明变量之前,该变量都是不可用的,称为暂时性死区。
3、不允许重复声明
let 不允许在相同作用域内重复声明一个变量
4、块级作用域
没有块级作用域的不合理的场景
ar temp = new Date(); function f() { console.log(temp); if(true)// true或false结果一样 { var temp = "hello" } }
f() //undefined
因为变量提升,导致内层的tmp变量覆盖了外层的tem变量
提升的为变量声明 var tmp
但是没有初始化,在if判断里才对声明的变量初始化
块级作用域的出现实际上使得广泛应用的立即执行函数不再必要了
//IIFE写法 (function() { var tmp = ...; }());
//块级作用域写法 { let tmp = ...; }
5、const命令
1)const用来声明常量。一旦声明,其值就不能改变
2)只在声明块级作用域有效
3)const命令声明的常量也不提升
4)对于复合类型的变量,变量名不指向数据,而是指向数据所在的地址。const命令只是保证变量名指向的地址不变,并不保证该地址的数据不变,所以将一个对象声明为常量必须十分小心
6、ES6一共有6种声明变量的方法:
var,function,let ,const,import,class
7、全局对象的属性
let,class,const声明的全局变量不属于全局变量的属性(仅仅不是他的属性)
8、变量的解构赋值
解构赋值允许指定默认值
var [a,b,c] = [1,2,3]
9、对象的解构赋值
var {foo,bar} = {foo:"aaa",bar:"bbb"}
对象的解构和数组有个重要的不同,数组的元素是按次序排列的,变量由它的位置决定。
对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
第七章 数组的扩展
1、Array.from
Array.from 用于将两类对象转为真正的数组:类似数组的对象和可遍历对象
下面是一个类似数组的对象:
let arraylike = { \'0\': \'a\', \'1\': \'b\', \'2\': \'c\', length: 3 }; // ES5的写法 var arr1 = [].slice.call(arraylike);//[\'a\',\'b\',\'c\'] //es6的写法 let arr2 = Array.from(arraylike);//[\'a\',\'b\',\'c\']
实际应用中,常见的类似数组的对象是DOM操作返回的Nodelist集合,以及函数内部的arguments对象,Array.from 都可以将他们转为真正的数组
只要是部署了interater接口的数据结构,Array.from 都能将其转为数组
set
所谓类似数组的对象,本质特征只有一点,必须有length属性,因此任何有 length属性的对象,都可以通过Array.from()转为数组
Array.from第二个参数类似map
Array.from(arraylike,x=>x*x)
Array.from(arraylike).map(x => x*x)
Array.of用于将一组值转为数组
copyWithin()
复制数组,会修改当前数组
find()
用于找到第一个符合条件的数组成员,他的参数为一个回调函数,所有成员执行这个回调函数,找到第一个返回true的成员,返回该成员,否则返回undefined
findIndex()
返回第一个符合条件的数组成员的位置,所有都不符合返回-1
fill()
使用给定值填充数组
entries(),keys(),values()
遍历数组
includes()
返回一个布尔值,表示某个数组是否包含给定的值
之前使用indexof,有两个缺点1、不够语义化 2、内部使用严格运算符(===)判断,对NaN误判
map和set解构有一个has注意区分,Map.prototype.has(key) 查找键名
Set.prototype.has(value)查找值
数组的空位
空位不是undefined一个位置等于undefined依然是有值的,空位是没有任何值
0 in [undefined,undefined,undefined]//true 0 in [, , ,]//false
0号位置有值,没值
Array.from()、扩展运算符会将空位转为undefined
数组推导
允许直接通过现有数组生成新数组
var a1 = [1,2,3,4]; var a2 = [for (i of a1) i*2]; a2 //[2,4,6,8]
第九章 函数的扩展
1、ES6之前,不能直接为函数的参数指定默认值,只能采用变通的方法
es6允许为函数的参数设置默认值,直接写在参数定义的后面
function log(x,y = \'World\'){ console.log(x,y) }
参数变量时默认声明的,所以不能用let或者const再次声明
2、函数的length属性
指定了默认值以后,函数的length属性将返回没有指定默认值的参数个数
(function(a){}).length // 1 (function(a = 5){}).length // 0
3、作用域
如果参数默认值是一个变量,则该变量所处的作用域与其他变量的作用域规则是一样的,先是当前函数的作用域然后才是全局作用域
let x = 1; function f(x,y = x){ console.log(y) } f(2);//2
以上,函数作用域的内部变量x已经生成,所以y等于参数x而不是全局变量x
let x = 1; function f(y = x){ let x = 2; console.log(y) } f();//1
函数调用时y的默认值变量x还没有在函数内部生成,所以x指向全局变量
如果函数A的参数默认值是函数B,那么由于函数的作用域是其声明时所在的作用域,函数B的作用域就不是函数A而是全局作用域。
4、rest参数
...变量名
用于获取函数的多余参数,这样就不需要使用arguments对象了
rest参数之后不能有其他参数,函数length属性不包括rest参数
5、扩展运算符
...
好比rest参数的逆运算,将一个数组转为用逗号分割的参数序列
console.log(...[1,2,3]) //1 2 3
6、替代数组的apply方法
由于扩展运算符可以展开数组,所以不需要apply方法将数组转为函数的参数了
//ES5 的写法 function f(x,y,z){} var args = [0,1,2]; f.apply(null,args); //ES6的写法 function f(x,y,z){} var args = [0,1,2]; f(...args);
7、扩展运算符的应用
1)合并数组
var arr1 = [\'a\',\'b\'] var arr2 = [\'c\'] var arr3 = [\'d\',\'e\'] //es5 arr1.concat(arr2,arr3) //es6 [...arr1,...arr2,...arr3]
2)字符串
扩展运算符可以将字符串专为真正的数组
8、name属性
函数的name属性返回该函数的函数名
function foo() {} foo.name // \'foo\'
9、箭头函数
var sum =(num1, num2) => num1 + num2; //等同于 var sum =function(num1, num2) { return num1 + num2; }
如果箭头函数的代码多于一条语句,就要使用大括号将其括起来,并使用return语句
箭头函数可以与变量解构结合使用
const full = ({first,last}) => first + \'\' + last; //等同于 function full(person) { return person.first + \'\' +person.name; }
箭头函数使用注意点:
1、函数体内this 对象就是定义时所在的对象,而不是使用时所在的对象
2、不可以当作构造函数,不可以使用new命令
3、不可以使用arguments对象,该对象在函数体内不存在,如果要用可以用rest参数代替
this指向的固定化并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this
由于箭头函数没有自己的this,当然也就不能用call(),apply(),bind()这些方法去改变this的指向
10、尾调用优化
尾调用是函数式编程的一个重要概念
就是指某个函数的最后一步是调用另一个函数
function f(x){ return g(x); }
函数调用会在内存中形成一个‘调用记录’。称为调用帧,保存调用位置和内部变量的信息。
如果在函数A的内部调用函数B,那么在A的调用帧的上方还会形成B的调用帧,等到b运行结束,将结果返回A,B的调用帧才会消失
尾调用由于是函数最后一步操作,所以不需要保留外层函数的调用帧,因为调用位置,内部变量等信息都不会再用到了,直接用内层函数的调用帧取代外层函数即可
function f() { let m = 1; let n = 2; return g(m+n); } f() //等同于 function f() { return g(3); } f() //等同于 g(3)
11、尾递归
递归非常耗费内存,因为同时需要保存成百上千个调用帧,很容易发生栈溢出错误,但对于尾递归来说,由于只存在一个调用帧,所以永远不会发生栈溢出错误
非尾调用,
function factorial(n) { if(n === 1) return 1; return n * factorial(n-1); } factorial(5) // 120
尾调用
function factorial(n, total) { if(n === 1) return total; return factorial(n - 1,n*total); } factorial(5,1);
如果改写为一个尾递归,只保留一个调用记录,复杂度为O(1)
尾递归的实现往往需要改写递归函数,确保最后一步只调用自身。做到这一点的方法就是把所有用到的内部变量改写成函数的参数
缺点是不太直观
方法一:在尾递归函数之外再提供一个正常形势的函数
function tailfactorial(n, total) { if(n === 1) return total; return factorial(n - 1,n*total); } function factorial(n) { return tailfactorial(n,1); } factorial(5);
函数式编程有一个概念:柯里化 :将多参数的函数转换成单参数的形式
柯里化是这样的一个转换过程,把接受多个参数的函数变换成接受一个单一参数(译注:最初函数的第一个参数)的函数,如果其他的参数是必要的,返回接受余下的参数且返回结果的新函数。
function currying(fn,n) { return function(m) { return fn.call(this, m, n) } } function tailfactorial(n, total) { if(n === 1) return total; return factorial(n - 1,n*total); } const factorial = currying(tailfactorial, 1); factorial(5)
方法二:采用ES6的默认值
function tailfactorial(n, total = 1) { if(n === 1) return total; return factorial(n - 1,n*total); }
第9章 对象的扩展
1、ES6允许在对象中只写属性名不写属性值
function f(x,y){ return {x,y}; } //等同于 function f(x,y){ return {x:x,y:y}; }
2、属性名表达式
方法一:
obj.foo = true
方法二:
obj[\'a\'+\'bc\'] = 123
3、方法的name属性
函数的name属性返回函数名,对象方法也是函数,因此也有name属性
如果对象方法是一个symbol值,那么name属性返回的是这个symbol值的描述
每个从Symbol()返回的
symbol值都是唯一的。一个symbol值能作为对象属性的标识符;这是该数据类型仅有的目的。
4、object.is()
object.is 用来比较两个值是否严格相等,它与严格比较运算符(===)的行为基本一致
Object.is(\'foo\',\'foo\') // true Object.is({},{}) // false
5、Object.assign() 这是一种浅复制
用来将源对象的所有可枚举属性复制到目标对象。它至少需要两个对象作为参数,第一个参数是目标对象,后面的参数都是源对象。
只要有一个参数不是对象,就会抛出错误
var target = {a: 1}; var source1 = {b:2}; var source2 = {c:3}; Object.assign(target,source1,source2); target //{a:1,b:2,c:3}
如果目标属性和源对象有同名属性,或者多个源对象有同名属性,后面的属性会覆盖前面的属性
var target = {a: 1,b:1}; var source1 = {b:2,c:2}; var source2 = {c:3}; Object.assign(target,source1,source2); target //{a:1,b:2,c:3}
Object.assign() 只复制自身属性,不可枚举属性和继承属性不会被复制。
对于嵌套的对象,Object.assign() 处理方法是替换而不是添加
var target = {a:{b:\'c\',d:\'e\'}} var source = {a:{b:\'hello\'}} Object.assign(target,source);//{a:{b:\'hello\'}}
为对象添加属性
为对象添加方法
Object.assign(someClass.prototype,{
someMethod(arg1, arg2) {
...
},
anotherMethod() {
}
});
克隆对象
不过这种方法克隆,只能克隆原始对象自身的值,不能克隆他继承的值
function clone(origin) { return Object.assign({},origin); }
合并多个对象
将多个对象合并到某个对象,如果希望合并后返回一个新对象,可以对一个空对象合并
const merge = (...sources) => Object.assign({},...sources);
为属性指定默认值
6、属性的可枚举性
for...in:只遍历对象自身的和继承的可枚举属性
object.keys():返回对象自身的所有可枚举属性
Object.assign()只复制对象自身的可枚举属性
7、属性的遍历
es6一共有6种方法可以遍历对象的属性
1)for ...in
2)Object.keys(obj)
返回一个数组
3)Object.getOwnPropertyNames(obj)
4) Object.getOwnPropertySymbols(obj)
5)Reflect.ownKeys(obj)
6) Reflect.enumerate(obj)
8、__proto__属性:用来读取或设置当前对象的prototype对象。
object.setPrototypeOf方法作用和__proto__相同
例子:
let proto = {}; let obj = {x:10}; Object.setPrototypeOf(obj, proto); proto.y = 20; proto.z = 40; obj.x //10 obj.y // 20 obj.z //40
以上代码将prop对象设置为obj对象的原型,所以从obj对象可以读取proto对象的属性 。
object.getPrototypeOf() 用于读取一个对象的prototype属性
function rectangle() { } var rec = new rectangle(); Object.getPrototypeOf(rec) === rectangle.prototype // true
9、对象的扩展运算符
rest参数
rest参数复制的是这个值的引用,而不是这个值的副本,即如果一个键的值是复合类型的值(数组,对象,函数),那么rest参数复制的是这个值的引用,而不是这个值的副本
let obj = {a: {b:1}}; let {...x} = obj; obj.a.b = 2; x.a.b // 2
扩展运算符
扩展运算符用于取出参数对象的所有可遍历属性,复制到当前对象
let aClone = {...a}; //等同于 let aClone = Object.assign({},a);
第13章 set和map数据结构
1、set
它类似于数组。但是成员的值都是唯一的,没有重复的值,Set本身是一个构造函数,用来生成Set数据结构
var s = new Set(); [2,3,5,4,5,2,2].map(x=>s.add(x)); for(i of s){ console.log(i)//2,3,5,4 }
结果表明,set结构不会添加重复的值
Set函数可以接受一个数组作为参数,用于初始化
var set = new Set([1,2,3,4,4]); [...set] //[1,2,3,4]
2、Set实例的属性和方法
属性:
Set.prototype.constructor; // 构造函数 Set.prototype.size; // 返回Set实例的成员总数
方法:
add(value)
delete(value)
has(value)
clear()
数组去重:
//数组去重 function dedupe(array) { return Array.from(new Set(array)); }
// 数组去重 let arr = [3,5,2,2,5,5]; let unique = [...new Set(arr)]; //[3,5,2]
使用set可以很容易的实现并集,交集,差集
// 数组求交 util.intersection = function (a, b) { let set1 = new Set(a); let set2 = new Set(b); let Intersect = new Set([...set1].filter(x => set2.has(x))); // console.log([...Intersect]); return [...Intersect]; }
// 数组求差 util.difference = function (a, b) { let set1 = new Set(a); let set2 = new Set(b); let difference = new Set([...set1].filter(x => !set2.has(x))); // console.log([...difference]); return [...difference]; }
// 并集 let a = new Set([1,2,3]); let b = new Set([4,3,2]); let union = new Set([...a,...b]);
3、Map
map结构的目的和基本用法
js的对象本质上是键值对的集合(hash结构)但是只能用字符串作为键
为了解决这个问题,es6提供了Map数据结构,它类似于对象,也是键值对的集合,但是键的范围不限于字符串,各种类型的值(包括对象)都可以当作键
object 结构提供了字符串-值的对应,map结构提供了值-值的对应,是一种更完善的hash结构实现
如果需要键值对的数据结构,map比object更合适
var m = new Map(); var o = {p:"hello world"}; m.set(o,"content"); m.get(o); // "content" m.has(o); //true m.delete(o); //true m.has(o); // false
第14章 iterator 和 for...of 循环
1、iterator 的概念
js 的原有的表示“集合”的数据结构,主要是数组和对象,es6又添加了map和set
需要一种统一的接口机制来处理所有不同的数据结构
遍历器是这样一种机制,它是一种接口,为各种不同的数据结构提供统一的访问机制。
任何数据结构只要部署了iterator 接口,就可以完成遍历操作。
iterator 作用有3个:
1)为各种数据结构提供一个统一的简便的访问接口
2)使得数据结构的成员能够按某种次序排列
3)es6创造了一种新的遍历命令,for...of循环,iterator 接口主要供for...of消费
Iterator 的遍历过程是这样的。
(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
(2)第一次调用指针对象的next
方法,可以将指针指向数据结构的第一个成员。
(3)第二次调用指针对象的next
方法,指针就指向数据结构的第二个成员。
(4)不断调用指针对象的next
方法,直到它指向数据结构的结束位置
2、for...of循环
以上是关于ES6 标准入门的主要内容,如果未能解决你的问题,请参考以下文章