es5、es6
1.super
-
super
关键字使得子类调用了父类中的构造函数,父类可以使用子类传进来的属性进行各种运算, -
super
关键字 还可以调用父类的方法
class Father{
constructor(x,y){
this.x=x;
this.y=y;
}
sum(){
console.log(this.x+this.y)
}
say(){
return "我是爸爸"
}
}
class Son extends Father{
constructor(x,y){
//调用了父类中的构造函数
super(x,y)
//子类独有的属性
this.x=x;
this.y=y
}
say(){
console.log(super.say())
}
//子类独有方法
subtract(){
console.log(this.x-this.y)
}
}
let son=new Son(5,3);
son.subtract();
son.sum();
tips: 继承中的属性或者方法查找原则:就近原则
- 继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的
- 继承中,如果子类里面没有,就去查找父类有没有这个方法,如果有,就执行父类的这个方法(就近原则)
- 子类在构造函数中使用
super
,必须放到this前面(必须先调用父类的构造方法,再使用子类构造方法) - 在
es6
中类没有变量提升,必须先定义类,才能通过类实例化对象\ - 类里面的共有的属性和方法一定要加
this
使用
2.this指向
- constructor (构造函数)里面的
this
指向的是 创建的实例对象 - 方法 谁调用了方法,那么它的this就是指向它
var that //等于构造函数里this 即ldh这个对象
class Star{
constructor(name,age){
that=this
this.name=name
this.age=age
this.btn=document.querySelector('button');
this.btn.onclick=this.sing
}
}
sing(){
/* 这个sing方法里的this,指向的是btn 这个按钮,因为这个按钮调用了这个函数 */
console.log(this)
console.log(this.name) //输出undenfind
console.log(that.name) //输出ldh
}
dance(){
/* 这个dance里面的this 指向的是实例对象 ldh 因为是ldh调用了这个函数 */
console.log(this)
}
let ldh= new Star(ldh,18)
ldh.dance()
3.构造方法和原型(模拟面向对象)
-
es5
不使用prototype
原型对象
function Star(name,age){
this.name=name;
this.age=age;
this.sing=funtion(){
console.log(我会唱歌)
}
}
var ldh=new Star('刘德华',18);
var zxy=new Star('张学友',19);
/* new多个,基本属性不会,但sing方法会开辟多个不同的内存空间,存在内存浪费的现象 */。
-
es5
使用构造函数prototype
原型对象
function Star(name,age){
this.name=name;
this.age=age;
Star.prototype.sing=funtion(){
console.log(我会唱歌)
}
}
Star.prototype ={
/*如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数*/
constructor:Star
}
var ldh=new Star('刘德华',18);
var zxy=new Star('张学友',19);
ldh.sing()
/* new多个,基本属性不会,但sing方法会开辟多个不同的内存空间,存在内存浪费的现象 */。
-
原型对象
- 原型是一个对象,我们也称为
prototype
为原型对象 -
构造函数
通过原型
分配的函数是所有对象所共享的。 - js规定, 每一个
构造函数
都有一个prototype
属性,指向另一个对象,注意这个prototype
就是一个对象,这个对象
的所有属性
和方法
,都会被构造函数
所拥有。 - 我们可以把这些不变的方法,直接定义在
prototype
对象上,这样所有对象的实例就可以共享这些方法 - 一般情况下,我们的公共属性定义的构造函数里面,公共的方法我们就放到原型对象上
- 对象身上(ldh对象)系统自己添加一个__proto__指向我们构造函数中的原型对象
- ldh.__proto__和构造函数Star.prototype 是等价的
- 原型是一个对象,我们也称为
-
__proto__和 prototype 的区别
-
__proto__
是new
出来的实例的的原型对象 -
prototype
是构造函数的原型对象tips:
es5
的构造函数方法很好用,但是存在浪费内存的问题,可以通过构造函数的原型对象来存放方法,使得方法在不同对象中共享,起到节约内存的作用。
-
4.原型链
- 实例对象 proto (找不到的再向上找)-> 构造函数的原型对象 __proto__(找不到再向上找)->Object.prototype__proto__(找不到再向上找)-> null
-
js成员的查找机制
- 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性 。
- 如果没有就查找它的原型(也就是__proto__指向的prototype原型对象)。
- 如果还没有就查找原型对象的原型(object的原型对象)。
- 依次类推一直找到Object为止(null)。
function Star(name,age){
this.name=name;
this.age=age;
}
Star.prototype.sing=function(){
console.log("我会唱歌");
}
Star.prototype.sex='女' //构造函数原型成员 ,优先级中等
Object.prototype.sex="男"//object对象的原型成员 ,优先级最低
var ldh =new Star('刘德华',18)
ldh.sex="男" //实例对象的原型成员 优先级最高
console.log(ldh.sex)
//打印这个实例向上找,可以找到Object
tips: 每个对象里都有 __proto__这个属性
5.利用原型对象,扩展内置对象方法
console.log(Array.prototype) 可以打印js的一些关于数组的api
//在Array对象上添加自定义方法
Array.prototype.sum=function(){
var sum=0;
for(var i=0;i<thsi.length;i++){
sum+=this[i];
}
return sum;
}
var arr=[1,2,3];
console.log(arr.sum())
var arr =new Array(11,22,33)
arr.sum();
//不可采用以下方法加入方法
Array.prototype=
{
sum: function(){
var sum=0;
for(var i=0;i<thsi.length;i++){
sum+=this[i];
}
return sum;
}
}
tips: 自己加的原型
方法会高亮,不可采用对象
的方式去添加原型方法,会覆盖原先就存在的方法。
6.call
方法
-
call
可以调用函数。 - 可以改变这个函数的
this
指向。 - 第一个参数是
this
的指向要被改变到这个参数的。 - 第二个开始 就当做是普通函数的普通参数就可以了,只是this指向改变了,但方法里一些操作没有改变
- es5用call绑定this继承了属性
function fn(x,y){
console.log('我想喝手磨咖啡');
console.log(this);
console.log(x,y);
}
var o={
name:'andy'
}
fn.call()
/* 此时这个函数的this,就指向了o这个对象,指向哪个对象根据第一个参数决定*/
fn.call(o,1,2)
1. 父构造函数
function Father(name,age){
//this 指向父构造函数的对象实例
this.name=name;
this.age=age;
}
2. 子构造函数
//这个score用的son自己的属性
function Son(name,age,score){
//this 指向子构造函数的对象实例,
//用call来调用Father这个构造函数,并把指向改成子类的构造函数的this,就实现了继承
Father.call(this,name,age)
this.score=score
}
var son=new Son('刘德华',18,200)
console.log(son)
7. prototype
继承方法
原型对象(prototype)继承方法,通过原型链向上找规则,
Father实例(找不到向上找)->Father的原型对象的方法
就可以继承方法了,且子son方法定义自己的方法时不会影响到父
//Father 是es5的构造函数
son.prototype=new Father()
son.prototype.constructor=son
8. 类的本质
类的本质其实还是一个函数 我们也可以简单的认为类就是构造函数的另外一种写法
-
es5 通过构造函数+原型实现面向对象 编程有以下特点
- 构造函数有原型对象prototype
- 构造函数原型对象prototype里面有constructor指向构造函数本身
- 构造函数可以通过原型对象添加方法
- 构造函数创建的实例对象有__proto__原型指向 构造的原型对象
-
es6
- class有原型对象prototype
- class的prototype里面有constructor指向构造函数本身
- class可以通过原型对象添加方法
- class创建的实例对象有__proto__原型指向 构造的原型对象
9. 数组方法
-
forEach 迭代遍历数组,break和retrun不会终止
var arr= [1,2,3]; arr.forEach((value,index,array)=>{ console.log("每个数组元素"+value) console.log("每个数组元素的索引号"+index) console.log("数组本身"+array) })
-
filter 主要用于筛选数组
var arr=[12.66,4,88] var newArr=arr.filter((value,index,array)=>{ return value>=2 //满足条件就把值放到新数组里 })
tips:返回的是一个新数组,不会影响原来的数组
-
-
some
主要用于检测数组中的元素是否满足指定条件,通俗点查找数组中是否有满足条件的元素
var
arr=[12.66,4,88]
var flag=arr.some((value,index,array)=>{
return value>=2 //满足条件就是true
})
arr=[12.66,4,88]
var flag=arr.some((value,index,array)=>{
if(value>30){
return true
}
})
tips:返回的是一个布尔值,如果查找到这个元素就返回true,不再进行循环,效率更高。
10. 字符串方法
trim 去除左右两边的空格,但不会去除中间的空格
str.trim()
11. object.defineProperty()
定义对象中新属性或修改原有的属性
object.defineProperty(obj,prop,descriptor)
object.defineProperty
的参数
参数 | 是否必需 | 类型 | 说明 |
---|---|---|---|
obj | true | Object | 目标对象 |
prop | true | String | 需新定义或修改的属性的名字 |
descriptor | true | Object | 目标属性所拥有的的特性 |
var obj={
id:1,
pname:"小米",
price:1999
}
//对对象定义新属性并赋值,以前的写法
obj.num=1000;
obj.price=99;
//现在的写法
Object.defineProperty(obj,'num',{
})
第三个参数descriptor的参数
参数 | 是否必需 | 类型 | 说明 |
---|---|---|---|
value | false | mixed | 设置属性的值 默认undefined |
writable | false | boolean | 是否可以重写 默认false |
ebumerable | false | boolean | 目标是否可以被枚举(遍历),默认false |
configurable | false | boolean | 目标属性是否可以被删除或是否可以再次修改特性true/false 默认为false |
tips: 注意新定义的descriptor 都是默认为false 用obj={} 来定义的因为没有定义是否可以被XX操作所以都是true
12.函数
所有的函数都是Function的实例(对象),函数也是个对象,万物皆对象
一、 函数的定义方式
1.自定义函数(命名函数)
function fn(){};
2.函数表达式(匿名函数)
var fun =function(){
}
3.利用new Function (不推荐,效率低)
new Function('参数1','参数2','函数体')
参数 | 是否必需 | 类型 | 说明 |
---|---|---|---|
参数1 | false | String | 第一个参数 |
参数2 | false | String | 第二个参数 |
函数体 | false | String | 第三个参数 |
tips:如果只有一个参数那么就是函数体
二 、 函数的调用方式
1.普通函数
this
指向window
function fn(){
console.log('人生的巅峰')
}
fn() (window.fn())
fn.call()
2.对象的方法
this
指向o
var o ={
sayHi:function(){
console.log('人生的巅峰')
}
}
o.sayHi();
3.构造函数
this
指向实例对象
function Star(){};
new Star();
4.绑定事件函数
this
指向是函数的调用者btn
这个按钮对象
//点击了按钮就可以调用这个函数
btn.onclick=function(){};
5.定时器函数
this
指向的是window
这个函数式定时器自动1秒钟调用一次
setInterval(function(){
},1000);
6.立即执行函数
this
指向window
(function(){
console.log('人生的巅峰')
})()
tips: 立即执行函数是自动调用
调用方式 | this指向 |
---|---|
普通函数调用 | window |
构造函数调用 | 实例对象,原型对象里面的方法也指向实例对象 |
对象方法调用 | 该方法所属对象 |
事件绑定方法 | 事件绑定对 |
定时器函数 | window |
立即执行函数 | window |
13.改变this指向
-
call
- 调用函数
- 可以改变函数内部的this指向
var o={
name:"1"
}
functon fn(a,b){
console.log(a+b)
}
fn.call(o,1,2)
tips: 后两个参数是形参 用于参与fn的一些对象,主要用于在es5中实现继承
-
apply
- 调用函数
- 可以改变函数内部的this指向
- 主要应用 apply 借助于数学内置对象求最大值和最小值。
var o={
name:"1"
}
functon fn(a,b){
console.log(a+b)
}
fn.apply(o,[1,2])
var arr=[1,66,4]
//如果不想改变this(指定对象)就写个null,但不合适写Math就行了
Math.max.apply(Math,arr)
tips:参数必须是数组,但打印后却是字符串或是数字(根据数组里的数据类型,判定)
-
bind
- 不会调用函数
- 可以改变函数内部的this指向
- 返回的是原函数改变this之后产生的新函数(深拷贝)
var o={
name:"1"
}
functon fn(a,b){
console.log(a+b)
}
var f= fn.bind(o);
f();
//亦或者是
functon fn(a,b){
console.log(a+b)
}.bind(this)
14. call、apply、bind总结
- 相同点:都可以改变函数内部的this指向
-
区别点:
- call和apply会调用函数,并且改变函数内部this指向。
- call和apply传递的参数不一样,call传递参数aru1,aru2形式,apply必须数组形式[arg]
- bind 不会调用函数,可以改变函数内部的this指向
-
主要应用场景:
- call经常做继承。
- apply经常跟数组有关系,比如借助于数学对象实现数组最大值和最小值
- bind不调用函数,但是还想改变this指向,比如改变定时器内部的this指向,ajax的this指向
15. 严格模式
-
作用:
- 消除了
js
语法的一些不合理、不严谨、减少了一些怪异行为。 - 消除代码运行的一些不安全之处,保证代码运行的安全。
- 提交编译器效率,增加运行速度。
- 禁用了
es
的未来版本中可能会定义的一些语法,为未来新版本的js
做好铺垫。比如一些保留字如:class
,enum
,export
,extends
,import
,super
不能做变量名
- 消除了
-
变化
- 我们的变量必须
先声明
再使用 - 我们不能
随意删除
已经声明好
的变量 - 在严格模式中全局作用域中函数中的
this
是undefined
而不是window
- 以前构造函数不加
new
也可以调用,当前普通函数,this
指向全局,在严格模式下不能使用了,this指向undefined
。 - new 实例化的构造函数指向创建的对象实例
- 定时器的this 还是指向window
- 函数不能有重名的参数
- 函数必须声明在顶层,不允许在非函数的代码块内声明函数。比如if和for的花括号里定义函数,但函数嵌套函数是可以的。
- 我们的变量必须
//为整个脚本开启严格模式
<script>
'use strict';
</script>
<script>
(function()){
'use strict';
}()
</script>
//为某个函数开启严格模式
<script>
function fn(){
'use strict';
/*里面的代码按照严格模式执行*/
}
function fun(){
/*里面的还是按照普通模式执行*/
}
</script>
tips:兼容ie10以上,ie10以下会忽略这个模式
16. 高阶函数
高阶函数- 函数可以作为参数传递,参数的接收方就是高阶函数,即 fn
function fn (a,b,callback){
console.log(a+b);
callback && callback()
}
fn(1,2,function(){
console.log(我是最后调用的);
})
17. 闭包
- 闭包指有权访问另一个函数作用域中变量的函数,简单理解就是,一个作用域可以访问另外一个函数内部的局部变量,(closure 是闭包)此时就有闭包的产生,闭包也是高阶函数
- 这里fn是闭包,fun调用了fn的变量,也就是被调用变量所在的函数,就是闭包函数
function fn (){
var num=10;
function fun(){
console.log(num)
}
fun ()
}
fn
- 我们fn外面的作用域可以访问fn 内部的局部函数
function fn(){
var num=10;
return function(){
console.log(num)
}
}
//这里f保存的是个函数
var f=fn()
//这里输出的是num
f()
for (var i=0;i<lis.length;i++){
/*利用for循环创建了4个立即函数*/
/*立即执行函数也成为小闭包因为立即执行函数里面的任何一个函数,都可以使用它的i这个变量*/
/*这里的i是从底下的那个i传进来的*/
(function(i){
lis[i].onclick=function() {
console.log(i)
}
})(i)
tips:闭包的主要作用:延伸变量的作用范围。但闭包容易照成内存泄漏,它一直不释放资源,闭包总结:闭包是一个函数,闭包延伸了作用域范围
18. 递归
- 函数内部自己调用自己,这个函数就是递归函数,递归函数必须加退出条件,利用递归函数求1-n的阶乘。
function fn(n){
if(n==1) return 1;
return n*fn(n-1);
}
console.log(fn(3))
执行过程
return 3*fn(2)
return 3*(2*fn(1))
return 3*(2*1)
return 6
- 用递归求斐波那契数列(兔子序列)
function fb(n){
if(n===1 || n===2){
return 1
}
return fn(n-1)+fb(n+2);
}
console.log(fb(3))
console.log(fb(3))
- 用递归来遍历数据
var data=[
{
id :1,
name:'家电'
goods':[
{
id :11,
name:'家电11'
},
{
id :12,
name:'家电12'
}
]
}
{
id :2,
name:'家电2'
goods':[
{
id :13,
name:'家电13'
}
]
}
]
function getID(json,id){
var o ={};
json.forEach(function(item)) {
if(item.id==id){
console.log(item);
o=item;
}else if(item.goods&&item.goods.learth>0){
o= getID(item.goods,id)
}
}
}
console.log(getID(data,1))
tips: 开辟内存空间,容易照成死递归,出现栈溢出。在forEach里面执行递归,不加退出条件,循环已经帮你加了。
19. 浅拷贝和深拷贝
- 浅拷贝只是拷贝一层,更深层次对象(对象或数据等复杂数据类型)级别的只拷贝引用。
1.for 循环实现浅拷贝
var obj={
id:1,
name:"andy",
msg:{ //这个对象是深层次对象会被浅拷贝,会被拷贝地址,修改o同时会修改obj,这个就是浅拷贝
age:18
}
}
var o={}
for(var k in obj){
//k 是属性名
// obj[k] 属性名
o[k]=obj[k];
}
console.log(o);
o.msg.age=20;
console.log(obj)
2.es6的语法糖实现浅拷贝
Object.assign(o)
/*主要用来对,对象赋值, key值相同的会覆盖
其他没进行赋值的不会处理,因为是浅拷贝所以会影响原数组
*/
- 深拷贝拷贝多层,每一级别的数据都会拷贝
1.用递归的方式来实现深拷贝
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;
}
}
}
2.用JSON.stringify()和 jSON.parse() 来实现 深拷贝
tips: 数组也属于Object 所以要把Array判断写在object的前面。总结:浅拷贝,深层次的数据如数组和对象只会拷贝引用,修改拷贝后的数据会同时修改原来的被拷贝的的数据,深拷贝,是对数组和对象开辟不同的空间,被拷贝的的数据。
20. 正则表达式
灵活性、逻辑性和功能性非常的强,
可以迅速地用极简单的方式达到字符串的复杂控制
- 创建正则表达式
1.利用RegExp
对象来创建 正则表达式
var regexp=new RegExp(/123/)
console.log(regexp)
2.利用字面量
,创建正则表达式,即用//即注释的中间写上规则匹配就是正则表达式了
var rg=/123/;
/*
可用test 方法来检测是否符合正则表 返回true或false
*/
var rg=/123/;
console.log(rg.test(123));
- 边界符 ^ $
var rg=/^abc/ ^匹配的是开头,这里的是abc
var rg=/^abc$/ 精确匹配 只有输入abc 才会匹配
- 字符类 []
表示有一系列字符可供选择,只要匹配其中一个就可以了。这个只能多选一要和量词符,匹配使用。量词是设定某个模式出现的次数。
/* 只要包含a,或者b,或c,其中一个就可以了 */
var rg=/[abc]/
/* 只能选择一个a或者b或者c其中的一个 三选一 */
var rg=/^[abc]$/
/* 只能选择一个a-z,26字符中其中的一个字符,26选一
表示到a到z的范围 可以在[]中添加各种字符组合
*/
var rg=/^[a-zA-z]$/
/* 如果中括号里 ^ 表示取反的意思 不要和边界符 ^ 混淆 取反即 以下就不能是 a-z和A-z
*/
var rg=/^[^a-zA-z]$/
- 量词符
如果前面的那个没有加上中括号,那么就作用于量词符前面那个的字符。有中括号就按照中规则里匹配的规则去实现。
//* 相当于>=0 可以出现0次或者很多次
var rg=/^a*$/;
console.log(rg.test(''))
console.log(rg.test('aaa'))
//+ 相当于>=1 可以出现1次或者很多次,不能不出现,即0次
var rg=/^a+$/;
console.log(rg.test(''))
console.log(rg.test('aaa'))
//? 相当于 1 || 0 可以出现1次或者0次
var rg=/^a?$/;
console.log(rg.test(''))
console.log(rg.test('a'))
console.log(rg.test('aaa'))
//{3}就是重复出现3次
var rg=/^a{3}$/;
console.log(rg.test(''))
console.log(rg.test('aa'))
console.log(rg.test('aaa'))
//{3,}出现大于等于3
console.log(rg.test(''))
console.log(rg.test('aaa'))
//{3,16}出现大于等于3并且小于等于16
var rg=/^a{3,16}$/;
console.log(rg.test(''))
console.log(rg.test('a'))
- 量词符和模式匹配
模式是 :[a-zA-z] 某个规则
量词符:{6,16} 这个规则的里字符可出现的次数 即只能出现6-16个a-zA-z 里的字符 且全部合起来只能有6-16个。
var rg=/^[a-zA-z]{6,16}$/
- 优先级 用小括号来表示优先级
//这里有小括号所以表示 abc 重复3次
var rg=/^(abc){3}/
//这里没有小括号 c 重复3次
var rg=/^abc{3}/
- 可以用菜鸟正则表达式的测试工具 菜鸟工具
- 预定义类 简写正则
预定类 | 说明 |
---|---|
d | 匹配0-9之间的任一数字,相当于[0-9] |
D | 匹配0-9以外的字符,相当于1 |
w | 匹配任意的字母、数字、和下划线,相当于[A-Za-z0-9_] |
W | 匹配任意的字母、数字和下划线以外的字符,相当于2 |
s | 匹配空格(包括换行符、制表符、空格符等),相当于[trnvf] |
S | 匹配非空格(相当于3 |
- 正则里还可以用 | 表示或者的意思
//座机号码验证 010-12345678 0591-1234567
//第一种
var reg=/^\d{3}-\d{8}|\d{4}-\d{7}$/
//第二种
var reg=/^\d{3,4}-\d{7,8}$/
//手机号码的正则表达式
var rg=/^1[3|4|5|7|8]\d{9}$/;
//qq号的正则 10000开始
var rg=/^[1-9]\d{4,}$/
- replace 替换
var str=str.replace(/andy/,bady)
参数 | 说明 |
---|---|
第一个参数 | 被替换的字符串或者正则表达式 |
第二个参数 | 替换为的字符串 |
返回值 是一个替换完毕的新字符串
tips: 正则表达式里面不需要加引号, 不管是数字型还是字符串型,正则表达式的中间不要有空格即//里添加的内容不要用空格来间隔
以下都是es6、es7
的知识点
1.let
关键字
- let关键字声明的变量具有块级作用域,用来替代var ,主要用来防止内层变量覆盖外层变量,防止循环变量变成全局变量。
if(true){
let a=1;
var b=3
}
console.log(a) //访问不到 undefined
console.log(b) //访问的到,输出3
- let关键字声明变量,没有变量提升未定义不能访问
- 具有暂时性死区特性
var num =10
if(true){
console.log(num) undefined
let num=1
}
/* num输出在块级作用域里,使用的是块级的作用域定义的变量,而且是在未定义前输出所以是undefined */
- let 解决循环的变量问题
for (var i=0;i<10;i++){
console.log(i)
setTimeOut(function(){
console.log(`i:${i}`)
})
}
在var定义的变量下,定时器里输出的都是10
因为此时循环已经结束了,i是最后的一个值。
用let 可以正常循环输出
2.const
关键字
- 声明的常量具有块级作用域
- 必须要赋初始值
-
常量赋值后,内存地址(值)不能修改,分两种情况
- 对于复杂数据类型,里面的值可以更改,但数据值本身不能更改
- 对于普通数据类型,值就是不能更改
const a =1
a=3 //报错,值不能更改
const arr=[1,2]
arr[0]=3 //可更改,只是更改结构内部的值 arr=[3,2]
arr=[34,44] //不可更改整体都更改,是地址的更改,不允许
- 不存在变量提升 (先声明再使用)
tips: 如果让对象里的属性不可进行修改可以使用es5的方法 Object.freeze(arr) 此时 arr内部的属性也不能更改
3.解构赋值
- 数组解构允许我们按照一一对应关系从数组中提取值,然后将值赋值给变量
let [a,b,c]=[1,2,3] || [];
console.log(a,b,c,d,e)
a 、b、c 输出 1,2,3 d,e是undefined
- 对象解构赋值
let person ={name:'zhangsan',age:20}
let {name,age}=person || {}
console.log(name) //zhangsan
console.log(age) // 20
let {name:myName,ages:myAge}=person
console.log(myName,myAge) //也可以输出
- 解构和扩展运算符一起的使用
//第一种情况,把整个的对象赋值给另一个读写
let user = { a: 1,b: 2,c: 3,d: 4,e: 5}
let
{
aa = { a: user.a, b: user.b },
bb = { ...user }
} = {}
/*
第二种情况,可以把对象的值分配到不同的对象
*/
let users = {}, userDetails = {};
({ a: users.a, b: users.b, ...userDetails } = user);
console.log(users, userDetails)
//users {a:1,b:2} userDetails {c:3,d:4,e:5}
- 对象解构可以嵌套获取值
const Tom={
name: 'Tom jones',
age:25,
family:{
mother: 'Norah Jones',
father: 'Richard Jones',
brother: 'Howard Jones'
}
}
//只有在变量是underfine时,才能使用默认值 null false 0 都是有值的
const { father:f,mother:m,brother='sss' } = Tom.family || {} ;
//以上的f、m 是别名 输出用别名
- 用数组解构来交换值
let a=10
let b=20
[a,b]=[b,a]
console.log(a,b) //a输出 20 b输出10
tips: 第二种情况,一定要加上括号。
4.箭头函数
- 箭头函数是用来简化函数定义语法的
function sum(num1,num2){
return num1+num2
}
const sum=(n1,n2)=>{
return n1+n2
}
//以上代码只有一行,且return这行代码就可以简写为
const sum=(n1,n2) => n1+n2
- 箭头函数的形参个数 只有一个时也可以省略个数
- 箭头函数不绑定this,箭头函数没有自己的this关键字,如果在箭头函数中使用this,this关键字将指向箭头函数定义位置中的this
- 经典面试题
var num=10
let obj={
num:1
say:(){
console.log(this.num)
}
}
obj.say() //这里输出的是10
- 具名的的箭头函数
const gg=()=>{
console.log(1)
}
- 顺口溜 删掉function关键字,加上一个胖箭头,没有参数加括号,一个参数可选择(加不加括号),多个参数逗号分隔(要加括号的)
- 箭头函数不执行this 绑定
-
不适用箭头函数的情况
- 构造函数
- 为原型添加方法的时候
- 各种绑定事件
- 在函数中使用arguments对象的时候
1.作为构造函数,一个方法需要绑定到对象
const Person=function(name,points){
this.name=name
this.points=points;
}
const jelly=new Person('jelly',5);
2.原型添加方法
Person.prototype.updatePoints=function(){
this.points++
console.log(this.points)
}
3.事件
const button=document.querySelector('.zoom');
button.addEventListener('click',function(){
})
4.需要使用arguments
const sum =function(arguments){
/*arguments不是个真正的数组 需要用Array.from 转换 或用扩展运算符[...arguments]来转换*/
return Array.from(arguments).reduce((prevSum,value)=>pervSum+value,0 )
}
tips: 对象是不产生作用域,指向的s是全局window,所以箭头函数的this,指向的是window,所以这里输出的是10
5.剩余参数
- 剩余参数语法允许我们将一个不定数量的参数表示为一个数组
//定义个方法
const sum =(...args)=>{
let total=0;
args.forEach(item=>{
total+=itme
})
return total
}
sum(10,20) //args里保存着10和20
sum(10,20,30)//args里保存10,20,30
- ...args,参数是一个数组的组合
- 剩余参数和数据解构结合
let ary1=['张三','李四','王五']
let [s1,...s2]=ary1
s1 //存着张三
s2 //接收剩余参数成为个新数组,存着李四和王五
6.扩展运算符
-
扩展运算符
可以将数组或者对象转为用逗号分隔的参数序列
let ary=[1,2,3];
...ary //1,2,3 转为去掉括号的用逗号分隔的参数序列
console.log(...ary) 1 2 3
-
扩展运算符
可用于合并数组,生成的是新数组
let ary1=[1,2,3]
let ary2=[4,5,6]
...ary1 //1,2,4
...ary2 //4,5,6
//第一种
let ary3=[...ary1,...ary2]
//第二种 -- push方法可以有多个参数
ary3=ary1.push(...ary2)
-
扩展运算符
可将伪数组转为真正的数组,就可以使用数组的一些方法
var Odivs=document.getElementsByTagName('div')
console.log(Odivs) //伪数组 可迭代遍历但没有数组方法
var ary=[...Odivs]
ary.push('a')
console.log(ary);
-
扩展运算符
是深拷贝,修改使用新生成的数组,不影响原来数组的值 -
扩展运算符
的应用实践
1. //把二维数组扩展成对象数组
[...array[1],...Array[1]]
输出
[object,object]
2. //可用来代替apply方法
const fruit=['apple','bananas','pear']
const newFruit=['orange','mongo']
//用apply合并数组,apply后面跟着的参数是数组
fruit.push.apply(fruit,newFruit)
//用扩展运算符来合并
fruit.push(...newFruit)
-
扩展运算符
适用于函数的传参
7 array.from() --数组的扩展方法
- 可以把伪数组转为真正的数组
- 方法还可以接收第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理的后的值放入返回的组数
例子1
var arrayLike={
"0":"1",
"1":"2",
"length":2
}
Array.from(arrayLike,item=>item*2)
例子2
const todos=document.querySelectorAll('li');
const names=Array.from(todos,todo=>todo.textContent);
console.log(names)
- 能把字符串也转换成数组
const str='abc'
console.log(Array.from(str)) [a,b,c]
8 array.find() --数组的扩展方法
- 用于找出第一个符合条件的数组成员,如果没有找到返回undefined
let ary=[{
id:1,
name:'张三'
},{
id:2,
name:'李四'
}
]
let target= ary.find(item=> item.id==2 )
9 array.findIndex() --数组的扩展方法
- 用于找出第一个符合条件的数组成员的位置,如果没有返回-1
let ary=[10,20,50]
const index= ary.findIndex((item,index)=>itme>15)
console.log(index)
10 array.includes() --数组的扩展方法
- 表示某个数组是否包含某个元素
- 字符串也可使用,可传入第二个参数表示第几个开始
let ary=["a","b",c]
ary.includes("a")
11 array.of() --数组的扩展方法
当前方法可以弥补 new Array 构造方法返回结果不一致问题。
/*当参数只有一位的时候,返回的是长度为1的数组 [undefined*1],而不是[1] 只有当参数是多个的情况下才会返回正确的数据*/
new Array(1)
array.of(1) 输出的是[1]
12 array.every() --数组的扩展方法
当遍历时遇到false时,就不执行了,返回false
13 模板字符串 --String的扩展方法
- 作用就是字符串可以解析变量,用于字符串拼接 使用反引号
let name=`账上`
let sayHello =`hello${name}`
- 模板字符串中可以换行,写的比较美观
let result={
name:"Zhangsan",
age:20
}
let html=`
<div>
<span>${result.name}</span>
<span>${result.age}</span>
</div>
`
- 模板字符串中可以调用函数、表达式等
const fn=(){
return '我是fn函数'
}
let html=`我是模板字符${fn()}`
const template=`
<div class="greet">
<p> Hello </p>
</div>
`.trim()
//模板字符串本身就是字符串,所以可以使用字符串方法
- 模板字符串的实战应用
function renderTodos(todos){
return (
`<ul>
${todos.map(todo=>`
<li>
${todo.name} ${todo.completed?'√':'X'}
</li>`).join('')}
</ul> `
)
}
//map 返回的是数组 循环渲染有,所以用join 去除
- 模板字符串的高级运用
const user='Mary';
const topic='learn to use markdown'
//模板字符串加方法
function highLight(strings,...values){
//strings 输出的是:has commented on your topic
//values 输出的是:user和topic
}
const sentence= hightlight `${user} has commented on your topic ${topic} `
- 使用 DOMPurify.sanitize 来防止xss攻击,要引入 dompurify的purify.js
14 startsWith()、endsWith()
--String的扩展方法
- startsWith():表示参数字符串是否在原字符串的头部,返回布尔值
- endsWith():表示参数字符串是否在原字符串的尾部,返回布尔值
- 大小写敏感的
- 可传递第二个参数, 从第几个参数开始
let str='hello world';
str.startsWith('Hello') //true
str.endsWith('!') 、//true
//可传递第二个参数, 从第几个参数开始
str.startsWith('Hello',6)
15.repeat --String的扩展方法
-
repeat
方法表示将原字符串重复n
次,返回一个新字符串 - 可以应用于让字符串对齐
'x'.repeat(3) // "xxx"
'hello'.repeat(2) //hellohello
const id ='510300198007300366x'
const fan ='I love Laravist.'
function padder(string,length=25){
return `${'',repeat(Math.max(length-string.length,0))}${string}`
}
console.log(padder(id))
console.log(padder(fan))
16. for or循环遍历
支持数组、字符串等 但目前版本不支取对象的循环
Array.protype.frist=function() {
return this[0]
}
const fruits=['Apple','Bannana','Orange','Mango'];
fruits.describe='My favorite fruits';
for(let index in fruits ){
console.log(fruits[index] ) 会输出多余的值
}
for(let index of fruits ){
console.log(index ) 只输出数组内的值 可以 break
}
//迭代器
for(let [index,fruit] of fruits.entries() ){
console.log(index,fruit ) 只输出数组内的值 可以 break
}
17.set
数据结构
-
es6
提供了新的数据结构Set
。它类似于数组,但是成员的值都是唯一的,没有重复的值。
const s=new Set()
s.size //看s的长度
-
es6
可以进行数组去重
const s3=new Set(["a","b","b"])
//用扩展运算符,变成用逗号分隔的
const arr=[...s3]
-
set
内置方法- add(value):添加某个值,返回Set结构本身
- delete(value):删除某个值 返回一个布尔值,表示删除是否成功
- has(value): 返回一个布尔值,表示该值是否为Set的成员
- clear(value):清除所有成员,没有返回值
tips: set
的方法可以使用链式调用
-
set
遍历
const s5=new Set(['a','b','c'])
s5.forEach(value=>{
console.log(value)
})
18. 递归
//接口返回数据结构--是个数组
0: {menu_id: 92, menu_name: "微信公众号", menu_fid: 0, menu_val: "92_wechat_publicnumber"}
1: {menu_id: 1, menu_name: "学校管理", menu_fid: 0, menu_val: "1_school"}
2: {menu_id: 2, menu_name: "教师管理", menu_fid: 0, menu_val: "2_psychologist"}
3: {menu_id: 3, menu_name: "学生管理", menu_fid: 0, menu_val: "3_counseling"}
4: {menu_id: 4, menu_name: "量表中心", menu_fid: 0, menu_val: "4_scale"}
5: {menu_id: 5, menu_name: "心理咨询", menu_fid: 0, menu_val: "5_advisory"}
6: {menu_id: 6, menu_name: "异常反馈", menu_fid: 0, menu_val: "6_abnormal"}
7: {menu_id: 7, menu_name: "活动管理", menu_fid: 0, menu_val: "7_activity"}
8: {menu_id: 8, menu_name: "在线求助", menu_fid: 0, menu_val: "8_help"}
9: {menu_id: 9, menu_name: "心理课堂", menu_fid: 0, menu_val: "9_classroom"}
10: {menu_id: 10, menu_name: "心理实验", menu_fid: 0, menu_val: "10_experiment"}
11: {menu_id: 11, menu_name: "订单管理", menu_fid: 0, menu_val: "11_order"}
12: {menu_id: 12, menu_name: "数据分析", menu_fid: 0, menu_val: "12_data"}
13: {menu_id: 14, menu_name: "学校列表", menu_fid: 1, menu_val: "14_school_list"}
14: {menu_id: 15, menu_name: "学校资讯", menu_fid: 1, menu_val: "15_information_list"}
15: {menu_id: 17, menu_name: "学校横幅", menu_fid: 1, menu_val: "17_schoolad_list"}
16: {menu_id: 18, menu_name: "老师列表", menu_fid: 2, menu_val: "18_psychologist_list"}
17: {menu_id: 19, menu_name: "老师导入", menu_fid: 2, menu_val: "19_teacher_import"}
18: {menu_id: 20, menu_name: "发送信息", menu_fid: 2, menu_val: "20_edit_notice"}
19: {menu_id: 21, menu_name: "学生列表", menu_fid: 3, menu_val: "21_counseling_list"}
20: {menu_id: 22, menu_name: "学生导入", menu_fid: 3, menu_val: "22_counseling_import"}
21: {menu_id: 23, menu_name: "发送信息", menu_fid: 3, menu_val: "23_edit_cou_notice"}
22: {menu_id: 24, menu_name: "用户统计", menu_fid: 3, menu_val: "24_user_count"}
23: {menu_id: 25, menu_name: "综合档案", menu_fid: 3, menu_val: "25_comprehensive_file"}
24: {menu_id: 26, menu_name: "量表分类", menu_fid: 4, menu_val: "26_scale_shape"}
25: {menu_id: 28, menu_name: "量表权限", menu_fid: 4, menu_val: "28_scale_rights"}
26: {menu_id: 29, menu_name: "测评查询", menu_fid: 4, menu_val: "29_assess_search"}
//根据fid生成带孩子的二级树形结构--只有二级就简单点
getJsonTree (data, fId) {
let itemArr = [];
data.forEach(item => {
const { menu_id = 0, menu_fid = 0, menu_name = '', menu_val = '' } = item || {}
if (menu_fid === fId) {
let newNode = {
id: menu_id,
label: menu_name,
value: menu_val,
checkAll: false,
checkedOptions: [],
options: this.getJsonTree(data, menu_id) //第一个是0写死的id找不到父亲的,找到的都是当父亲的,第二个就是把自己的id当做是fid 去找它的孩子
};
itemArr.push(newNode);
}
})
return itemArr;
},
// 孩子的数组,特殊情况就用这个来代替 this.getJsonTree函数 (只限于二级)
getChild (data, fId) {
let itemArr = [];
data.forEach(item => {
const { menu_id = 0, menu_fid = 0, menu_name = '', menu_val = '' } = item || {}
if (menu_fid === fId) {
let newNode = {
label: menu_name,
value: menu_val,
};
itemArr.push(newNode);
}
})
return itemArr;
},
19. 对象字变量的扩展
es6 对象 变的更加强大
const name='Lara'
const age=2
const keys=['name','age']
const values=['lara',2]
const laravist={
[keys.shift()]:values.shift(),
[keys.shift()]:values.shift(),
[keys.shift()]:values.shift(),
}
20. promise
- 解决回调地狱
const p=new Promise(reslove,reject)=>{
setTimeout()=>{
reject(Error('Error'))
}
})
p.then(data=>{console.log(data)})
.catch(err=>{console.log(err)})
tips: 在reject中使用Error返回错误信息时,使浏览器正确定位到是reject那行发出的错误信息,而不是catch那行。
- Promise all 同时调用多个promise
const userPromise=new Promise((resolove,reject)=>{
setTimeout(()=>{
reslove(['mojom','vanpe'])
},2000)
})
const moviePromise=new Promise(()=>{
setTimeout(()=>{
reslove({name:"摔跤",rat:9.2})
},500)
})
Promise.
all([userPromise,moviePromise])
.then(responses=>{
console.log(responses)
})
.catch(responses=>{
console.log(responses)
})
tips:Promise all
当以上所有promise
都正确执行时,才返回正确的,否则会被catch
捕获。
-
Promise race
同时调用多个promise
当第一个调用的promise 正确执行时就在then中返回结果,不然就在catch中返回
21. Symbol
-
Symbol
是js的第七种数据类型, 其他6种:Undefined、Null、Boolean、Number和String,复杂的数据类型Object - 主要用来标识唯一key,由于当前属性不能用普通的方法进行遍历,所以可以当私有属性,对于key值会重复的场景就可以使用Symbol来对其唯一标识
const peter=Symbol('peter');
const student=Symbol('student');
//以下nina两个key值重复,所以用Symbol来标识
const classRoom={
[Symbol('lily')]:{grade:60,gender:'female'},
[Symbol('nina')]:{grade:60,gender:'female'},
[Symbol('nina')]:{grade:60,gender:'female'},
}
//for 循环不能遍历
for(let key of classRoom){
console.log(key) //输出[]
}
//另类遍历的方法
const syms=Object.getOwnPropertySymbols(classRoom).map(sym>classRoom[sym])
console.log(syms)
tips:classRoom[sym] 不能改成 classRoom.sym 这里会执行 classRoom['sym']但classRoom数据里并没有sym这个属性名
22. eslint
代码规范
-
eslint
会找当前目录的.eslintrc.js文件,如果找不到就往上级找 - 全局使用某个对象 比如 Vue 可以使用,可不对eslint的规则进行配置
/* globals Vue */
.eslintrc.js 文件中
globals: {
Yx: true
}
- 禁用某条规则
/* eslint-disable no-new */
- 开启某条规则
/* eslint-enable no-new */
- eslint 可以使用外部的插件
{
"plugins":["markdown"]
}
23 Reflect.ownKeys()
静态方法 Reflect.ownKeys() 返回一个由目标对象自身的属性键组成的数组。
const object1 = {
property1: 42,
property2: 13
};
var array1 = [];
console.log(Reflect.ownKeys(object1));
// expected output: Array ["property1", "property2"]
console.log(Reflect.ownKeys(array1));
// expected output: Array ["length"]
24. parseInt
转化为整形
//可以使用+也可隐士转化为数字
+'243'
25. class
- 方法名可以通过计算属性命名并可进行调用
- 不能进行变量提示,必须先声明再引用
let methodName = 'info'
class User {
constructor(name,age){
this.name=name;
this.age=age;
}
[methodName](){
console.log(1)
}
}
- 继承
class student extends User {
constructor(name,age){
super(name);
/*super的作用及原理
作用 :继承父类,等同于User.call(this,name)调用基类的构造函数
原理: student.prototype=new User()
类的原型被改变了,但是类的原型的构造函数的也改变了,要指定构造函数为student
student.prototype.constructor =student
*/
this.age=age;
}
}
- 扩展内置对象
为Array 添加自己的方法
class Movie extends Array {
constructor(name,...items){
super(...item)
this.name=name
}
add(movie){
//
this.push(movie)
}
}
const movies=new Movie('favorite movies',
{name:'rr1',score:8.7},
{name:'rr2',score:99.7},
{name:'rr3',score:91.7}
)
movies.add() //添加内容
26. Iterator
遍历器(迭代器)
const colors=[1,2,3]
const iterator= colors[Symbol.iterator]()
iterator.next()
/*输出*/
Object{value:1,done:false }
iterator.next()
/*输出*/
Object{value:2,done:false }
iterator.next()
/*输出*/
Object{value:3,done:false }
iterator.next()
/*输出*/
Object{value:undefined,done:true}
tips:
当done为true时遍历就结束了,输出一个方法时有包含Iterator,就是遍历器了,比如数组的 keys,entries,
values
- 遍历器内部原理
//实现一个Array.values
Array.prototype.values=function(){
let i = 0;
let items = this;
return{
//返回一个函数,形成闭包,可以进行累加
next(){
const done =i > = items.length;
const value = done?undefined:items[i++]
return{
value,
done
}
}
}
}
for of 的运行机制
for(const itme of color){}
//of调用,colors[Symbol.iterator]() 这个属性。 并把里面的value值赋值给color
27. generator (可不学,直接看async、await)
- generator是生成器,可以开始、暂停 ,最终生成的是个遍历器(迭代器);
function* listColors(){
let i=0;
yield i; //返回
i++;
yield i;
i++;
yield i;
}
//生成了遍历器
const colors=listColors();
colors.next()
/*输出*/
Object{value:0,done:false}
colors.next()
/*输出*/
Object{value:1,done:false}
colors.next()
/*输出*/
Object{value:2,done:false}
colors.next()
/*输出*/
Object{value:undefined,done:true}
const colors=[1,2,3]
function* loop(repos){
console.log(colors)
for(const repo of repos){
yield repo
}
}
const repoGen=loop(repos)
- 在项目中的实际应用(异步转同步)
//进行ajax方法
function ajax(url){
axios.get(url).then( res =>
执行成功后再执行下一步
userGen.next(res.data)
)
}
//生成器
function* steps(){
const users = yield ajax('https://api.github.com/users');
const firstUser = yield ajax(`https://api.github.com/users/${users[0].login}`)
const followers = yield ajax(firstUser.followers_url);
}
const userGen =steps()
userGen.next()
28.proxy
- 添加、重写 对象上的默认方法,格式化默认值。
const person = {name:'laravist',age:2000};
const personHandle={
//获取值
get(target,key){
//target:原目标; key:键值
//把属性值都转换成大写
return target[key].toUpperCase();
}
//设置值
set(target,key,value){
//去掉空格
if(typeof value==='string'){
target[key]=value.trim()
}
}
}
const personProxy = new Proxy(person,personHandle)
personProxy.name=' codecasts '
console.log(personProxy.name)
// 输出:CODECASTS --去去掉空格并转换成大写
- 实际应用
// 安全挟持
const safeHandler = {
set(target,key,value){
//找相类似的键
const likekey = Object.keys(target).find(k=>k.toLowerCase()===key.toLowerCase());
//如果找到就不让其赋值
if(!(key in target) && likekey ){
throw new Error(` Oops! looks like we already have a property ${key} but with the case of ${likeKey}`)
}
}
}
const safetyProxy=new Proxy({id:2}, safeHandler);
safetyProxy.ID=5 /*会报错,赋值已经被挟持了只要和id这个键值相类似的 比如 Id ID iD 都不让赋值*/
29.set
- 唯一的数组
- 不能通过索引值来获取值
// 定义一个set
const colors=new Set()
colors.add(4)//添加元素
colors.delete(4)//删除
colors.has(4)//检验是否存在
/*可以用正常的数组循环方式进行循环*/
for(let color of colors ){
console.log(color)
}
colors.forEach(item=>{
console.log(item)
})
- 可通过扩展运算符转换成数组
const colorsSet=new Set([1,2,3,4,4,4,4])
const colorsArr=[...colorsSet]
// 或者可以通过array.from() 来进行转换
30.WeakSet
- 保存值的类型只能是对象
- 没有set一样的clear方法,有自动清理功能、防止内存泄漏
- 不能对他进行遍历
const {
jelly:{name:'jelly',age:20},
mary:{name:'mary',age:25}
} ={}
const weakPeople=new WeakSet([jelly,mary])
console.log(weakPeople)
mary=null
console.log(weakPeople)
// mary 这个属性值就不见了, 如果用数组来存储,这个值还是存在的
- 应用:单例属性,引用的追踪
31.map
- 可以和数组一样进行循环和遍历
- 可以和set一样使用相类似的api
const people = new map();
people.set('jelly',23)
people.has('jelly') //检验是否含有jelly这个值
people.delect('jelly') //进行删除
people.clear()全部删除
//对 fruits 进行赋值
const fruits = new Map([['apple',6],['banana',5]])
//用forEach和解构的方式来遍历map
fruits.forEach([key,value]=>{
console.log(key,value)
})
//如果不用解构方式,他返回的值是一整个是数组
- 应用场景: 存储对象的信息,而不单单是这个对象
32.weakMap
- 和weakSet 类似不能进行循环,也没有clear方法,没有size
- 保存的key值只能是对象
- 防止内存泄漏
const jelly = {name:'jelly'};
const miffy = {name:'miffy'}
const strong= new Map();
const weak=new WeakMap();
strong.set(jelly,'jelly is the best!')
weak.set(miffy,'miffy is the 2nd best!')
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。