1 let与const的区别
1.1 let与const的区别
-
let与const都是只在声明所在的块级作用域内有效
-
let声明的变量可以改变,值和类型都可以改变,没有限制。但是,let 不能重复定义一个变量,只能定义一次。
-
const声明常量,不能改变,且声明的时候必须赋值,如果赋值的时候是一个对象,是可以改变的,因为指针指向的是对象,且没有发生改变,但对象里面的内容是可以发生改变的。
1.2 块作用域
-
{ 该处为块作用域 },也就是说 你只需要用 {} 包起来,这个 {} 就是一个代码块
1.3 严格模式
-
es6中已经默认是严格模式
-
声明方式:‘use strict ’
-
例子
'use strict’ function test(){ for(leti=0;i<3;i++){ console.log(i); } console.log(i);//报引用错误,因为采用了严格模式 } test();
2 ES6之解构赋值
2.1 解构的概念
- ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。
- 通俗的理解,解构:左边一种结构,右边一种结构
2.2 解构赋值的分类
- 数组解构赋值:左,右数组
<span style="color:#000000">let a,b,rest; [a,b]=[1,2]; [a,b,...rest]=[1,2,3,4,5,6]; 结果:a=1,b=2 a=1,b=2,rest=[3, 4, 5, 6] </span>
- 对象解构赋值:左,右对象
<span style="color:#000000">let a,b; ({a,b}={a:2,b:0}) 结果:a=2,b=0;</span>
- 字符串解构赋值:左数组,右字符串
- 布尔值解构赋值:属于对象解构的一种
- 函数参数解构赋值
2.3 默认值
- 数组:
<span style="color:#000000">[a,b,c=3]=[1,2]; [a,b,c]=[1,2]; 结果:a=1,b=2,c=3; a=1,b=2,c=undefined; </span>
- 对象
<span style="color:#000000">let {a=10,b=2,c=8}={a:4} 结果:a=4,b=2,c=8;</span>
2.4 使用场景
(1)变量值的交换
- 数组
let a=1;
let b=2;
[a,b]=[b,a];
结果: a=2,b=1
- 对象:
let a={q:22,p:false}; let {p,q}=a; 结果:p=22,q=false;
(2)取函数中的变量值,可以选择性的取值
- 数组:
functionfoo(){ return[1,2,3,4]; } leta,b; [a,b]=foo(); console.log(a,b);--a=1,b=2 [a,,,b]=foo(); console.log(a,b);--a=1,b=4 [a,…b]=foo(); console.log(a,b);--a=1,b=[2,3,4]
- 数组与对象的联合使用--主要应对取后台数据相对应的值
let metaData={
title:'metaData',
test:[{
title:'test',
desc:'description';
}]
}
{title:esTitle,test:[{title:testTitle}]}=metaData;
结果:esTitle=metaData,testTitle=test;
3 ES6之正则扩展
3.1 正则新增加特性
-
构造函数的变化
-
正则方法的扩展
-
u修饰符
-
y修饰符
-
s修饰符
3.2 表达方式的不同
- es5的表示方法:
let regexp1=newRegExp('xyz','i'); let regexp2=newRegExp(/xyz/i); console.log(regexp1.test('xyz123'),regexp2.test('xyz123'));
-
es6的表示方法:es6中允许有第二个参数,但是第二个参数(i)会覆盖第一个参数(ig)
- flags是es6新增加的,用来获取正则修饰符
let regexp3=new RegExp(/xyz/ig,'i'); console.log(regexp3.flags);//i
- flags是es6新增加的,用来获取正则修饰符
3.3 修饰符
-
y修饰符与g修饰符
-
都是全局匹配
-
g修饰符是从上一次匹配的位置的下一个位置开始匹配,直到匹配到为止,不强调是从哪个位置匹配到的。
-
y修饰符是从上一次匹配的位置的下一个位置开始匹配,必须是紧跟着的下一个字符匹配成功,才能够匹配。其中,sticky用来判断是否开启了y修饰符,开启了返回true,未开启返回false
- 注:exec()用于检索字符串中的正则表达式的匹配。
let s ='bbb_bb_b'; let a =/b+/g; let b =/b+/y; console.log('one',a.exec(s),b.exec(s));//one ["bbb", index: 0, input: "bbb_bb_b"]["bbb", index: 0, input: "bbb_bb_b"] console.log('two',a.exec(s),b.exec(s));//two ["bb", index: 4, input: "bbb_bb_b"]null console.log(a.sticky,b.sticky);//false true
-
-
u修饰符
- 用于unicode编码
console.log('u-1',/^\uD83D/.test('\uD83D\uDC2A'));//u-1 true console.log('u-2',/^\uD83D/u.test('\uD83D\uDC2A'));u-2 false
-
注:没有u的会把\uD83D\uDC2A当作是2个字节,有u的会把\uD83D\uDC2A当作是1个字节
- 用于unicode编码
-
点(.)
-
处理的正则表达式中大于等于一个字符,则必须要加上/u;
-
es5中可以匹配所有字符,es6中只能匹配小于两个的所有字符,es6中并不是匹配所有字符
-
回车符,换行符,行分隔符,段分隔符也是不能识别的
-
-
s修饰符--还未实现
4 ES6之字符串扩展(重要)
4.1 字符串新增特性:
- Unicode表示法
- 遍历接口
- 模板字符串
- 新增方法
4.2 Unicode表示法
- 字符的编码大于0XFFFF的时候,unicode编码要用{}包起来
- charAt()方法可返回指定位置的字符
- charCodeAt()方法可返回指定位置的字符的 Unicode 编码。这个返回值是 0 - 65535 之间的整数。
- 方法charCodeAt() 与 charAt() 方法执行的操作相似,只不过前者返回的是位于指定位置的字符的编码,而后者返回的是字符子串.
console.log('a',"\u0061");//a a console.log('s','\u20BB7');// s ₻7(被分解成了两个字符) console.log('s','\u{20BB7}');//1
4.3 遍历接口
- for ... of 循环遍历字符串
let text = 'abc' for (let i of text) { console.log(i); }
4.4 模板字符串
- 传统的JavaScript语言,输出摸板通常:
$("#result").append( "There are <b>" + basket.count + "</b> " + "items in your basket, " + "<em>" + basket.onSale + "</em> are on sale!" );
- ES6的模板字符串:
-
其中,将字符串写在``中,即`字符串`。${}表示变量。不需要像js中表示变量要用+连接字符串和变量。$("#result").append(` There are <b>${basket.count}</b> items in your basket, <em>${basket.onSale}</em> are on sale! `);
4.5 新增方法(10种)
子字符串的识别
-
【includes()】
该方法在给定文本存在于字符串中的任意位置时会返回 true ,否则返回false
-
【startsWith()】
该方法在给定文本出现在字符串起始处时返回 true ,否则返回 false
-
【endsWith()】
该方法在给定文本出现在字符串结尾处时返回 true ,否则返回 false
字符串的重复var s = 'Hello world!'; s.startsWith('Hello') // true s.endsWith('!') // true s.includes('o') // true //三个方法都支持第二个参数,表示开始搜索的位置 s.startsWith('world', 6) // true s.endsWith('Hello', 5) // true 向前搜索 s.includes('Hello', 6) // false
-
【repeat()】
ES6为字符串添加了一个 repeat() 方法,它接受一个参数作为字符串的重复次数,返回一个将初始字符串重复指定次数的新字符串
补全长度'x'.repeat(3) // "xxx" 'hello'.repeat(2) // "hellohello" 'na'.repeat(0) // ""
-
【padStart()】
头部补全
【padEnd()】
尾部补全
'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'
'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'
//原字符串的长度,等于或大于指定的最小长度,则返回原字符串
'xxx'.padStart(2, 'ab') // 'xxx'
'xxx'.padEnd(2, 'ab') // 'xxx'
//补全的字符串与原字符串,两者的长度之和超过了指定的最小长度,则会截去超出位数的补全字符串
'abc'.padStart(10, '0123456789')// '0123456abc'
//省略第二个参数,则会用空格补全长度
'x'.padStart(4) // ' x'
'x'.padEnd(4) // 'x '
5 ES6之数值扩展
5.1 数值处理新增特性
1、新增方法
2、方法调整
5.2进制
-
不区分大小写
console.log('b',0b111110111);//二进制503
console.log('B',0B111110111);//二进制503
console.log(0o767);//八进制503
5.3 判断是否有限
-
Number.isFinite()
console.log('15',Number.isFinite(15));--true
console.log('NaN',Number.isFinite(NaN));--false
console.log('1/0',Number.isFinite('true'/0));--false
5.4 判断是否是数
-
Number.isNaN()
console.log('NaN',Number.isNaN(NaN));--true
console.log('0',Number.isNaN(0));--false
5.5 判断是否是整数
-
Number.isInteger(必须是一个数)--重要
console.log('25',Number.isInteger(25));--true
console.log('25.0',Number.isInteger(25.0));--true
console.log('25.1',Number.isInteger(25.1));--false
console.log('25',Number.isInteger('25'));--false
5.6 支持的最大、最小限
-
Number.MAX_SAFE_INTEGER,Number.MIN_SAFE_INTEGER
console.log(Number.MAX_SAFE_INTEGER,Number.MIN_SAFE_INTEGER);----9007199254740991 -9007199254740991
5.7 判断一个数是不是安全的数--是否在最大、最小范围之内
-
Number.isSafeInteger(必须是一个数字)
console.log('10',Number.isSafeInteger(10));--true
console.log('a',Number.isSafeInteger(a);//false
5.8 返回带小数的整数
-
Math.trunc()向下取整
console.log(4.1,Math.trunc(4.1));--4
console.log(4.9,Math.trunc(4.9));--4
5.9 判断0,正数,负数
-
Math.sign(必须是数字)
console.log('0',Math.sign(0));-- 0
console.log('5',Math.sign(5));-- 1
console.log('-5',Math.sign(-5));-- -1
console.log('foo',Math.sign('foo'));-- NaN
注:0就返回0,正数返回1,负数返回-1,字符串返回NaN
5.10 立方根的计算--Math.cbrt( )
5.11 三角函数
5.12 对数
6 ES6之数组扩展(重要)
ES6对于数组又扩展了很多方法,包括静态方法和原型链上添加的方法,让我们可以更方便的操作数组。
- Array.from()
-
Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括ES6新增的数据结构Set和Map)。
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']
- 如果参数是一个数组,那么会返回一个一模一样的新数组(浅拷贝),而且这个方法会将数组空位转换为undefined;换句话说,会忽略空位。
let arr = Array.from([10, , 30]); console.log(arr); //[10, undefined, 30]
- 还有一个额外的参数 ,用于对元素进行处理 ,类似于ES5的数组方法map。
let arrLike = { 0: 10, 1: 20, 2: 30, length: 3 } let arr = Array.from(arrLike,function(x){ return x*x; }); console.log(arr); //[100, 400, 900]
-
- Array.of()
-
用于将一组值转换为数组,它弥补了使用new Array()构造数组的奇怪行为,比如说填入一个参数,只填写一个参数就会构建一个长度为3的稀疏数组
let arr = new Array(3); //构建了一个稀疏数组
- Array.of()可以很好的解决这个问题
let arr=Array.of(3,4,5,6,7); console.log('arr=',arr);//arr=[3, 4, 5, 6, 7] let empty=Array.of(); console.log('empty=',empty);//empty= []
-
-
内部拷贝CopyWithin()
- 这是一个原型方法,作用是把这个数组的一部分元素复制到其他位置 ,这会覆盖原有的元素 ,返回当前数组 .换句话说,这个方法会修改原数组.
- 第一个参数代表从这个位置开始替换
后两个参数代表要拷贝的起始位置和结束为止
同样不包含结束元素,左闭右开
可以使用负值代表倒数let arr = [1, 2, 3, 4, 5, 6, 7, 8]; arr.copyWithin(1, 5, 8); console.log(arr); //[1, 6, 7, 8, 5, 6, 7, 8]
-
查找find()或findIndex()
- 这两个原型方法都有一个回调函数作为参数 (回调函数的参数依次为元素、索引、数组引用,与ES5数组方法相同)
find()会返回第一个满足条件的元素 ,findIndex()会返回第一个满足条件的索引 ,没有找到都会返回-1 ,这里满足条件意思就是函数参数返回值为trueconsole.log([1,2,3,4,5,6].find(function(item){ return item > 3;//4 (只返回第一个满足条件的内容) })) console.log([1,2,3,4,5,6].findIndex(function(item){ return item>3;//3(返回第一个满足条件的下标) }))
- 这两个原型方法都有一个回调函数作为参数 (回调函数的参数依次为元素、索引、数组引用,与ES5数组方法相同)
-
填充fill()
- 用于填充数组 ,会修改调用它的数组
console.log('fill-1',[1,'a',undefined].fill(7));//7 7 7 console.log('fill,pos',[1,'a',undefined].fill(7,1,3));//fill,pos [1, 7, 7]
- 用于填充数组 ,会修改调用它的数组
-
包含include()
- 用于检测数组是否含有某个特定值,返回布尔值。这个方法甚至连NaN都可以检测到。
console.log('number',[1,2,NaN].includes(1));//true
- 但是我们ES5中的indexOf()是不可以的
由此可见indexOf()内部是用严格等于判断的 ,我们可以这样来判断NaNlet arr = [1, 'a', true, null, NaN]; console.log(arr.indexOf(1)); //0 console.log(arr.indexOf('a')); //1 console.log(arr.indexOf(true)); //2 console.log(arr.indexOf(null)); //3 console.log(arr.indexOf(NaN)); //-1
let demo = NaN; console.log(demo + '' === 'NaN'); //true
- 用于检测数组是否含有某个特定值,返回布尔值。这个方法甚至连NaN都可以检测到。
这里注意isNaN()不仅仅是非数返回true ,不是数字都会返回true
-
数组迭代entries()/keys()/values()
- 它们用于迭代数组,均返回一个迭代器对象 ,配合for-of循环可以迭代数组
for(let index of ['1','c','ks'].keys()){ console.log('keys',index);//012 } for(let value of ['1','c','ks'].values()){ console.log('values',value);//1 c ks } for(let [index,value] of ['1','c','ks'].entries()){ console.log('index->values',index,value); }// index->values 0 1 index->values 1 c index->values 2ks
- 不过for-of就是用于遍历数组的(用for-in遍历对象),如果全遍历使用for-of更加方便
- 它们用于迭代数组,均返回一个迭代器对象 ,配合for-of循环可以迭代数组
7 ES6之函数扩展(非常重要)
-
箭头函数
- 基本用法 :ES6允许使用“箭头”(=>)定义函数,即 函数名 = (函数参数) => {函数返回值}
//不是箭头函数
var f = function (v){
return v;
}; //箭头函数(有参数)
var f=(v)=>{
return v;
}
//箭头函数(无参数)
var f=()=>{
return 5;
}
-
- 箭头函数有几个使用注意点。
(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用Rest参数代替。
(4)不可以使用yield命令,因此箭头函数不能用作Generator函数。
上面四点中,第一点尤其值得注意。this对象的指向是可变的,但是在箭头函数中,它是固定的。
上面代码中,setTimeout的参数是一个箭头函数,这个箭头函数的定义生效是在foo函数生成时,而它的真正执行要等到100毫秒后。如果是普通函数,执行时this应该指向全局对象window,这时应该输出21。但是,箭头函数导致this总是指向函数定义生效时所在的对象(本例是{id: 42}),所以输出的是42。function foo() { setTimeout(() => { console.log('id:', this.id); }, 100); } var id = 21; foo.call({ id: 42 }); // id: 42
箭头函数可以让setTimeout里面的this,绑定定义时所在的作用域,而不是指向运行时所在的作用域。
- 箭头函数有几个使用注意点。
-
参数默认值
-
//如果使用let或者const在函数内部再次声明会报错 function Point(x=0,y=0){ this.x=x; this.y=y; } var p=new Point() console.log(p.x,p.y);//0,0 //函数不能有同名参数,否则就报错 function Point2(x=0,x,y=0){ //Duplicate parameter name not allowed in this context this.x=x; this.y=y; } //如果参数默认值是包含变量的表达式,那么参数就不能传值,需要修改变量的值来改变参数值 let o=99; function foo(p=o+1){ console.log(p) } foo();//100 o = 100; foo() // 101
-
-
参数作用域
-
//当函数的参数中有设置默认值的参数时,包含参数的圆括号就会形成一个作用域 let x=1; //参数y有默认值x,所以圆括号就形成一个临时作用域,y=变量x,变量x=参数x,参数通过传值得到 2 ,而取不到全局的x值 function f(x,y=x){ console.log(y); } f(2)//2 ---------------------------------------------------------------------------------- let x=1; //参数y有默认值x,所以圆括号就形成一个临时作用域,y=变量x,由于本作用域中没有x变量,所以向全局中取得x的值 function f(y=x){ console.log(y); } f()//1 -------------------------------------------------------------------------------- //下面这样写 也会报错,变量必须先声明再调用 var x = 1; function foo(x = x) { ... } foo() // ReferenceError: x is not defined
-
-
rest参数
- 把一系列的参数转换成数值,rest参数之后不能有其他的参数(值转换成数组)
- rest参数 与之前的变量结构赋值的rest变量是一个东西;
需要注意的是,rest参数会被length属性忽略,就是前面说的函数length;
rest参数与参数默认值一样 必须放到参数的最后一个 -
参数的形式: …变量名
rest搭配的变量是一个数组,就可以不再使用arguments对象了
function test3(...arg){ for(let v of arg){ console.log('rest参数',v); } } test3(1,2,3,4,'a'); // rest参数1 rest参数2 rest参数3 rest参数 4 rest参数 a
-
扩展运算符
- 是rest参数的逆运算(将数组转换为用逗号分隔的参数列表)
- 形式: …
-
console.log(...[1,2,4]);//1 2 4转换成一个个的值 console.log('a',...[1,2,4]);//a 1 2 4
-
尾调用
- 尾调用(Tail Call)是函数式编程的一个重要概念,本身非常简单,一句话就能说清楚,就是指某个函数的最后一步是调用另一个函数。
上面代码中,函数f的最后一步是调用函数g,这就叫尾调用。function f(x){ return g(x); }
以下三种情况,都不属于尾调用。
上面代码中,情况一是调用函数g之后,还有赋值操作,所以不属于尾调用,即使语义完全一样。情况二也属于调用后还有操作,即使写在一行内。情况三等同于下面的代码。// 情况一 function f(x){ let y = g(x); return y; } // 情况二 function f(x){ return g(x) + 1; } // 情况三 function f(x){ g(x); }
尾调用不一定出现在函数尾部,只要是最后一步操作即可。function f(x){ g(x); return undefined; }
上面代码中,函数m和n都属于尾调用,因为它们都是函数f的最后一步操作。function f(x) { if (x > 0) { return m(x) } return n(x); }
- 尾调用(Tail Call)是函数式编程的一个重要概念,本身非常简单,一句话就能说清楚,就是指某个函数的最后一步是调用另一个函数。
-
尾调用优化
- 尾调用之所以与其他调用不同,就在于它的特殊的调用位置。
我们知道,函数调用会在内存形成一个“调用记录”,又称“调用帧”(call frame),保存调用位置和内部变量等信息。如果在函数A的内部调用函数B,那么在A的调用帧上方,还会形成一个B的调用帧。等到B运行结束,将结果返回到A,B的调用帧才会消失。如果函数B内部还调用函数C,那就还有一个C的调用帧,以此类推。所有的调用帧,就形成一个“调用栈”(call stack)。
尾调用由于是函数的最后一步操作,所以不需要保留外层函数的调用帧,因为调用位置、内部变量等信息都不会再用到了,只要直接用内层函数的调用帧,取代外层函数的调用帧就可以了。
上面代码中,如果函数g不是尾调用,函数f就需要保存内部变量m和n的值、g的调用位置等信息。但由于调用g之后,函数f就结束了,所以执行到最后一步,完全可以删除 f(x) 的调用帧,只保留 g(3) 的调用帧。function f() { let m = 1; let n = 2; return g(m + n); } f(); // 等同于 function f() { return g(3); } f(); // 等同于 g(3);
这就叫做“尾调用优化”(Tail call optimization),即只保留内层函数的调用帧。如果所有函数都是尾调用,那么完全可以做到每次执行时,调用帧只有一项,这将大大节省内存。这就是“尾调用优化”的意义。
注意,只有不再用到外层函数的内部变量,内层函数的调用帧才会取代外层函数的调用帧,否则就无法进行“尾调用优化”。function addOne(a){ var one = 1; function inner(b){ return b + one; } return inner(a); }
- 尾调用之所以与其他调用不同,就在于它的特殊的调用位置。
-
尾递归
- 函数调用自身,称为递归。如果尾调用自身,就称为尾递归。
递归非常耗费内存,因为需要同时保存成千上百个调用帧,很容易发生“栈溢出”错误(stack overflow)。但对于尾递归来说,由于只存在一个调用帧,所以永远不会发生“栈溢出”错误。
上面代码是一个阶乘函数,计算n的阶乘,最多需要保存n个调用记录,复杂度 O(n) 。function factorial(n) { if (n === 1) return 1; return n * factorial(n - 1); } factorial(5) // 120
如果改写成尾递归,只保留一个调用记录,复杂度 O(1) 。
还有一个比较著名的例子,就是计算fibonacci 数列,也能充分说明尾递归优化的重要性function factorial(n, total) { if (n === 1) return total; return factorial(n - 1, n * total); } factorial(5, 1) // 120
如果是非尾递归的fibonacci 递归方法
如果我们使用尾递归优化过的fibonacci 递归算法function Fibonacci (n) { if ( n <= 1 ) {return 1}; return Fibonacci(n - 1) + Fibonacci(n - 2); } Fibonacci(10); // 89 // Fibonacci(100) // Fibonacci(500) // 堆栈溢出了
由此可见,“尾调用优化”对递归操作意义重大,所以一些函数式编程语言将其写入了语言规格。ES6也是如此,第一次明确规定,所有ECMAScript的实现,都必须部署“尾调用优化”。这就是说,在ES6中,只要使用尾递归,就不会发生栈溢出,相对节省内存。function Fibonacci2 (n , ac1 = 1 , ac2 = 1) { if( n <= 1 ) {return ac2}; return Fibonacci2 (n - 1, ac2, ac1 + ac2); } Fibonacci2(100) // 573147844013817200000 Fibonacci2(1000) // 7.0330367711422765e+208 Fibonacci2(10000) // Infinity
- 函数调用自身,称为递归。如果尾调用自身,就称为尾递归。
-
尾递归优化
- 尾递归优化只在严格模式下生效,那么正常模式下,或者那些不支持该功能的环境中,有没有办法也使用尾递归优化呢?回答是可以的,就是自己实现尾递归优化。
它的原理非常简单。尾递归之所以需要优化,原因是调用栈太多,造成溢出,那么只要减少调用栈,就不会溢出。怎么做可以减少调用栈呢?就是采用“循环”换掉“递归”。
- 尾递归优化只在严格模式下生效,那么正常模式下,或者那些不支持该功能的环境中,有没有办法也使用尾递归优化呢?回答是可以的,就是自己实现尾递归优化。
注意:尾调用和尾递归是参考别处。
8 ES6之对象扩展
函数新增特性:
简洁表示法
(1)普通对象
let a=1;
let b=2;
let es5={
a:a,
b:b
};
let es6={
a,
b
};
console.log(es5,es6);
Object{a: 1, b: 2} Object {a: 1, b:2}结果一样
(2)带有函数的对象
let es5_methods={
hello:function(){
console.log('hello');
}
};
let es6_methods={
hello(){
console.log('hello');
}
};
console.log(es5_methods.hello(),es6_methods.hello());--hello
属性表示式
let a='b';
let es5_obj={
a:'c',
b:'c'
};
console.log(es5_obj);--Object {a: "c", b:"c"}
let es6_obj={
[a]:'c'
};
console.log(es6_obj);--Object {b: "c"}
扩展运算符
--import'babel-polyfill'支持性不好
let {a,b,...c}={a:'test',b:'kill',c:'ddd',d:"ccc"};
Object新增方法
判断是否相等 类似于===
Object.is('字符串','字符串');
Object.is('数组','数组');
拷贝:合并两个对象--浅拷贝,只拷贝自身对象的属性,不拷贝来自继承,枚举的对象
Object.assign({原始对象},{要拷贝的对象});
9 ES6之Symbol
Symbol的概念
提供一个独一无二的值,是一种数据类型
//声明
Let a1=Symbol();
Let a2=Symbol();
console.log(a1===a2);--false永不相等
//Symbol.for()与Symbol()一样都是声明一个值,但是Symbol.for(key)会检查之前有没有声明过key,如果声明了则返回声明的值,之前没有声明过,则生成一个独一无二的值
Let a3=Symbol.for('a3');
Let a4=Symbol.for('a3');
console.log(a3===a4);---true
symbol的作用
(1)
let a1=Symbol.for('abc');
let obj={
[a1]:'123',
'abc':345,
'c':456
};
console.log('obj',obj);---obj Object {abc: 345, c: 456,Symbol(abc): "123"}
//利用for of遍历是取不到symbol()声明的值,只能取到对象里面的值
for(let [key,value] of Object.entries(obj)){
console.log('letof',key,value);--let of abc 345 , let of c 456
}
//只能得到Symbol()声明的值,使用Object.getOwnPropertySymbols(obj),得到一个数组
Object.getOwnPropertySymbols(obj).forEach(function(item){
console.log(obj[item]);---123
});
//利用Reflect.ownKeys(obj)可以取到obj中所有的值,返回值是一个数组
Reflect.ownKeys(obj).forEach(function(item){
console.log('ownKeys',item, obj[item]);
})
返回值:
ownKeys abc 345
ownKeys c 456
ownKeys Symbol(abc)123
10 ES6之Set-map
Set的用法(与数组相似,但是里面的元素是不能重复的)
//声明 Set()--声明的时候未添加默认值
let list= new Set();
//add()给Set()添加元素
list.add(5);
list.add(7);
list.add(7);---不能添加重复的元素,不会生效,但是不会报错
//size计算Set()的大小
console.log('size',list.size);-- 2
//声明 Set()--声明的时候添加默认值
let arr=[1,2,3,4,5];
let list= new Set(arr);
console.log('size',list.size);--5
Set可以用用于去重
let arr=[1,2,3,4,5,2,4];
let list = new Set(arr);
console.log('unique',list);--unique Set(5) {1, 2, 3, 4, 5}
-
add():添加元素
-
delete():删除特定元素
-
clear() :删除全部元素,即清空
-
has():是否包含某个元素
Set()的遍历
let arr=['add','delete','clear','has'];
let list=new Set(arr);
(1)for(let key of list.keys()){
console.log('keys',key);--add delete clear has
}
(2)for(let value of list.values()){
console.log('values',value);--add delete clear has
}
(3)for(let [key,value] oflist.entries()){
console.log('entries',key,value);--add add delete delete clear clearhas has
}
(4)list.forEach(function(item){
console.log(item);--add delete clear has
})
WeakSet的用法
--不能进行遍历
//WeakSet与Set支持的数据类型不一样(他的元素只能是对象)
//WeakSet的引用只能是弱引用
let weakList=newWeakSet();
let arg={};--只能是对象
weakList.add(arg);
console.log('weakList',weakList);
-
add():添加元素
-
delete():删除特定元素
-
clear() :删除全部元素,即清空
-
has():是否包含某个元素
Map的用法
(与对象object相似,都是键/值对,但object里面的key是字符串,但Map里面的key可以是任意的数据类型)
-
第一种定义方式:
let map=newMap();
let arr=['123'];
map.set(arr,456);---添加值,key,value
console.log('map',map,map.get(arr));--get()获取值map Map(1) {["123"] => 456}456
-
第二种定义方式:
let map=new Map([['a',123],['b',456]]);
console.log('mapargs',map);--map args Map(2) {"a"=> 123, "b" => 456}
Map对象的属性值:
-
Size:获取大小
-
Get()获取元素
-
delete():删除特定元素
-
clear() :删除全部元素,即清空
遍历于Set一样
WeakMap的用法--也不能遍历,key必须是对象
let weakmap=newWeakMap();
11 ES6之Proxy和Reflect
Proxy和Reflect:
Proxy和Reflect的概念---方法一样
Proxy:代理,扮演代理的作用---连接了用户和最真实的对象中间的一个层
Reflect:反射的作用---反射object
Proxy和Reflect的适用场景
Proxy的用法:
letobj={
time:'2017-7-21',
name:'net',
_r:123
};
letmonitor=newProxy(obj,{
//拦截对象属性的读取--读get()
get(target,key){//target代表obj对象
returntarget[key].replace('2017','2018');
},
//拦截对象,设置属性--写set()
set(target,key,value){
if(key==='name'){
returntarget[key]=value;
}else{
returntarget[key];
}
},
//拦截keyinobject操作
has(target,key){
if(key==='name'){
returntarget[key];
}else{
return false;
}
},
//拦截不需要的属性--拦截delete
deleteProperty(target,key){
if(key.indexOf('_')>-1){//检索到下划线,不需要
deletetarget[key];
return true;
}else{
returntarget[key];
}
},
//拦截object.keys,object.getOwnPropertySymbols,object.getOwnPropertyNames
ownKeys(target){
returnObject.keys(target).filter(item=>item!='time');
}
});
console.log('get',monitor.time);
monitor.time='2018-7-22';//在set()没有定义time,不能改变time内容
monitor.name='network';//在set()里面设置了name,可以改变内容
console.log('set',monitor.time,monitor.name,monitor);
console.log('has','name'inmonitor,'time'inmonitor);
deletemonitor.time;//time是符合规范的,不会删除掉
deletemonitor._r;//_r不符合规范,会被删掉
console.log('deleteProperty',monitor);
console.log('ownKeys',Object.keys(monitor));
reflect的用法:---与Proxy的用法一致,只是表示方法不同
letobj={
time:'2017-7-21',
name:'net',
_r:123
};
console.log(Reflect.get(obj,'time'));
Reflect.set(obj,'name','network');
console.info(obj);
console.log(Reflect.has(obj,'name'));
实际例子:
//用户代理
functionvalidator(target,validator){//target是对Person的代理
return newProxy(target,{
_validator:validator,
set(target,key,value,proxy){
if(target.hasOwnProperty(key)){
letva=this._validator[key];
if(!!va(value)){
returnReflect.set(target,key,value,proxy);
}else{
throwError(`不能设置${key}到${value}`)
}
}else{
throwError(`${key}不存在`);
}
}
})
}
//限制属性的条件,与业务分离validator
constpersonValidators={
name(val){
return typeofval==='string';
},
age(val){
return typeofval==='number'&&val>18
}
};
classPerson{
constructor(name,age){
this.name=name;
this.age=age;
returnvalidator(this,personValidators);
}
}
constperson=newPerson('KEKE',20);
console.log(person);
person.name=48;//报错不能设置name到48
12 ES6之类和对象
类的概念
-
基本语法
//类的基本定义和生成实例
//类的定义
classParent{
constructor(name='keke'){
this.name=name;
}
}
//生成实例
letv_parent1=newParent();//默认name:keke
letv_parent2=newParent('nono');//name:nono
-
类的继承
//继承
classParent{
constructor(name='keke'){
this.name=name;
}
}
classChildextendsParent{
}
letchild=newChild();//name:keke
//继承传递参数
classParent{
constructor(name='keke'){
this.name=name;
}
}
classChildextendsParent{
constructor(name='child'){
super(name);//super(参数):参数为空,则默认使用父类的值;参数不为空,则传递自己的参数
this.type='child';
}
}
letchild=newChild();//name:child---存在super(name)
letchild=newChild();//name:keke---不存在super(name)
-
静态方法
-
--通过类去调用,而不是通过类的实例去调用
//静态方法
classParent{
constructor(name='keke'){
this.name=name;
}
//静态方法的定义
statictell(){
console.log('tell');
}
}
Parent.tell();//tell
-
静态属性
//静态属性
classParent{
constructor(name='keke'){
this.name=name;
}
statictell(){
console.log('tell');
}
}
Parent.type='text';//设置静态属性
console.log(Parent.type);//text
-
Getter和setter属性
//getter,setter
classParent{
constructor(name='keke'){
this.name=name;
}
getlongName(){
return'mk'+this.name;
}
setlongName(value){
this.name=value;
}
}
letv=newParent();
console.log(v.longName);//mkkeke
v.longName='hello';//赋值相当于setter
console.log(v.longName);//mkhello
13 ES6之Promise
(1)Promise是异步操作的一种解决方案
(2)什么是异步?
先执行什么,后执行什么
(3)Promise的作用
(4)Promise的基本用法
//基本定义的调用
letajax=function(callback){
console.log('执行');
setTimeout(function(){
callback&&callback.call();
},1000);
};
ajax(function(){
console.log('timeout1');
})
//Promise的调用--一次调用
letajax=function(){
console.log('执行');
Return newPromise(function(resolve,reject){
setTimeout(function(){
resolve();//
},1000);
})
};
ajax().then(function(){
console.log('promise','timeout2');
})
//Promise的调用--连续调用
letajax=function(){
console.log('执行3-1');
Return newPromise(function(resolve,reject){
setTimeout(function(){
resolve();
},1000);
})
};
ajax()
.then(function(){
return newPromise(function(resolve,reject){
console.log('执行3-2');
setTimeout(function(){
resolve();
},2000);
})
})
.then(function(){
console.log('promise','执行3-3');
})
//捕获异常错误
letajax=function(num){
console.log('执行4');
return newPromise(function(resolve,reject){
if(num>5){
resolve();
}else{
throw newError('出错了');
}
})
};
ajax(6).then(function(){
console.log('log',6);
}).catch(function(err){
console.log('catch',err);
});
ajax(3).then(function(){
console.log('log',3);
}).catch(function(err){
console.log('catch',err);//捕获到错误
})
//所有图片加载完再加载页面
//加载图片
functionloadImg(src){
return newPromise((resolve,reject)=>{
letimg=document.createElement('img');
img.src=src;
//图片加载成功
img.οnlοad=function(){
resolve();
};
//图片加载失败
img.οnerrοr=function(){
reject(err);
}
})
}
//将图片加载到页面上
functionshowImgs(imgs){
imgs.forEach(function(img){
document.body.appendChild(img);
})
}
Promise.all([
loadImg('图片地址'),
loadImg('图片地址'),
loadImg('图片地址')
]).then(showImgs)
Promise.all():只有在all()里面的内容发生改变的时候且全部加载完成,就会触发Promise.all()这个实例
Promise.race():只要race()里面的内容有一个加载完成,就可以触发Promise.race()实例
//只要有一张图片加载完就加载页面
//加载图片
functionloadImg(src){
return newPromise((resolve,reject)=>{
letimg=document.createElement('img');
img.src=src;
//图片加载成功
img.οnlοad=function(){
resolve();
};
//图片加载失败
img.οnerrοr=function(){
reject(err);
}
})
}
functionshowImgs(imgs){
imgs.forEach(function(img){
letp=document.createElement('p');
p.appendChild(img);
document.body.appendChild(p);
})
}
Promise.race([
loadImg('图片地址'),
loadImg('图片地址'),
loadImg('图片地址')
]).then(showImgs);
14 ES6之Iterator接口和for…of循环
(1)什么是Iterator接口
(2)Iterator的基本用法
letarr=['hello','world'];
letmap=arr[Symbol.iterator]();
console.log(map.next());
console.log(map.next());
console.log(map.next());
(2)部署接口:有颜色字体标志的是必须要有的(重要)
letobj={
start:[1,3,2],
end:[7,9,8],
[Symbol.iterator](){
letself=this;
letindex=0;
letarr=self.start.concat(self.end);
letlen=arr.length;
return{
next(){
if(index<len){
return{
value:arr[index++],
done:false
}
}else{
return{
value:arr[index++],
done:true
}
}
}
}
}
};
for(letkeyofobj){
console.log(key);//1 3 2 7 9 8
}
for… of
letarr=['hello','world'];
for(letvalueofarr){
console.log(value);//hello world
}
15 ES6之Generator
(1)基本概念
异步编程的一种解决方案
(a)genertor的基本定义
lettell=function*(){
yield'a';
yield'b';
return'c';
};
letk=tell();
console.log(k.next());
console.log(k.next());
console.log(k.next());
console.log(k.next());
(2)generator与Iterator接口的关系
letobj={};
//创建一个接口
obj[Symbol.iterator]=function*(){
yield1;
yield2;
yield3;
};
for(letvalueofobj){
console.log(value);
}
(3)状态机
letstate=function*(){
while(1){
yield'a';
yield'b';
yield'c';
}
};
letstates=state();
console.log(states.next());//a
console.log(states.next());//b
console.log(states.next());//c
console.log(states.next());//a
console.log(states.next());//b
(4)语法糖:
- async ---实现方式与上面一样
letstate= async function(){
while(1){
await'a';
await'b';
await'c';
}
};
letstates=state();
console.log(states.next());//a
console.log(states.next());//b
console.log(states.next());//c
console.log(states.next());//a
console.log(states.next());//b
//抽奖
letdraw=function(count){
//具体抽奖逻辑
console.info(`剩余${count}次`)
};
letresidue=function*(count){
while(count>0){
count--;
yielddraw(count);
}
};
letstar=residue(5);
letbtn=document.createElement('button');
btn.id='start';
btn.textContent='抽奖';
document.body.appendChild(btn);
document.getElementById('start').addEventListener('click',function(){
star.next();
},false);
(5)长轮询:
客户端向服务器发送Ajax请求,服务器接到请求后hold住连接,直到有新消息才返回响应信息并关闭连接,客户端处理完响应信息后再向服务器发送新的请求。
letajax=function*(){
yield newPromise(function(resolve,reject){
setTimeout(function(){
resolve({code:0});
},200);
})
};
letpull=function(){
letgenerator=ajax();
letstep=generator.next();
step.value.then(function(d){
if(d.code!=0){//长轮询的查询
setTimeout(function(){
console.log('wait');
pull();
},1000);
}else{
console.log(d);
}
})
};
pull();
(6)next函数的用法
(7)Yield*的语法
16 ES6之Decorator--修饰器
(1)基本概念
是一个函数,修改行为,修改类的行为,只在类的范畴内有用
(2)基本用法
//定义修饰器
第一种用法:在类的里面使用
letreadonly=function(target,name,descriptor){
descriptor.writable=false;
returndescriptor;
};
classTest{
@readonly//引入修饰器,修改time()的行为
time(){
return'2017-07-21';
}
}
lettest=newTest();
test.time=function(){//修饰器设置的只读属性,不能进行修改会报错
console.log('resettime')
};
console.log(test.time());
第二种用法:在类的外面使用
lettypename=function(target,name,descriptor){
target.myname='hello';
};
@typename
classTest{
}
console.log(Test.myname);
扩展:
第三方库修饰器的js库:core-decorators; npm install core-decorators
就不用自己手动的写readonly,typename等,只需要import引入就可以使用
//埋点,做日志统计
letlog=(type)=>{
return function(target,name,descriptor){
letsrc_method=descriptor.value;
descriptor.value=(...arg)=>{
src_method.apply(target,arg);
console.info(`log${type}`);//实现埋点,真实的业务中直接换成一个接口就可以了
}
}
};
classAD{
@log('show')
show(){
console.log('adisshow');
};
@log('click')
click(){
console.log('adisclick');
}
}
letad=newAD();
ad.show();
ad.click();
17 ES6之模块化
(1)基本概念
(2)Es6的模块化语法
-
模块的引入import
-
模块的导出export
//导出变量,函数,类
第一种写法:--对应第(1)(2)种导出方法
export letA=123;
export functionTest(){
console.log('Test');
}
export classHello{
test(){
console.log('test');
}
}
第二种写法:--对应第(3)种导出方法----推荐使用
letA=123;
letTest=function(){
console.log('Test');
};
classHello{
test(){
console.log('test');
}
}
export default{
A,
Test,
Hello
}
//导入
(1)import{A,Test,Hello}from'./class/lesson17';//导入的内容逐个写,当内容很多的时候,不适用
(2)import*aslessonfrom'./class/lesson17';//全部一起导入,用的时候再针对性的取
console.log(lesson.A);
(3)importlessonfrom'./class/lesson17';
console.log(lesson.A);