ES6变量和的定义方式
let变量的定义
在以前ES5里面,我们使用var定义关键字有以下几个特点
1
.var定义的变量没有区域性,它必须定义在方法里面才有区域性
2
.var定义的变量会有一个建立阶段,在定义之前是可以使用这个变量的,只是没有值,值是undefined
let变量
let定义的变量没有建立阶段同时let定义的变量会有先天的自带闭包特性
let定义虽然没有建立阶段,但是在之前会有一个扫描特性
let的暂时性死区
下面的代码中方法sayHello里面的变量a见不到外边的值,也获取不到里面的值,这个阶段,我们就称之为暂时性的死区
let a = 123;
function sayHello(){
console.log(a); //暂时性的死区
let a = "hello world"
}
sayHello();
[^1]总结let没有声明提前,也就是没有建立阶段,let变量自带区域性,同一个区域里面定义的let变量,变量名不能重复
const常量的定义
常量是与变量相对应的过程,变量是可以变化的量,常量则是不能变化的量
const具备let所有的特点,只是他不能改变而已
[^1]const a;
上面的代码没有任何意义,常量a在不进行初始化的时候,他就是undefined后期不能更改他的值
const锁栈不锁堆
对象的解构取值
let obj = {
userName: "张三",
age: 18,
sex: "女",
hobby: ["看书", "睡觉", "玩游戏"]
}
let { userName, age } = obj;
console.log(userName, age);
*解构失败以后得到的值就是一个undefined*
var obj = {
userName: "张三",
teacherInfo: {
teacherName: "标哥哥"
},
stuList: [
{
stuName: "小夫",
age: 20
}
]
}
上面的代码解构老师的姓名tname,与第一个学生的姓名stuname
let { teacherInfo: { teacherName }, stuList: [{ stuName }] } = obj;
console.log(teacherName,stuName);
展开运算符
展开运算的写法 …
var obj = {
userName:"张三",
age:18
}
let obj1 = {...obj,sex:"女"}; //把obj这个对象展开,然后再解构赋值
obj1.userName = "李四";
解构与展开运算符结合
let arr1 = ["a", "b", "c", "d"];
let arr2 = [1, 2, 3, 4, 5];
let newArr2 = [...arr1, ...arr2];
字符串拓展
模板字符串不在使用单双引号,转而去使用反引号 ``
let x = 10;
let y = 5;
function abc(s, v1, v2) {
console.log(s[0]); //hello
console.log(s[1]); //world
console.log(s[2]); //""
console.log(v1); //15 x+y
console.log(v2); //50 x*y
}
abc`hello${x + y}world${x * y}`;
上面的代码就相当于下面的代码
abc(["hello", "world", ""], x + y, x * y);
在以前的ES5里面,我们有很多字符串的方法,其实在ES6里面,也有新的方法产生
1.
padStart,padEnd 补元素 第一个参数是用什么补,第二个参数是补多少个
2.
trimStart,trimEnd 去掉空格
3.
repeat()方法返回一个新字符串,表示将原字符串重复n次。如
'x'.repeat(3) // "xxx"
'hello'.repeat(2) // "hellohello"
'na'.repeat(0) // ""
4.
includes方法,是否包含
let site = ['runoob', 'google', 'taobao'];
site.includes('runoob');
// true
site.includes('baidu');
// false
数组的扩展
1.
Array.of() 使用Array.of跟[]定义效果一样
2.
Array.from() 把类数组转换变成数组 ,之前使用的是Array.prototype.slice.call
, jQuery里面使用的是$.toArray()
或者$.makeArray()
来进行
3.
Array.prototype.fill() 填充数组
`javascript
let arr=new Array(100);
arr.fill(0,2,5);
fill第一个参数用指定的元素去填充数组,第二个参数起始位置,第三个参数结束位置不包含
4.
Array.prototype.flat() 拍平当前数组,后面接参数代表拍平几层,默认拍平一层
5.
==Array.prototype.find()==用于数组中查找对象,返回的是一个对象
let arr = [
{ sid: "H20010001", sname: "张三" }
]
let obj = arr.find(function(item,index,_arr){
return item.sid=="H20010003";
})
6.
Array.prototype.findIndex() 用于在数组中查找指定索引
数组的迭代
1.
forEach
2.
map
3.
filter
4.
some
5.
every
6.
for语句
7.
for…in
上面的7个点都可以将数组遍历出来,接下来,我们学习一个新的遍历点(迭代iterable)
现在在上面的代码当中,我们学到了一新的迭代方法,叫for...of
,为什么可以使用for...of
?是不是所有的东西都可以使用for...of
?它与for...in
又 有什么区别呢
for...in
是用于遍历有序集合,for...of
不仅可以遍历有序集合,还可以遍历无序集合
只要能够使用展开运算符…的,就可以使用for... of
默认的五种
1.NodeList
Array
Set
Map
arguments
是因为他们内部都有迭代器interable
Set单值集合
set是一个内部元素不重复的单值集合,它是一个无序集合
可以利用Set来实现快速去重
1.
内部元素不重复
2.
无序集合
let s1 = new Set();
s1.add("张三") //添加
add()
方法,向集合中添加一个元素delete()
方法,删除一个元素clear()
方法,清空这个集合has()
方法,判断集中是否有这个元素,存在则返回true
,不存在则返回false
size
属性,获取当前集合里面的元素个数
Set对象的遍历
在这里就要注意了,Set是一个单值集合,它没有索引,所以是不能够通过
for...in
来进行遍历。因为for...in
遍历出来的是索引(属性名)
这个时候,我们就可以使用for...of
,这种遍历方式,不依赖于索引,它直接就把里面的值拿出来了,而Set对象是一个单值集合中,它里面是有值的
Map键值对集合
Map比Set多了一个Key,它是键值对集合,也不是单值集合了,但是Map内部的键是不允许重复的,值可以重复
Map的创建
let m1 = new Map();
m1.set("userName", "标哥哥");
m1.set("age", 19);
在上面的代码里面,
m1.set
就是向map对象当中添加新的元素,它是成对的添加的,第一个参数代表key键,第二个参数代表value值,所以在上面的代码当中它age
所表示的结果就是20
Map对象的相关方法
set()
方法,向对象里面添加键值对get()
方法,从对象当中根据一个key来取值,如果取不到则返回undefined
delete()
方法,根据一个key从这个集合中删除一个键值对clear()
方法,清空这个集合has
方法,判断对象中是否有这个键keys()
方法 ,返回当前对象的键的迭代器(Iterator),这个迭代器可以用于for…of遍历values()
方法,返回当前对象值的迭代器(Iterator),这个迭代器可以用于for…of遍历size
属性,当前集合对象里面的元素个数
Map对象的遍历
依然不能用for…in遍历,用的是for…of
遍历的结果是键值,成双成对的
单独遍历key跟value
let m1_keys = m1.keys(); //它返回的也是一个迭代器,也可以使用for...of去完成
for(let k of m1_keys){
console.log(k);
}
迭代器接口
迭代器接口叫Iterable,它是一个规范,满足了这个规范就可以用for…of遍历或者用…展开运算符、
下面5个对象默认实现了接口
1.NodeList
2.Array
3.Set
4.Map
5.arguments
函数的扩展
无构造函数的函数
在对象里面定义的方法它是不能通过new去取的
sayHello() {
//通过这种方式定义的方法,它是没有构造函数的
console.log("我是对象2里面的方法");
}
函数参数的默认值
function def(a = 1, b = 2) {
console.log(a,b);
}
一般函数的默认值写在最后
箭头函数
let e = (x, y) => x + y; 这是函数有返回值的,没有返回值是不允许这样写的
let d = (x, y) => {
console.log(x, y);
}
箭头函数与普通函数的区别
- 关于this指向的问题
箭头函数会绑定this
【箭头函数会跳过当前的环境的this,取上级环境的this】箭头函数最主要的使用声明就是在回调函数里面,因为它可以绑定外部的this,正是因为有一个像这样的特点,箭头函数就不能作为对象里面的方法 - 箭头函数里面没有
arguments
const add = (x, y) => {
console.log(arguments);
}
add(11,20);
这个时候的arguments
在这里它不是指实集合,它拿不到11
与20
这两个值 - 箭头函数是不能当成构造函数去执行的
const abc = (userName, sex) => {
console.log(userName, sex);
}
let obj = new abc(“张三”, “男”); // 不能当成构造函数去执行
- 箭头函数也是有作用域的,这一点与普通函数一样
函数柯里化
函数柯里化是函数的另一种写法,它将函数的执行步骤分成了多个部分来进行
function add(a, b, c) {
return a + b + c;
}
let result1 = add(11,12,13);
console.log(result1)
上面的函数就是一个普通的函数,我们将这个函数柯里化一下
function add1(a) {
return function (b) {
return function (c) {
return a + b + c;
}
}
}
let result2 = add1(11)(12)(13);
console.log(result2);
class关键字创建对象
class Person {
//使用class的时候一定要注意,如果有参数要接收,要在constructor里面接收,它是真正的构造函数
//如果我自己创建了constructor ,则系统就不再给我创建了
constructor(userName, sex, age) {
this.userName = userName;
this.sex = sex;
this.age = age;
}
}
let p1 = new Person("张珊", "女", 19); //调用了Person里面的constructor
class
里面的constructor
叫构造器,如果我要接收外部的参数,我就手动的写一个,如果不需要接收外部的参数,可以不用写,系统会自动帮我们创建一个无参的constructor
同时要注意constructor
这个方法是在new
的一瞬间自动调用的
当一个对象当中既有属性,又有方法的时候,应该怎么办呢?
class Person {
constructor(userName, sex, age) {
this.userName = userName;
this.sex = sex;
this.age = age;
}
sayHello() {
console.log(`大家好,我叫${this.userName},我的性别是${this.sex},我的年龄是${this.age}`);
}
}
let p1 = new Person("杨标","男",19);
p1.sayHello();
静态方法
在ES6的class创建的构造函数里面,它有一个新的关键字叫
static
,通过这个关键字来修饰的方法是不需要new
和对象就可以直接调用了
class Person {
constructor(userName) {
this.userName = userName;
}
sayHello() {
console.log("我是sayHello方法")
}
//静态方法
static abc(){
console.log("我是方法abc");
}
}
let p1 = new Person("张三");
p1.sayHello(); //sayHello是一个普通的方法,所以必须通过new出来的对象来调用
Person.abc(); //abc是一个通过static修饰的静态方法,可以直接使用Person来调用,无需new
访问器方法
get 取值
set 赋值
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
//访问器方法 取值的时候自动调用
get userName() {
console.log("我是取值的时候调用");
return this.firstName + this.lastName;
}
set userName(v) {
//v就是你赋的值
console.log("我是赋值的时候调用");
this.firstName = v[0];
this.lastName = v.substr(1);
}
}
在上面的对象里面,我们分别在userName的这个方法的前面添加了
get
与set
,然后我们测试一下上面的代码
let p1 = new Person("张", "三丰"); p1.userName = "周玮夫"; //赋值操作 console.log(p1.userName); //取值操作
继承
丢弃apply跟call使用extends
//新建一个Student,让它继承Person
class Student extends Person {
constructor(userName, sex, age, sid) {
super(userName,sex,age); //指的是父级的constructor
this.sid = sid;
}
}
在上面的代码当中,你会看到一个
super
的关键字,它有一个注意事项,它必须放在最前面。在使用this
关键字之前,一定要先有super
(因为必须是先有父级,再有子级)
在无参数的继承的时候,其实我们是可以省略掉构造器constructor
,但是如果你手动写的了constructor
,那么,你必须主动的调用super
方法的重写
class Person {
sayHello() {
console.log("我是Person里面的sayHello")
}
}
class Student extends Person {
sayHello() {
console.log("我是Student的sayHello");
}
abc(){
this.sayHello();
}
def(){
//调父级的sayHello
// this.__proto__.sayHello.call(this); 以前ES5里面的写法
super.sayHello(); //现在新的写法
}
}
let s1 = new Student();
s1.sayHello(); //调用的是自身的sayHello
s1.abc(); //内部调用的还是自身的Student的sayHello
s1.def();
代码分析:上面的代码当中,父级对象
Person
里面它有一个sayHello
,子级对象Student
里面也有一个sayHello
,这样当使用s1
在调用sayHello
的时候默认调用的是自身的方法,但是我们仍然可以使用super
关键字主动的调用父级里面的方法
class与extends的特点
1.class
与extends
来实现的对象与继承关系它们有个显著的特点,把所有的属性都放在了子级对象 ,把方法放在了父级对象
2.class
与extends
实现的对象的创建与继承它们没有破坏instanceof
的结构
3
.通过class
关键字所创建的东西本质上面还是一个function,这些方法的内部都有一个new.target
的判断,这个判断让当前的function只能够new
来调用,这就说明它只是一个构造函数
function aaa(){
if(new.target===undefined){
throw new Error("Class constructor GirlStudent cannot be invoked without 'new'");
}
}
上面的`aaa`的方法就只能是一个构造函数通过`new`来调用,你如果通过其它的方式来调用,这个时候就会报错
正则的扩展
分组捕获
提取字符串match 跟 exec 方法
exec 首先捕获大的组,在进行分组
分组不捕获
?:
分组号
let reg = /(\w)\1+/g;
let str = "大家好abcaaabcccdee";
let result = null;
while ((result = reg.exec(str)) != null) {
console.log(result);
}
上面是通过
exec
来进行提取使用分组号提取了重复的字符
当然也可以用match
来提取
let result = str.match(reg);
console.log(result); //[ 'aaa', 'ccc', 'eeee' ]
前瞻A(?=B)
let str1 = "abc123";
let reg = /abc(?=123)/g;
reg.test(str1); //true
负前瞻A(?!B)
let reg =/abc(?!123)/g;
reg.test(str1); //false
后顾(?<=B)A
let str3 = "123abc";
let reg = /(?<=123)abc/g;
reg.test(str3); //true
负后顾(?<!B)A
let reg = /(?<!123)abc/g;
reg.test(str3); //false
指数运算
let a1 = Math.pow(2,3); ES5写法
let a2 = 2 ** 3; ES6写法
大整型数据类型BigInt
let a = 2n ** 513n;
let b = 2n ** 513n + 1n;
console.log(typeof a, typeof b); //bigint类型
console.log(a == b); //false
反射Reflect
反射就是操作对象的另一种方式,它是以第三方的角度去操作对象
Reflect.set(obj,"userName","李四");
let a = Reflect.get(obj,"userName");
反射在调用方法的时候是容易出问题的
let b = Reflect.get(obj,"sayHello");
b(); //这个时候的this就出问题了,因为它不再指向obj这个对象的,所以反射在调用方法的时候有其它的写法
反射如果要调用方法,应该采取如下的方式来进行
let b = Reflect.get(obj, "sayHello"); //这一步是获取sayHello的方法名
Reflect.apply(b, obj, []); //这就是反射调用方法
//上面的代码等价于
obj.sayHello.apply(obj,[]);
反射也可以执行构造函数
//ES5的代码
function Person(userName) {
this.userName = userName;
}
let p1 = new Person("张三");
let p2 = Reflect.construct(Person,["李四"]);
console.log(p1,p2);
//ES6的构造函数
class Student { //class相当于new
constructor(stuName, stuSex) {
this.stuName = stuName;
this.stuSex = stuSex;
}
}
let s1 = new Student("王五", "男");
let s2 = Reflect.construct(Student, ["赵六", "女"]);
console.log(s1, s2);
不要把
Object.keys()
与Reflect.ownKeys()
混淆了,它们是不一样的
Reflect.get
获取对象的属性值Reflect.set
设置对象的属性值Reflect.apply
反射调用方法Reflect.construct
执行某一个构造函数Reflect.defineProperty
通过反射定义特殊属性Reflect.deleteProperty
通过反射删除对象的某一个属性Reflect.has
通过反射判断一个属性是否存Reflect.ownKeys
通过反射来获取对象的属性名Reflect.getOwnPropertyDescriptor
通过反射获取某一个对象的某一个属性的描述信息Reflect.isExtensible
通过反射判断某一个对象是否是一个“不可扩展对象”Reflect.preventExtensions
通过反射设置一个对象为“不可扩展对象”Reflect.getPrototypeOf
获取一个对象的prototype
Reflect.setPrototypeOf
设置一个对象的prototype
代理Proxy
我们可以把代理认为是一种全方位的get
与set
访问控制
let obj = function () {
let temp = {
userName: "胡歌",
sex: "男",
age: 34
}
let p1 = new Proxy(temp, {
get(target, properName, receiver) {
return Reflect.get(target,properName,receiver);
},
set(target, properName, value, receiver) {
}
});
return p1;
}();
分析:p1就是代理对象,obj就是实实在在的对象,p1代理了obj的所有操作。其中get负责取值,set负责赋值操作
在get方法里面,有三个参数,分别如下
1.target
代理的目标对象,这里指obj
2.properName
代表当前操作的属性,上面的代码中指userName
3.receiver
代表理(经济人),这里指p1
Symbol数据类型
这是ES当中的新数据类型[JavaScript当中的第7种数据类型],它是全局唯一标识,不重复。与其它语言中的GUID/UUID
非常类似
let c = Symbol("标哥创建的"); Symbol创建出来的变量是独一无二的
生成器函数
生成器函数在调用以后它不会立即执行,只是这个方法被激活了,处理暂停状态,然后会返回一个迭代器
通过next方法来执行
unction* abc() {
let arr = ["张三", "李四", "王五", "赵六", "小夫"];
//我现在想把里面的值一个一个,依次返回出去
yield arr[0]; //停止来,把刚刚生产出来的值arr[0]返回到外边去
yield arr[1];
}
x.next();
只要有了Iterable就可以实现for...of
for(let item of x){
console.log(item);
}
迭代器接口
接口:interface
,接口可以理解为规范,当一个对象实现某一个接口就遵守这个接口的规范(我们也称之为具备某一种能力)
Iterable
这个接口它主要定义的规范就是可以让元素使用for...of
去遍历(能够通过for…of遍历的,就一定可以使用…的展开运算符)
在JavaScritp里面,以下数据对象默认实现了Iterable
接口
1. NodeList
2. Array
3. Set
4. Map
5. arguments
所有的迭代器默认都会有一个方法叫
next()
,这个方法让程序继续向后运行,直到下一个yield
再暂停,我们可以让一个对象手动去实现迭代器的接口,这样这个对象就可以被for...of
let obj = {
userName: "张三",
age: 18,
sex: "女",
address: "湖北省武汉市",
[Symbol.iterator]: function* () {
// 获取到自己所有的属性
yield obj.userName;
yield obj.age;
yield obj.sex;
yield obj.address;
}
}
//obj对象实现了Symbol.iterator这个方法,就说明它实现了迭代器的接口
for(let item of obj){
console.log(item);
}
ES6的异步解决方案
在之前的ES5里面,我们解决异步编程的方法就是回调
在ES6里面,为了更好的解决异步编程,它使用了一个新的对象叫Promise
。在这个对象的内部它有三个状态
pending
等待状态resolve
成功状态
3.reject
失败状态
默认情况下,它是等待状态
上面的代码我们可以通过ES7
里面的await
与async
来进一步的简化,这是典型的异步转同步的概念
function abc() {
//abc现在就是一个异步的方法,它不可能立刻就外边结果
//默认情况下的Promise是等待状态
let p = new Promise((resolve, reject) => {
setTimeout(function () {
let temp = parseInt(Math.random() * 100);
if (temp > 50) {
//成功
let obj = {
value: temp, msg: "成功"
}
resolve(obj); //等待状态变成了成功状态
}
else if (temp <= 50) {
//失败
let obj = {
value: temp, msg: "失败"
}
reject(obj); //等待状态变成了失败状态
}
}, 3000);
});
return p;
}
async function ddd() {
try {
//let result = abc(); //返回的是一个Promise
let result = await abc(); //这个时候返回的就不再是一个Promise了,而是这个承诺的【结果】。这结果可能成功,可能失败
***//await等待的只能是成功的结果,永远都是resolve***
console.log(result);
} catch (error) {
***//失败就在这里,error就是失败的obj ,这里执行的一定是reject的结果***
console.log("你等到的是失败的结果");
console.log(error);
}
}
ddd();
Promise
一定是有三个状态的
2. 成功的状态是resolve
,失败的状态是reject
3.await
必须与async
同时使用
4.await
等待的只能是Promise
,不能是其它东西,必须只能等到resove
的结果,失败的结果在catch
里面
5. 一定要熟悉Promise
的写法