《ECMAScript6 入门》阮一峰 读书笔记
- let和const
- let
- 声明的变量仅在
块级作用域内有效
,var声明的变量在全局范围内有效 - let
不存在变量提升
,所以变量一定要先声明后使用,换言之,var是有变量提升的(也就是可以先使用后声明,声明在编译的时候会被提升到最开头) - 暂时性死区,也就是使用let命令声明变量之前,该变量都是不可用的,也就是第二条。暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
-
不允许重复声明
,也就是在同一个作用域内不能重复声明同一个变量。
- 声明的变量仅在
- let
- 块级作用域
- 不合理场景一
var tmp = new Date();
function f() {
console.log(tmp);
if (false) {
var tmp = "hello world";
}
}
f(); // undefined,因为变量提升内层的tmp变量覆盖了外层的tmp变量????
🚩有疑惑,并不理解。。。
2. 不合理场景二,变量泄漏为全局变量(比如for循环里面的计数变量,如果用var来声明,其实在全局也是可以访问到的)
3. 块级作用域与函数声明
- es5规定函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域中声明,比如在if语句里面就不能声明一个变量,但实际上,浏览器是支持的
- es6明确允许在块级作用域之中声明函数。比较特别的是,块级作用域之中,函数声明语句的行为类似于`let`,在`块级作用域之外是不可以引用的`
```
eg:
function f(){console.log("I am outside");}
(function (){
if(false){
function f(){console.log("I am inside");}
}
f()
}())
*===================================================*
es5中实际上是如此运行的:
function f(){console.log("I am outside");}
(function (){
function f(){console.log("I am inside");}//变量提升
if(false){}
f()
}())
*===================================================*
es6中实际运行代码如下:
function f(){console.log("I am outside");}
(function(){
f()//在块级内声明的那个inside的f函数对作用域之外没有影响,所以相当于不存在,
调用的是外面的函数,因为作用域查找都是向外查找哒!
}())
```
- ⚠️为了兼容的问题,es6允许浏览器可以不遵守上面的规定,也就是可以有自己的行为方式,具体表现为(也就是实际上定义函数的时候遵守的规则,只针对支持es6的浏览器有效):
- 允许在块级作用域内声明函数
- 函数声明类似于var,即会提升到全局作用域或函数作用域的头部
- 同时,函数声明还会提升到所在的块级作用域的头部
- 在块级作用域内声明函数的时候,块级作用域一定要加大括号,比如在if里面声明一个函数,if后面一定要有大括号
const声明一个只读的常量,声明之后,值不能改变。所以,
声明的时候就要赋值
,其他的和let一样。有一个注意的点是,比如const了一个数组,这个数组本身是不能给赋值的,但是这个数组是可以push一个变量的!
-
顶层对象的属性
- 全局变量在es5中是顶层对象的属性,es6规定,var和function命令声明的全局变量依然是顶层对象的属性,但是let、const、class命令声明的全局变量不属于顶层对象的属性。
-
顶层对象
混乱的顶层对象
- 浏览器里面,顶层对象是window,但Node和Web Worker没有
window
- 浏览器和Web Worker里面,
self
指向顶层对象,但是Node 没有self
- Node里面,顶层对象是
global
,但其他环境都不支持
为了在不同的环境中都能去到顶层对象,一般使用
this
变量,但是有局限性
- 全局环境中,this
会返回顶层对象,但是,Node模块和ES模块中,this
返回的是当前模块
- 函数里面的this
,如果函数不是作为对象的方法运行,而是单纯作为函数运行,this
会指向顶层对象,但是,严格模式下,this
会返回undefined
。
- 不管是严格模式,还是普通模式,new Function('return this')()
,总是会返回全局对象,但是,如果浏览器用了CSP(Content Security Policy,内容安全政策),那么eval
、new Function
这些方法都可能无法使用。
综上所述,两种解决办法: 方法一: (typeof window !== 'undefined' ? window: (typeof process === 'object' && typeof require === 'function' && typeof global === 'object')? global : this ) 方法二: var getGlobal = function(){ if(typeof self !== 'undefined'){return self;} if(typeof window !== 'undefined'){return window;} if(typeof global !== 'undefined'){return global;} throw new Error('unalble to locate global object'); }
- 浏览器里面,顶层对象是window,但Node和Web Worker没有
- 变量的解构赋值
es6允许按照一定模式,从数组和对象中提取值,对变量进行复赋值,成为解构赋值。
- 数组的解构赋值
- “模式匹配”,也就是只要等号两边的模式相同,左边的变量就会被赋予对应的值。
let [foo, , baz] = [1,2,[d]];//foo就是1,baz就是[d]这个数组 let [head, ...tail] = [1,2,3,4];//head就是1,tail就是[2,3,4] let [x,y,...z] = ['a'];//x就是“ a”,y是undefined(解构不成功的话,变量的值就等于undefined),z是[],为啥z是[](????为啥😭),因为"..."是将右边剩余的值用数组的形式赋给左边的值,所以z就得到了一个空的数组 let [a,[b],d] = [1,[2,3],4];//a是1,b是2(完全对应过去,如果b不加外面的中括号,那b就是[2,3]),d是4
- 等号的右边如果不是可遍历的结构,会报错的
- 默认值,允许指定默认值,数组成员严格等于
undefined
才会使默认值生效
var [foo = true] = [];//foo是true
- 对象的解构赋值
- 变量必须与对象的属性同名,才能取到正确的值,如果没有对应的同名属性,取不到值的话,值为undefined
谁后面有“:”,谁就是模式,并不是变量var {bar, foo} = {foo:'aaa',bar:'bbb'}//foo的值是'aaa',bar的值是'bbb' //注意真正被赋值的是谁 var {foo:baz} = {foo:"aaa",bar:"bbb"}//baz的值是"aaa"而不是foo的值是“aaa”,foo是个“模式”
- 这种写法,变量的声明和赋值是一体的,像下面,如果用let或const是不行的,但是用var是可以的。
let foo; let {foo} = {foo:1}//这样会报错滴,因为foo在上面已经声明过了,所以不能再次声明 //如果非常想用let并且分开的话,可以用下面的方式 let foo; ({foo} = {foo:1})//这样不会报错,外面必须用()包起来,因为解析器会将起首的大括号理解成一个代码块而不是赋值语句。
- 默认值,默认值生效的条件是,对象的属性值严格等于
undefined
,同样,如果解构失败(也就是对应右边没有相同变量名的属性),那么变量的值等于undefined
- 实际应用场景:可以很方便的把一些对象的属性值赋值给其他的变量,不用一个一个的写
var { log,sin,cos } = Math
- 字符串的解构赋值
var [a,b,c] = 'hello'; //a==>h,b==>e,c==>l var {length:len} = 'hello'; //len的值是5,有点懵❓❓❓(类似数组的对象都有一个length属性,所以就5了❓❓❓)
- 数值和布尔值的解构赋值
解构赋值时,如果等号右边是数值或布尔值,则会先转为对象,(undefined和null无法转为对象,所以对他们解构赋值会出错)
- 函数形式参数的解构赋值
- 变量的解构赋值的用途
- 交换变量的值
[x,y] = [y,x]//数组的解构是严格按照顺序的
- 从函数返回多个值:函数一次只能返回一个值,如果要返回多个值,只能将她们放在数组或对象里返回,有了解构赋值,可以很方便的取出这些值
function example(){ return [1,2,3] } var [a,b,c] = example()//这样就赋值了,多方便😄
- 函数参数的定义
function f([x,y,z]){....} f([1,2,3])
- 提取JSON数据
var jsonData = { id:42, status:"ok", data:[22,3] } let { id, status, data:number } = jsonData;
- 遍历Map结构
- 输入模块的指定方法
- 字符串的扩展(这节好深奥,先放下😳
- 字符的Unicode表示法,
用\uxxxx形式来表示一个字符,其中xxxx表示字符的码点
- 字符的Unicode表示法,
- 正则的扩展
- 数值的扩展
-
数组的拓展
-
Array.from()
,用于将两类对象转为真正的数组:类似数组的对象和可遍历的对象。
let arrayLike = { '0':'a', '1':'b', '2':'c', length:3 };//类似数组的对象 let arr = Array.from(arrayLike);//['a','b','c']
- 实际应用中,常见的类似数组的对象是DOM操作返回的NodeList集合,以及函数内部的arguments对象。(所谓类似数组的对象本质特征只有一点,即必须有length属性,任何有length属性的对象,都可以通过Array.from方法转为数组)
-
Array.of()
,用于将一组值转换为数组,没有参数的时候返回一个空数组
Array.of(1,2,4)//【1,2,4】
-
copyWithin()
,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组,也就是会修改当前数组。
Array.prototype.copyWith(target,start,end)//接受三个参数,其中target是必需的,target代表从该位置开始替换数据,start表示从该位置开始读取数据,默认为0,负数则表示倒数,end表示到该位置前停止读取数据,默认等于数组长度,负数则表示倒数 [1,2,3,4].copyWithin(0,3)//[4,4,4,4],从0开始用3位置上(也就是4这个数字到末尾的0来替换0
- 数组实例的find()和findIndex()
find方法用于找出第一个符合条件的数组成员,参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。回调函数可以接受三个参数,
依次为当前的值,当前的位置,原数组
。[1,2,-5].find(n => n < 0)//返回第一个小于0的成员
findIndex方法返回第一个符合条件的数组成员的位置,如果都不符合条件,则返回-1,回调函数参数和find方法一致。
find和findIndex方法都可以接受除回调函数以外的第二个参数,用来绑定回调函数的this对象。
- 数组实例的fill()
fill方法使用给定值,填充一个数组。如果数组中已有元素,则会全部被抹去。参数和copyWithin函数的参数是一致的。
['a','b','c'].fill(7)//返回一个[7,7,7]
- 数组实例的entries(),keys()和values()----遍历数组的函数
------对键名的遍历 for(let index of ['a','b'].keys()){ console.log(index); }//结果如下 //0 //1 ------对键值的遍历 for(let elem of ['a','b'].values()){ console.log(elem); }//结果如下 //'a' //'b' ------对键值对的遍历 for(let [index,elem] of ['a','b'].entries()){ console.log(index,elem); }//结果如下 //0 "a" //1 "b"
- 数组实例的includes()
Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似。
[1,2,3].includes(2)//包含2,所以返回true
Map结构的has方法,是用来查找键名的 Set结构的has方法,是用来查找值的
- 数组的空位
数组的空位是指,数组的某一个位置没有任何值,es6明确的将空位转为undefined。以上数组的扩展方法都会把空位考虑进去,而不是忽略。
-
-
函数的扩展
- 函数参数的默认值
function log(x, y = 'world'){ ...}
- 与解构赋值默认值结合使用(没看懂😳
- 参数默认值的位置
非尾部的参数如果设置了默认值,那么这个参数是不能够省略的!
- 函数的length属性
指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数,也就是说,指定了默认值以后,length属性将失真。
失真也就是不算它
。(function (a,b=2){}).length //1
-
作用域---大爷
- 如果参数默认值是一个变量,则该变量所处的作用域,与其他变量的作用域规则是一样的,即先是当前函数的作用域,然后才是全局作用域。
情况一: var x = 1; function f(x, y = x){ 略 } //比如这里,y的默认值就是一个变量x,那么x的值要优先取传进来的x的值,如果没有, 才是全局变量中的x。
情况二: let x = 1; function f(y=x){ let x = 2; console.log(y); } f()//输出的是1,因为给y赋值的时候,x尚未在函数内部生成,故取全局变量中的x给y赋值, 如果此时全局变量中的x不存在,就会报错了。
情况三: let foo = "outer"; function bar(func = x=> foo){ let foo = "inner"; console.log(func()); } bar() //当函数的参数是一个函数的时候,该函数的作用域是其声明时所在的作用域。 ⚠️⚠️bar方法的参数func是一个匿名函数,具体的意义是传进去x,返回值为foo, 也就是func()的值,匿名函数声明的时候(也就是给func赋值的时候),bar函数的作 用域还没有形成,所以匿名函数里面的foo指向外层作用域来的foo,输出的是outer ⚠️⚠️
2.rest参数
rest参数形式为"...变量名",用于获取函数的多余参数,这样就不需要使用arguments对象了,rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中。👉需要注意的是rest参数之后不能再有其他参数,也就是这个参数只能放在最后面。
function add(...values){ let sum = 0; for(var val of values){ sum+=val; } return sum; } add(2,4,5)//神似python里的参数🙄
3.扩展运算符
扩展运算符(spread)是三个点
...
,rest参数的逆运算,将一个数组转为用逗号分隔的参数序列。注意,扩展运算符针对的是数组数组数组❗️也就是把一个数组相当于给拆开了。console.log(...[1,2,3]) //1 2 3
function push(array, ...items){ array.push(...items); } function add(x, y){ return x + y; } var numbers = [4,3]; add(...numbers)//7
替代数组的apply方法(⚠️总结一下apply方法,因为得知道apply究竟是干嘛才知道怎么个替代法🚩10.26)
-
扩展运算符的应用:
- 合并数组
var arr1 = ['a','b']; var arr2 = ['c','d']; var arr = [...arr1,...arr2]
- 与解构赋值结合
这里和前面的解构赋值很容易混啊🚩 const [first,...rest]=[1,2,3,4]; first===》1 rest====》2,3,4 //有个易错点,和前面很像,就是i 这个...变量只能放在最后⚠️
- 函数的返回值,返回多个值的时候
- 字符串,可以将字符串转换为真正的数组
[...'hello'] //['h','e','l','l','o']
- 实现了Iterator接口的对象,任何Iterator接口的对象,都可以用扩展运算符转为真正的数组。
let arrayLike = { '0':'a', '1':'b', '2':'c', length:3 }; let arr = [...arrayLike]//TypeError //arrayLike是一个类似数组的对象,但是没有部署Iterator接口,扩展运算符就会报错, 这时,可以改为使用Array.from方法将arrayLike转为真正的数组。
- Map结构和Set结构,Generator函数
扩展运算符内部调用的是数据结构的Iterator接口,因此只要具有Iterator接口的对象,都可以使用扩展运算符。
4.严格模式
use strict
es6规定,只要函数的形参使用了默认值、解构赋值或者扩展运算符,那么函数内部就不能显示的设定为严格模式。
5.name属性,函数的name属性,返回该函数的函数名
6.箭头函数
=>
- 如果箭头函数不需要参数或者需要多个参数,就使用一个圆括号代表参数部分。
- 如果箭头函数的代码块部分多于一条语句,就要使用大括号将他们括起来,并且使用return语句返回
- 如果要返回一个对象,外面要用小括号包起来,因为大括号被解释为代码块,所以必须在对象外面加上括号
-
函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象
,在箭头函数中,this是固定的。因为箭头函数没有自己的this,他们的this其实都是外层对象(也就是函数)的this。所以bind(),call(),apply()这些方法无法改变this的指向。 - 不可以当做构造函数,也就是不能用new操作符
- 不可以使用arguments对象,该对象在函数体内不存在,如果要用,可以用rest参数代替
- 不可以使用yield命令,因此箭头函数不能用做generator函数
7.绑定this
::
es7的一个题案,如果用的话需要babel转码器双冒号左边是一个对象,右边是一个函数,该运算符会自动将左边的对象,作为上下文环境(即this对象)绑定到右边的函数上面,如果双冒号左边为空,右边是一个对象的方法,则等于将该方法绑定在该对象上面。
8.尾调用优化😳看不懂🚩10.27
尾调用是函数式编程的一个重要概念,指的是某个函数的最后一步是调用另一个函数
-
对象的扩展
- 属性的简洁表示法
情况一: var foo = 'a'; var baz = {foo}; //以往写对象的时候要属性名、属性值成对出现,es6允许在对象中,只写属性名, 不写属性值,这时,属性值等于属性名所代表的变量。 情况二: var o = { method(){ return "hello";//这不就是之前写react的用法嘛😮 } }; 等同于: var o = { method:function(){ return "hello"; } }; 综合的例子: var birth = '2000/01/01'; var Person = { name: '张三', //等同于birth: birth birth,//react返回对象的时候就是这样写的!!!😌 // 等同于hello: function ()... hello() { console.log('我的名字是', this.name); } };
- 属性名表达式🚩
- Object.js()
比较两个值是否相等,只有两个运算符:相等运算符
==
和严格相等运算符===
,前者会自动转换数据类型,后者的NaN不等于自身,以及+0
等于-0
。Object.is用来比较两个值是否严格相等,弥补了===
的缺点。- Object.assign()
Object.assign()
用于对象的合并,将源对象的所有可枚举属性,复制到目标对象。Object.assign(target,source1,source2...) //第一个参数是目标对象,也就是最终的,后面都是源对象。如果只有一个参数那就直接返回 //⚠️⚠️⚠️⚠️如果目标对象与源对象有同名属性,或者多个源对象有同名属性,则后面的属性会 覆盖前面的属性 ⚠️undefined和null不能放在目标对象的位置(放,报错,不放也就是放在别的位置会被跳过) ⚠️字符串可以作为源对象,复制到目标对象里会是数组的形式,布尔值和数值都不能作为源对象, 虽然不报错,但是会跳过
⚠️注意点 1.Object.assign是浅拷贝,如果源对象某个属性的值是对象,如果源对象某个属性的值是对象, 那么目标对象拷贝得到的是这个对象的引用。 2.Object.assign也可以处理数组,但是也是将数组视为对象。
- Object.assign常见用途 - 为对象添加属性 - 为对象添加方法,目标对象就是xxx.prototype,然后源对象就是用大括号包裹的函数 - 克隆对象 - 合并多个对象 - 为属性指定默认值(可以将属性的默认值为一个对象,用户输入的为另一个对象,两个对象作为源对象复制给目标对象,因为用户输入的放在后面所以会覆盖掉默认值对象的值,so~)
- 属性的可枚举性
对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为。
Object.getOwnPropertyDescriptor
方法可以获取该属性的描述对象let obj = {foo:123}; Object.getOwnPropertyDescriptor(obj,'foo') //{ // value:123, // writable:true, // enumerable:true,是否可以枚举 // configurable:true //}
- 属性的遍历
-
for...in
,遍历对象自身的和继承的可枚举属性(不包含Symbol属性) -
Object.keys(obj)
,返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性)。 -
Object.getOwnPropertyNames(obj)
,返回一个数组,包含对象自身的所有属性(不含Symbol属性,但是包括不可枚举属性)。 -
Object.getOwnPropertySymbols(obj)
,返回一个数组,包含对象自身的所有Symbol属性 -
Reflect.ownKeys(obj)
,返回一个数组,包含对象自身的所有属性,不管是属性名是Symbol或字符串,也不管是否可以枚举。
-