js3_es6基础
一、类和对象
1.1 概念
es6中新增加了类的概念,可以使用class关键字声明一个类
1.2 创建类
- 实例 类似于Java
class Star {
constructor(name) {
this.uname = name;
}
song() {
console.log(this.uname);
}
}
var zt = new Star('zt');
console.log(zt.uname);
-
constructor()构造器(默认方法)
- 用于传递参数,返回实例对象,通过new命令生成对象实例时,自动调用该方法。如果没有显示定义,类内部会自动创建一个constructor()
- constructor()构造器不需要声明function
- 由于constructor也是一个函数,因此具有arguments内置属性
-
constructor属性
-
对象间拥有一个共享的属性constructor,该属性存储当前对象的构造函数
-
实例
class Star { constructor(name) { this.uname = name; } } var zt = new Star('zt'); console.log(zt.constructor);// 返回class Star { // constructor(name) { // this.uname = name; // } // } console.log(zt.constructor == Star); // 返回true
-
-
注意
- 类方法之间不需要加逗号分隔
- 类方法不需要用function进行声明
1.3 创建对象
es6之前,js并没有引入类的概念,而目前浏览器的js是es5版本的,大多数高版本的浏览器也支持es6,不过只实现了es6部分特性和功能,并且在es6之前,对象不是基于类创建的,而是用构建函数来定义对象。
-
通过字面量方式创建
var obj = { this.uname = 'zt', this.song = function() { } }
-
通过内置对象Object创建
var obj = new Object(); obj.uname = 'zt'; obj.song = function() {};
-
通过with程序块新建属性以及方法
var obj = new Object(); with(obj) { uname = 'zt'; song = function() {}; console.log('zt'); }
-
-
通过class创建
class Star { constructor(uname) { this.uname = uname; } song() {} }; var zt = new Star('zt');
-
通过构造函数创建对象
-
构造函数是一个特殊的函数,主要用来初始化对象,即为对象变量赋初始值,总是与new一起使用。
-
new在执行时会做的事件
- 在内存中创建一个新的空对象
- 让this指向这个新的对象
- 执行构造函数里面的代码,给这个新对象添加属性和方法
- 返回这个新对象,所以构造函数里面不需要return
-
语法:
function Star(uname, age) { this.uname = uname; this.age = age; this.song = function () { } } var zt = new Star('zt',18);
-
成员:构造函数中的属性和方法称为成员,成员可以添加
- 实例成员
- 构造函数内部通过this添加的成员
- 只能通过实例化对象来访问
- 静态成员
- 在构造函数本身上添加的成员
- 只能通过构造函数来访问
- 实例成员
-
构造函数使用上存在的问题
-
存在浪费内存的问题
-
实际情况:同一个构造函数创建两个实例,如果构造函数中有方法(复杂数据类型),则创建一个对象都会再开辟一个空间存储方法,因此两个实例对象的方法是存储在不同的空间中的,当实例对象数量上增加时,内存消耗也会同步增加。
-
为了所有对象使用同一个函数,且解决空间,解决方式是:使用构造函数原型:prototype
-
构造函数通过原型分配的函数是所有对象所共享的
-
js规定,每个构造函数都有一个prototype属性。而该属性指向的是对象,该对象的所有属性和方法,都会被构造函数所拥有。
-
对象如何使用prototype属性中的方法?
- 通过对象原型_proto_
- **对象都有一个属性_proto_**指向构造函数的prototype原型对象
- 因此方法的查找规则:先查看当前对象上是否有方法,如果没有则通过属性_proto_来对prototype方法中进行查找
-
注意:
- 把不变的方法,直接定义在prototype对象上
- 把属性直接定义在构造函数内部
- _proto_对象原型的意义在于为对象的查找机制提供一个方向,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象prototype
-
原型:一个对象,也称为prototype为原型对象
-
原型的作用:共享方法
-
constructor属性
-
对象原型_proto_和构造函数原型对象prototype对象里面都有一个属性constructor属性
-
constructor主要用于记录该对象引用哪个构造函数,可以让原型对象重新指向原来的构造函数
-
执行过程:
-
当给构造函数原型对象prototype赋值为一个对象时,
class Star() { } Star.prototype = { sing:function(){} } var zt = new Star(); console.log(zt._proto_.constructor); console.log(Star.prototype.constructor); // 此时以上的constructor不再指向构造函数本身
-
解决:手动利用constructor指回原来的构造函数
class Star() { } Star.prototype = { constructor:Star, sing:function(){} } var zt = new Star(); console.log(zt._proto_.constructor); console.log(Star.prototype.constructor); // 此时以上的constructor重新指向构造函数本身
-
-
-
-
-
构造函数、实例、原型对象三者之间的关系
-
构造函数通过属性prototype指向原型对象,原型对象prototype通过其属性constructor指向构造函数
-
构造函数通过new方法创建了对象实例,对象实例通过属性_proto_指向原型对象
-
关系图:
-
原型链
-
由于每个对象都有_proto_属性,因此当前原型对象的_proto_会指向Object的prototype对象,而Object的原型对象指向null,而这个_proto_属性的不断指向prototype对象的过程称为原型链
-
-
js成员查找机制
- 当访问一个对象的成员时,首先查找这个对象本身有没有该属性
- 如果没有就查找它的原型(_proto_指向的prototype原型对象)
- 如果还没有就查找原型对象的原型(Object的原型对象)
- 依次类推,一直找到Object为止(null)
-
例子:Object.prototype里面有toString方法,但是除非手动添加该方法,其他对象中没有该方法,但是其他对象仍然可以使用该方法:原型链存在的原因
-
-
-
1.4 类的继承
-
es6之前组合继承的方式:构造函数+原型对象模拟实现继承
-
call(thisArg,arg1,arg2,…)
-
参数说明
- thisArgs:当前调用函数this的指向对象
- args…:传递的其他参数
-
作用
- 调用函数
- 修改函数运行时的this指向
-
调用函数
function print_s() { console.log('s'); console.log(this);// 此时指向window对象 } print_s.call();
-
修改函数运行时的this指向
function print_s(x,y) { console.log('s'); console.log(this);// 此时指向example_o console.log(x + y); } var example_o = { uname: 'zt' } print_s.call(example_o,1,2);
-
-
通过构造函数实现继承父构造函数属性
function Father(uname,age) { // this 指向父构造函数的对象实例 this.uname = uname; // this(Son).uname = uname this.age = age; } function Son(uname,age,score) { // this 指向Son构造函数的对象实例 // 通过call 调用当前函数时,修改父类的指向,从而使得父类属性改为了子类属性 Father.call(this,uname,age); this.score = score; } var son = new Son('zt',18,100); console.log(son);
-
通过原型对象实现继承父构造函数方法
-
如果只是通过Son.prototype = Father.prototype方式进行赋值,虽然这样使得Son.prototype指向了父构造函数,但是由于对同一个对象的修改会导致同时被修改,因此当通过追加son.prototype中存储的方法的时候,父构造函数prototype也会相应的被修改
function Father(uname,age) { this.uname = uname; this.age = age; } function Son(uname,age,score) { Father.call(this,uname,age); this.score = score; } Father.prototype.test = function () {}; Son.prototype = Father.prototype; // 不可取
-
为了解决上述问题,通过原型链的方式来解决
function Father(uname,age) { this.uname = uname; this.age = age; } function Son(uname,age,score) { Father.call(this,uname,age); this.score = score; } Father.prototype.test = function () {}; Son.prototype = new Father();
由于实例对象与Father原型对象是互不相关的两个对象,因此当让son.prototype指向Father实例的时候,由于new Father()实例的_proto_指向的是Father中的原型对象,因此根据原型链的思路,如果在当前prototype对象中找不到改方法,会在Father原型对象中进行查找,另一方面,当Son.prototype追加方法的时候,不会影响父构造函数原型对象中的内容。但是还是存在一个问题,son.prototype.constructor指向的是Father构造函数,为了解决,手动添加:
function Father(uname,age) { this.uname = uname; this.age = age; } function Son(uname,age,score) { Father.call(this,uname,age); this.score = score; } Father.prototype.test = function () {}; Son.prototype = new Father(); son.prototype.constructor = Son;
-
-
-
es6_关键字extends
-
语法
class Father { sum() { console.log(1); } } class Son extends Father { } var son = new Son(); son.sum();
-
子类继承了父类的方法,即可以使用父类的方法
-
当父类方法中使用了父类自己的属性的时候,需要在子类中进行调用父类的构造器将属性传递过去,采用super关键字
- super关键字用于访问和调用对象父类上的函数。
- 可以调用父类的构造函数
- 可以调用父类的普通函数
class Father { constructor(x,y) { this.x = x; this.y = y; } sum() { console.log(this.x + this.y); } } class Son extends Father { constructor(x,y) { super(x,y);// 调用父类的构造函数 } sum() { super().sum();// 调用父类的普通函数 } } var son = new Son(1,2); son.sum();
-
继承中的属性/方法查找原则:就近原则,先看子类中有没有这个方法,没有再看父类中有没有这个方法
-
注意点
- 子类在构造函数中使用super,必须放在this前面,也即必须先调用父类的构造方法,在使用子类的构造方法
- 在es6中类没有变量提升,所以必须先定义类,才能通过类实例化对象
- 类里面的共有属性和方法一定要加this使用
-
this指向调用者
-
1.5 类的本质
- 类的本质也是function
- es6类实际上也是一个语法糖
- 类同样含有prototype属性对象以及所创建的对象含有_proto_属性对象,也即含有构造函数的所有用法,只是es6进一步的封装了
- 类的所有方法都定义在类的prototype属性上
二、函数
2.1 函数的定义
-
命名函数
function 函数名() { //函数体 }
-
匿名函数 函数表达式的形式进行声明
var 变量名 = function() {
};
-
通过new Function()方式创建 不推荐使用 太麻烦
- 每个函数都是Function函数的实例对象
- 每个函数都是一个对象,因此也具有原型对象等属性对象
var func = new Function('参数1','参数2','函数体');
2.2 函数的调用
-
普通函数
function test(){}; test();
-
对象的方法
var obj = { test: function(){}; } obj.test();
-
构造函数
function Test(){}; new Test();
-
绑定事件函数 当事件发生时便开始执行
div.onclick = function(){}
-
定时器函数 自动执行
setInterval(function(){},1000)
-
立即执行函数 里面的this指向window,在其前面必须加上逗号,以适应多个立即执行函数产生的情况
(function({})){} 或(function(){}())
2.3 改变函数内部的this指向
-
call(thisArg,arg1,arg2,…)
-
参数说明
-
thisArgs:当前调用函数this的指向对象
-
args…:传递的其他参数
-
作用
- 调用函数
- 修改函数运行时的this指向
-
调用函数
function print_s() { console.log('s'); console.log(this);// 此时指向window对象 } print_s.call();
-
修改函数运行时的this指向
function print_s(x,y) { console.log('s'); console.log(this);// 此时指向example_o console.log(x + y); } var example_o = { uname: 'zt' } print_s.call(example_o,1,2);
-
应用场景:实现继承,应该需要在正常模式下才行
-
-
apply(thisArgs,[arg1,arg2…])
-
用法与call方式一致,只是参数必须通过数组(伪数组)的形式进行传递
-
应用场景:与数组有关,常用于对数组的数学运算中。
-
利用apply借助于数学内置对象求最大值
console.log(Math.max.apply(Math, [-8, 2, 3, 4]));
-
-
-
bind
-
不会调用函数。但是能改变函数内部this指向
-
用法
fun.bind(thisArg,arg1,arg2,...)
-
thisArgs:当前调用函数this的指向对象
-
args…:传递的其他参数
-
返回指定的this值和初始化参数改造的原函数拷贝
-
注意:
- 原函数指向没有被改变,会将改变之后的函数返回
- 一般通过此方法调用函数,进行改变this指向
-
应用:当在某个指向内部需要调用定时器函数时,并在定时器函数内部调用其他对象引用,此时就不能用this,因此为了编码不复杂,并在之后对代码的可维护性,可以通过改变调用指向的方式
var btn = document.querySelector('button'); btn.onclick = function () { this.disabled = true; // 指向btn setTimeout(function () { this.disabled = false; // 如果不写bind,则指向window,写了btn则指向btn }.bind(this),3000); }
-
-
2.4 严格模式
-
什么是严格模式
-
概念
js提供了正常模式以及严格模式。es5的严格模式是采用具有限制性js变体的一种方式,也即在严格模式下运行js代码,需要严格按照一定的严格的规范才能够通过js解释器解析。
-
严格模式对正常模式下的js语义的一些更改
- 消除了js语法的一些不合理、不严谨地方
- 消除了代码运行的一些不安全之处,保证代码运行的安全
- 提高编译器效率,增加运行的安全
- 禁用了在ecmascript的未来版本中可能会定义的一些语法,为未来新版本的js做好铺垫。比如一些保留字如:class,enum不能做变量名
-
注意
严格模式在IE10以上版本的浏览器才被支持,旧版本浏览器中会被忽略。因此此模式谨慎使用,避免一些很多的问题
-
-
开启严格模式
-
严格模式可以应用于整个脚本和个别函数中,因此严格模式分为:为脚本开启严格模式和为函数开启严格模式
-
为脚本开启严格模式
-
为整个脚本文件开启严格模式,需要在所有语句之前放一个特定语句
'use strict'/"use strict"
-
案例
window.addEventListener('pageshow',function () { 'use strict'; num = 1;// 直接显示红色不符合规范 })
-
-
为函数开启严格模式
-
把声明
'use strict'/"use strict"
放在函数体所有语句之前,作用域仅限于当前函数
-
案例
window.addEventListener('pageshow',function () { function fn() { 'use strict'; num = 1;// 直接显示红色不符合规范 } })
-
-
-
严格模式中的变化
-
变量规定
-
在正常模式中,如果一个变量没有声明就赋值,默认是全局变量,该变量作为window对象的属性存在。严格模式禁止这种用法,变量都必须先用var声明再使用
-
严禁删除已经声明的变量,例如delete x;语法是错误的。
'use strict'; var num = 1; delete num;// 显示为红色
-
-
严格模式下this指向问题
- 以前构造函数不加new可以当普通函数一样调用,this指向全局对象,严格模式下,如果构造函数不加new调用,this会报错。
- 以前全局作用域函数中的this指向window对象,严格模式下全局作用域中函数中的this是undefined。主要解决区分构造函数与普通函数。
- 定时器this还是指向window,new实例化的构造函数指向创建的对象实例。事件,对象还是指向调用者。
-
函数变化
-
函数不能有重名的参数
-
函数必须声明在顶层。新版本的js会引入“块级作用域”(es6中已经引入)。为了与新版本接轨,不允许在非函数的代码块内声明函数
if (1 > 3) { // 不允许 function ftn() { } }
-
-
其他:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode
-
2.5 高阶函数
-
概念
对其他函数进行操作的函数,其接收函数作为参数/将函数作为返回值输出。
-
接收函数作为参数:回调函数
-
将函数作为返回值输出:闭包
-
什么是闭包
有权访问另一个函数作用域中变量的函数。也即一个作用域可以访问另一个函数内部的局部变量,而被访问的局部变量所在的函数便为闭包。简单的可以认为:闭包就是一种现象,一个作用域可以访问另一个函数内部局部变量的一个现象,也可以认为子函数访问父函数局部变量的一种机制。
-
闭包的作用:
-
延申了变量的作用范围
-
理解:函数中的函数成员可以访问父函数中的局部变量,但是当函数执行完成之后,局部变量一般会被释放掉,但是如果采用闭包,在函数外调用函数中的函数时,由于子函数使用了父函数中的变量,因此暂时不会释放,等待调用。因此在一定程度上延申了变量的作用范围,让其不局限作用于函数中,但是长此以往,也会产生内存泄漏的问题的出现。
-
案例:
function fn() { // 闭包(函数) var num = 1;// 父函数局部变量 return function fun() { console.log(num);// 使用父函数局部变量 } } fn()();// 等价于 fun();
-
-
使用闭包:立即执行函数(小闭包)
-
循环注册点击事件
<ul class="nav"> <li>好的</li> <li>欧克</li> <li>是的</li> <ul> var lis = document.querySelector('.nav').querySelectorAll('li'); for (var i = 0; i < lis.length; i++) { // 为每个li标签添加立即执行函数,并传递当前的索引i (function (i) { lis[i].onclick = function () { console.log(i); } })(i); // 每一次的执行类似于: //(function(0) { // lis[0].onclick = function() { // console.log(0); // } // })(0); // 当事件发生的时候,便于采取闭包思路,获取父函数(立即执行函数中的局部变量i的值),直接进行打印操作 }
-
闭包中的this指向
-
第一种情形:没有使用闭包
uname = 'OutThing';// 作为window属性存在 var ob = { uname: "Myob", myFun: function () { return function () { return this.uname; } } } console.log(ob.myFun()());// OutThing // 等价于 // var f = ob.myFun(); // var fn = f(); // fn = function () { // return this.uname; // }(); // 所以当前的this指向的是window,因此结果为OutThing
-
第二种情形:使用了闭包
uname = 'OutThing';// 作为window属性存在 var ob = { uname: "Myob", myFun: function () { var that = this;// 指向函数调用者 return function () { return that.uname; } } } console.log(ob.myFun()());// Myob // 等价于 // var f = ob.myFun(); // var fn = f(); // fn = function () { // return that.uname; // 而that作为父函数局部变量,值为指向的函数调用者,也即ob对象,因此结果为Myob // }();
-
-
-
2.6 浅拷贝和深拷贝
-
概述
- 浅拷贝只是拷贝一层,针对于更深层次的对象级别的数据,只是拷贝引用,因此拷贝之后再修改对象数据时,会把原来的数据一并进行修改操作。
- 深拷贝拷贝多层,每一级别的数据都会拷贝,而对象级别的数据则会另开辟新的空间,因此对象级别的拷贝的修改不会影响原来的数据。
-
浅拷贝
-
es6新增方法实现浅拷贝:Object.assign(target,…sources)
-
js实现浅拷贝
var o = { uname: 'zt', age: 19, mes: { uname: 'zztt' } }; var ob = {}; for (var k in o) { ob[k] = o[k]; } console.log(ob); ob.mes.uname = 'iii'; console.log(o);
-
解释:对于普通数据类型,直接赋值,但是对于复杂的对象级别的数据,拷贝引用,也即共用同一个空间,而例题中又对对象级别数据进行了修改,因此原数据中的对象级别的数据也会被进一步的跟着修改
- 结果:
可以发现:打印ob和打印o分别在重新赋值前后,理应不一样,但是打印结果却是一样的。原因:观察时间不一样,打开开发者工具,进行断点调试
可以发现打印结果如下:
出现上述原因的主要在于:观察时间不一样,打印mes的时候是花了一定的时间去寻找地址并打印相关的数据,而如果在第一个打印处添加断点,实则等待了第一个查询地址并打印,因此内容为赋值之前的内容。
-
-
深拷贝
-
Js实现深拷贝
function deepCopy(newobj,oldobj) { for (var k in oldobj) { // 获取属性值 var item = oldobj[k]; // 判断这个值是否是数组 由于数组也是对象,因此需要先判断 if (item instanceof Array) { newobj[k] = []; deepCopy(newobj[k],item); } else if (item instanceof Object) { // 判断这个值是否是对象 newobj[k] = {}; deepCopy(newobj[k],item); } else { // 该值属于简单数据类型 newobj[k] = item; } } } var o = { uname: 'zt', age: 19, mes: { uname: 'zztt' } } var ob = {}; deepCopy(ob,o); console.log(ob); ob.mes.uname = 'iii';// 此时不会影响另一个对象中的值 console.log(o);
-
三、正则表达式
3.1概述
正则表达式在js中作为对象存在
3.2 创建正则表达式
-
通过对象方式创建
var regexp = new RegExp(/123/);
-
通过字面量的形式创建
var rp = /\w/;
-
注:
/里面写的正则表达式不加引号,即使是字符串或者数字都不加引号/
3.3 验证正则表达式
采用对象方法test()来进行验证,只要字符串某一片段满足该正则表达式,则返回true,否则返回false。如果要对数据匹配等进行限定,则可以通过限定符等进行验证
3.4 正则表达式替换
-
StringObject.replace(‘替换规则’,‘替换成的字符串’);
var uname = '这句话很有意思,但是有点搞笑了'; console.log(uname.replace(/意思|搞笑/, 'hh'));// 这句话很有hh,但是有点搞笑了
满足一个匹配则替换并返回,要全局搜索,可以通过正则表达式参数
-
正则表达式参数
-
形式:
/表达式/[switch]
-
switch:修饰符,表示按照什么样的模式来匹配
-
g:全局匹配
-
i:忽略大小写
-
gi:全局匹配+忽略大小写
-
案例
var uname = '这句话很有意思,但是有点搞笑了'; console.log(uname.replace(/意思|搞笑/g, 'hh'));// 这句话很有hh,但是有点hh了
-
-
四、es6
4.1 什么是es6
es全称是ecmascript,它是由ecma国际标准化组织制定的一项脚本语言的标准化规范。
11年的时候便开始制定新版本es,但是会引入很多的内容,因此为了方便,规定每年的6月发布新的js版本,版本由年份组成,而es6实际上是一个泛指,泛指es2015及后续的版本。
4.2 es6自增语法
4.3 let
-
es6中新增的用于声明变量的关键字,let声明的变量只在所处于的块级有效,针对于es6新增的块级作用域
if (true) { let a = 1; } console.log(a);// a is not defined;
-
特别注意:一个块级作用域中存在子作用域,子作用域可以访问父作用域,但是父作用域不可以访问子作用域
if (true) { let a = 1; if (true) { let b = 1; console.log(a); // 1 } console.log(b); // b is not defined; }
-
注意:
- 使用var声明的变量不具备块级作用域特性,因此let变量的使用可以防止循环变量变成全局变量
for (var i = 0; i < 3; i++) { } console.log(i); // 3 // 外面可以访问 for (let i = 0; i < 3; i++) { } console.log(i); // i is not defined 外面不可以访问
-
使用let关键字声明的变量,不具有变量提升
-
使用let关键字声明的变量,具有暂时性死区特性,也即let声明的变量与块级作用域绑定,即使与外部变量同名,也没有相互的关系,因此以下代码会报错。
var a = 1; if (true) { console.log(a);// a is not defined let a; }
4.4 const
- es6中新增用于声明常量的关键字,常量也即:变量所存储的内存地址不可修改
- 具有的特性:
- 具有块级作用域
- 不存在变量提升
- 需要赋初值
- 值不可被修改
4.5 解构赋值
解构:分解数据解构,赋值:给变量赋值。es6中允许从数组中提取值,按照对应位置,对变量赋值。对象也可以实现解构
-
数组解构
let arr1 = [1,2,3]; let [a,b,c] = arr1;// [a,b,c]表示解构,而不是数组 console.log(a);// 1 console.log(b);// 2 console.log(c);// 3
[a,b,c]与数组中的数据进行一一匹配
-
左边解构变量个数小于右边数组数据个数
仍然一一对应关于
let arr1 = [1,2,3]; let [a,b] = arr1; console.log(a);// 1 console.log(b);// 2
-
左边解构变量个数大于左边数组数据个数
左边多于解构变量由于只是声明了,没有赋值,因此为undefined
let arr1 = [1,2,3]; let [a,b,c,d] = arr1; console.log(a);// 1 console.log(b);// 2 console.log(c);// 3 console.log(d);// undefined
-
-
对象解构
-
对象解构是按照属性匹配的方式进行解构
-
方式一:属性一一对应
let obj = { uname: 'zt', age: 19, sex: '男'}; let { uuname, age, sex} = obj;// { uuname, age, sex}为对应的解构变量 console.log(uuname);// undefined console.log(age);// 19 console.log(sex);// 男
左边的解构变量的变量名必须与右边对象属性名称对应方可获取值,否则undefined
-
方式二:属性一一对应的基础上,为解构变量取别名
let obj = { uname: 'zt', age: 19, sex: '男'}; let { uname: myName} = obj; console.log(myName);// zt console.log(uname);// uname is not defined
左边解构变量取别名之后,实则创建的变量是myName,而没有创建变量uname,因此实际能使用的变量为myName
-
4.6 剩余参数
-
当实参大于形参的时候,传统的函数可以使用函数内置属性对象arguments来获取数据,但是箭头函数没有arguments函数对象,而其采用的是剩余参数的方式进一步实现的。
-
使用
-
形参采用…args方式接收一个数组形式的数据
-
案例
var fn = (...args) => { var total = 0; args.forEach(item => total += item); return total; } console.log(fn(1,2,3));// 6
用法类似于arguments,都是一个伪数组
-
-
剩余参数与数组解构一起使用
-
用于获取剩余的参数数据
const arr = [1,2,3]; let [a,...b] = arr; console.log(a);// 1 console.log(b);// [2,3]
b获取了数组中匹配的剩余的数据
-
注:由于对象解构需要属性匹配,因此不能使用剩余参数来进行匹配。
-
4.7 扩展运算符
-
扩展运算符可以将数组或者对象转为用逗号分隔的参数序列
-
不同于剩余参数,剩余参数是将剩余的参数放在一个数组中,而扩展运算符则是将数组拆分为以逗号分隔的参数序列。但是两者都是用三个点(…)表示
-
用法:
let arr = [1,2,3]; // ...arr; // 1,2,3 console.log(...arr); // 1 2 3 // 等价于 console.log(1,2,3);// 1 2 3 // 由于console.log方法将逗号作为参数的分隔符,因此打印结果为1 2 3而不是1,2,3
-
应用:
-
合并数组
由于扩展运算符可以将数组转换为用逗号分隔的参数序列,因此可以将多个数组转换为多个参数序列,参数序列之间用逗号连接,加上[]构成新的数组
-
方法一:
let arr1 = [1,2,3]; let arr2 = [4,5,6]; let arr3 = [...arr1, ...arr2]; console.log(arr3);// [1,2,3,4,5,6];
-
方法二:
由于push()方法参数当为多个参数,且参数之间用逗号进行分隔的时候,也可以追加到数组中,因此符合扩展运算符对数组进行转换的结果
let arr1 = [1,2,3]; let arr2 = [4,5,6]; arr1.push(...arr2); console.log(arr1);
-
-
将伪数组转换为真正的数组,以便使用数组相应的方法
- 通过扩展运算符
由于伪数组只能读取以及只有Length属性,但是对于真正的数组的其他属性方法不可以使用,因此可以通过扩展运算符对于进行转换以至于可以使用数组相应的方法和属性
function fn() { var arr = [...arguments]; console.log(arr); } fn(1,2,3);
-
通过Array.from()方法
-
Array.from(伪数组,回调函数);
-
参数解释
- 伪数组:即需要转换的伪数组
- 回调函数:类似于数组的map方法,用来对转换后的每个元素进行处理,将处理后的值放入返回的数组
-
使用
let arrayLike = { '0': 1, '1': 2, 'length': 2 } let newArray = Array.from(arrayLike,item => item * 2); console.log(newArray);
-
-
4.8 箭头函数
-
es6中新增的定义函数的方式
(arg1,arg2,...) => {函数体};// ()放参数,{}中作为函数体 // 等价于 function (arg1,arg2,...) { 函数体; }
-
箭头函数的调用
采用函数表达式的方式,用变量进行接收
const fn = (n) => { return n * 10 }; console.log(fn(3));
-
注意:
-
函数体中只有一句代码,且代码的执行结果就是返回值,可以省略大括号
const fn = (n) => n * 10; console.log(fn(3));
-
如果形参只有一个,可以省略小括号
const fn2 = n => n + 10; console.log(fn2(3));
-
-
与this关键字的关系
-
与传统的函数不一样,箭头函数不绑定this关键字,箭头函数中的this,指向的是函数定义位置的上下文this
-
传统的函数
var obj = { uname: 'zt'}; function fn() { console.log(this); return function () { console.log(this); } } let resFn = fn.call(obj);// 指向obj resFn();// 指向window
-
箭头函数
var obj = { uname: 'zt'}; function fn() { console.log(this); return () => { console.log(this); } } let resFn = fn.call(obj);// 指向obj resFn();// 指向obj
解释:由于箭头函数不绑定this关键字,其定义区域便为其this的指向,由于箭头函数定义在fn中,且fn指向为obj,因此箭头函数中的this指向也是obj
-
案例分析
var obj = { uname: 'zt', fn: () => { console.log(this.uname); } } obj.fn();// undefined
箭头函数不绑定this关键字,其定义区域便为this指向,但是对象没有作用域,因此当前箭头函数中的this指向的是全局作用域,而全局作用域中没有uname属性,因此输出为undefined;
-
4.9 模板字符串
-
es6新增的创建字符串的方式,使用反引号定义
let str = `模板字符串`; console.log(str);
-
特点
-
可以解析变量。将变量放于${}中
let val = 123; let str2 = `模板字符串${val}`; console.log(str2);
-
现在在模板字符串的字符串换行书写,同时显示也会进而换行显示
let obj = { uname: 'zt', age: 18 } let str3 = ` <div>模板字符串显示中也是换行的${obj.uname}</div> <div>模板字符串显示中也是换行的${obj.age}</div> `; console.log(str3);
-
可以调用函数
let fn = () => '模板字符串'; let str4 = `显示${fn()}`; console.log(str4);
-
4.10 set数据结构
-
es6提供了新的数据结构Set。类似于数组,但是不同于数组的是,Set数据结构成员的值都是唯一的,没有重复的值
-
创建Set数据
Set本身是一个构造函数,用来生成Set数据结构
-
创建空的Set数据
const s = new Set();
-
接受一个数组作为参数,用于初始化
const set = new Set([1,2,3,4]);
-
-
应用:
-
数组去重
由于Set数据结构数据成员的唯一性,因此可以用其对数组元素去重,再利用扩展运算符转换为数组
const set = new Set(['a','a','b']); var arr = [...set];; console.log(arr);
-
-
属性
- size 获取set数据个数
-
方法
-
add(value) 添加某个值,返回Set结构本身
-
delete(value) 删除某个值,返回布尔值
-
has(value) 表示该值是否是Set的成员,返回布尔值
-
clear() 清除所有成员,没有返回值
-
用例
const set = new Set(); set.add(1).add(1);// 相同数据添加不进去 console.log(set.size);// 1 set.delete(1); console.log(set); set.has(1); console.log(set); set.clear() console.log(set);
-
-
遍历Set数据结构
Set结构的实例与数组一样,都可以使用forEach,Map等方法进行遍历数据
const set = new Set(); for (var i = 0; i < 5; i++) { set.add(i); } set.forEach(item => console.log(item));