js基础(4)
15、正则
正则就是一个规则,用来处理字符串的规则
1、正则匹配
编写一个规则,验证某个字符串是否符合这个规则,正则匹配使用的是test方法2、正则捕获
编写一个规则,在一个字符串中把符合规则的内容都获取到;正则的exec、字符串的split、replace、match等方法都支持正则
正则的创建方式以及区别
1.字面量方式:var reg=/\d/;
2.实例创建方式 var reg=new RegExp('a','b');支持两个参数,第一个为元字符,第二个是修饰符
<font color=red>两种创建方式的区别:<font>
1,字面量方式中出现一切字符都会按找元字符来解析,所以不能进行变量值的拼接,而实例创建的方式是可以的。
2,字面量方式中直接写\d就可以,而在实例创建方式中需要把它转义;只需要在\d之前再加一个\即可。即\\d
正则的元字符和修饰符
任何一个正则都是由
元字符
和修饰符
组成的
修饰符
ignoreCase(i):忽略大小写;
global(g):全局匹配;找到所有的匹配而不是只找到第一个匹配
multiline(m):多行匹配;
元字符
代表出现次数的量词元字符
* : 出现零到多次
? : 出现0次或1次
+ : 出现1到多次
{n} : 出现n次
{n,} : 出现n到多次
{n,m} : 出现n到m次
具有特殊意义的元字符
\ : 转义字符(两个\\就表示两个\\)
^ : 以某一个元字符开始,不占位置。(只对字符串的开头起作用)
$ : 以某一个元字符结尾,不占位置。(只对字符串的末尾起作用)
. : 除了\n(换行符)以外的任意字符
() : 分组
x|y : x或y中的一个
[xyz] : x或者y或者z中的一个
[^xyz] : 除了x y z的任何一个字符
[a-z] : a-z之间的任何一个字符
[^a-z] : 除了a-z的任何一个字符
\d : 一个0-9之间的数字
\D : 匹配一个非数字字符
\b : 一个边界符:“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”,单词左右两边是边界,-的左右两边也是边界,所以会把zhu-feng-pei-xun当作4个单词,而我们想把它当成一个单词
\n : 匹配一个换行符。
\s : 匹配一个空白字符 (空格,制表符,换页符)
\w : 数字,字母,下划线中的任意一个字符
?: : 只匹配不捕获
?= : 正向预查
?! : 负向预查
正则中[]的特殊含义
1、里面出现的元字符一般都是代表本身意义的但是有一些依然表示符号本身的意思如[\d\w]
2、中括号中出现'-'代表两种意思
- [a-z0-9]:- 代表范围连接符
- [a-z_0-9-]:如果-的左右两边任何一边没有内容那么’-‘代表本身的意思。
3、[]中出现的两位数不代表本身意思,而是以一位数来解析,如[18]代表1或8两个数字中的一个;
正则中()的作用
提高优先级
正则中的分组,也可以理解为一个大正则中的一个正则(包起来的部分是个一个整体);我们可以使用小括号改变一些默认的优先级。如(x|y)
分组引用
(\d)\1:\1代表引用和前面相同的数字;
分组捕获
正则捕获的时候不仅可以把大正则匹配的内容捕获到,而且也会把小括号中的内容捕获到
常用的正则表达式编写
注意:正则中如果同时加入了^和 $表示只能是xxx,不加的话表示包含xxx,而且^只对字符串的开头起作用,$只对字符串的结尾起作用;
reg= /^-?((\d)|([1-9]\d+))(\.\d+)?$/ 有效数字正则
reg=/^1\d{10}$/ 手机号验证
//=>/^[\u4E00-\u9FA5]$/ 中文汉字的正则
姓名的正则:
reg = /^[\u4E00-\u9FA5]{2,10}$/;
reg = /^[\u4E00-\u9FA5]{2,10}(·[\u4E00-\u9FA5]{2,10})?$/;
邮箱正则
/*
* 以数字字母下划线开头
* @前面可以是 数字、字母、下划线、-、. 这些符号
* 不能把 -和. 连续出现,出现一次后面必须跟数字字母下划线
*
* @后面的部分支持
* 企业邮箱
* .com.cn 多域名情况
*/
reg=/^\w+((-\w+)|(\.\w+))*@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/
身份证:
前六位:省市县
接下来八位 出生年+月+日
接下来的两位:没什么用
倒数第二位数字 奇数为男,偶数为女
reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(\d|X)$/;
正则的捕获
exec
正则的捕获:reg.exec(str)
捕获的内容是一个数组,数组中的第一项是当前正则捕获的内容,如果有小括号(),则第二项捕获的是小括号里的内容。
- index:捕获内容在字符串中开始的索引位置。
- input:捕获的原始字符串。如果没有匹配的返回null;
1、(懒惰性
)捕获的特点:执行一次捕获一次,每次执行exec只捕获第一个匹配的内容。
为什么会存在懒惰性?
- 每个正则都自带lastIndex属性:正则每一次捕获在字符串中开始查找的位置,默认值为0;
我们手动修改lastIndex的值也不会影响exec每次开始查找的索引。
如何解决只捕获第一个匹配(懒惰性
)?
在正则末尾加一个修饰符"g"
原理:
修改lastIndex的值为上一次捕获内容之后的第一个字符的索引;加了g之后能保证下次捕获第二个匹配的内容;
通过加g 每次捕获结束后,lastIndex的值都会改变成最新的值,下次捕获从最新的位置开始找。如果没有找到符合的,则返回null;
如何解决exec执行一次捕获一次?
1、
//我的方法
RegExp.prototype.myExec=function(){
var str=arguments[0]||'',ary=this.exec(str),ary1=[];
if(this.global!==true)return ary;
while(ary){
ary1=ary1.concat(ary);
ary=this.exec(str);
}
return ary1;
}
//老师的方法
RegExp.prototype.myExec=function () {
var str=arguments[0]||'', ary=this.exec(str),result=[];
if(!this.global)return ary;
while (ary){
result.push(ary);
ary=this.exec(str);
}
return result;
}
###match
>用字符串方法方法实现多次捕获
str.match(reg)
>1、如果正则中加了修饰符g,那么执行一次就可以把所有匹配的结果捕获到
>2、如果不加g,那么只捕获第一次匹配的结果。返回一个数组,数组中是捕获的每一项;
`[局限性]`
>在分组捕获中,
>如果正则不加修饰符g,
> - 那么match和exec一样,可以捕获到小分组的内容。
>如果正则加了修饰符g
> - match只能捕获到大正则匹配的内容,而对于小分组匹配的内容是无法捕获到的;
`注意`:由于test和exec的原理相同,如果加了g,都是修改lastIndex值来匹配或者捕获,所以使用test和exec都会修改lastIndex的值,所以使用exec捕获之前最好不要使用test;
用test也可以捕获符合的字符串
reg.test(str);
RegExp.$1//只能捕获第1个小分组里面的内容
RegExp.$2//只能捕获第2个小分组里面的内容
RegExp.$3//只能捕获第2个小分组里面的内容
同reg.exec(str);也能使用RegExp.$1来捕获第1个小分组
RegExp.$1//只能捕获第1个小分组里面的内容
RegExp.$2//只能捕获第2个小分组里面的内容
###所有的支持正则的方法都可以实现正则的捕获(一般都是字符串的方法)
>字符串中常用的支持正则的方法:
>- match
>- replace
>- split:如果给的正则中包含小分组,那么会把小分组中的内容也捕获到;
>?:只匹配不捕获, 只能放在小分组的开头。如果加在分组中,那么只会匹配该分组中的内容,而不会捕获
>/(?:&|=)/
>计算是第几个分组的时候,我们从左向右找'('即可
var reg = /^-?(\d|([1-9]\d+))(\.\d+)?$/;//=>计算是第几个分组的时候,我们从左向右找 ( 即可
##replace(第二个参数支持直接在字符串中使用$1-9)
>替换字符串
>在不使用正则的情况下,每次执行只能替换第一个匹配的字符串,而且每次执行都是从字符串索引为0的位置开始查找
>`第一个参数为正则时`,正则捕获几次,就替换几次,换成函数也同理,正则捕获几次,函数就执行几次,函数中返回的是什么,就相当于把正则捕获的内容替换成什么。
>`第二个参数为函数时`,正则捕获几次,函数就执行几次,函数执行的时候还会默认的传入三个参数:
>context(捕获的内容)
>index(捕获内容在字符串中的开始索引)
>input(原始的字符串)
str.replace(/珠峰/g,function(context,index,input){
arguments[0]大正则匹配的内容
//如果正则中有分组的话
arguments[1]第1个小分组匹配的内容
arguments[2]第2个小分组匹配的内容
…………直到小分组的内容显示完
argument[n]每一次捕获的开始索引
argument[n+1]原始字符串
return 的是什么就会把正则每次捕获的内容替换成什么
})
需求:把每个单词的首字母替换为大写,zhu-feng-pei-xun当成一个单词
var str='my name is zhu-feng-pei-xun,i am 8 years old,i am qian duan pei xun no1!';
var reg=/[a-zA-Z-]+/g;
str=str.replace(reg,function () {
return arguments[0].slice(0,1).toUpperCase()+arguments[0].slice(1);
})
console.log(str);
###时间格式化字符串
方法1
var str = '2017-11-07 16:37:00';
//=>'2017年11月07日 16时37分00秒'
//=>使用正则实现
//1、执行一次捕获操作,得到需要的六个结果
var reg = /^(\d{4})-(\d{1,2})-(\d{1,2})\s+(\d{1,2}):(\d{1,2}):(\d{1,2})$/g;
// str = str.replace(reg, function () {
// var arg = arguments;
// return arg[1] + '年' + arg[2] + '月' + arg[3] + '日 ' + arg[4] + '时' + arg[5] + '分' + arg[6] + '秒';
// });
str = str.replace(reg, '$1年$2月$3日 $4时$5分$6秒');//=>$1等价于第一个分组中获取的内容,类似于上面代码的arg[1]
console.log(str);//=>'2017年11月07日 16时37分00秒'
方法2
String.prototype.myformate=function () {
var ary=this.match(/\d+/g);
var temp=arguments[0]||'{0}年{1}月{2}日 {3}时{4}分{5}秒';
var reg=/\{(\d)\}/g;
temp=temp.replace(reg,function () {
var value=ary[arguments[1]]||'0';
return value.length<2?'0'+value:value;
})
return temp;
}
##16、OOP(面向对象编程思想)
###单例模式
>我们把对象数据类型实现`把描述同一件事务的属性或者特征归纳汇总在一起,以此避免全局变量冲突问题`的方式和思想叫做:单例设计模式;
>把描述同一件事务的属性或者方法存放在某一个命名空间下,多个命名空间中的属性和方法是互不干扰的
```javascript
//=>单例模式
//1、singleton不仅仅是对象名了,在单例模式中,singleton称之为 “命名空间(nameSpace)”
var singleton={
xxx:xxx,
...
};
var singleton={
nameSpace1:{
xxx:xxx,
...
},
nameSpace2:{
xxx:xxx,
...
}
...
};
使用单例模式实现模块化开发
模块化开发:把一个复杂页面按照具体功能划分成几大块,然后由不同的人分别去开发,这种模块划分的思想就是模块化开发功能。
//=>项目主管(开发人员):公共模块
var utils={
trim:function(){}
};
//=>陆永勇:搜索模块
var searchModel={
submit:function(){
utils.trim();
},
...
};
//=>唐元帅:天气模块
var weatherModel={
setWeather:function(){},
...
};
//=>陈金广:频道模块
var channelModel={
show:function(){
//=>在当前的命名空间下调取其它命名空间的方法:指定好对应的命名空间名字即可,使用 [NameSpace].[property] 就可以操作了
searchModel.submit();
//=>调取本模块中的一些方法,可以直接使用THIS处理即可:此方法中的THIS一般都是当前模块的命名空间
this.setChannel();
},
setChannel:function(){},
...
};
channelModel.show();
高级单例模式
基于JS高阶编程技巧
惰性思想
,来实现的单例模式,并且可以把一些常用的设计模式(如:命令模式、发布订阅模式、promise设计模式等)融合进来,最后清晰的规划我们的业务逻辑代码,方便后期二次开发和维护,这种设计思想综合体就是高级单例模式,也是最常用的。
var serchModel=(function(){
function submit(){
}
return {
init:function(){
this.submit();
}
}
})();
searchModel.init();
对象、类、实例
对象:万物皆对象
类:对象的具体细分(按照属性或特性细分的一些类别)
实例:某一个类中的具体事物
JS常用的内置类
数据类型的类
Number:每个数字或者NaN是它的一个实例
String:字符串类
Boolean:布尔类
Null
Undefined:浏览器屏蔽了我们操作null和udnefined这个类
Object:对象类,每个对象数据类型都是它的实例
- Array:数组类
- RegExp:正则类
- Date:日期类
Function:函数类,每个函数都是它的一个实例
元素对象或者元素集合的类
HTMLCollection:元素集合类
- getElementsByTagName()
- getElementsByClassName()
- querySelectorAll
NodeList:节点集合类
- childNodes
- getElementsByName()
HTMLDivElement
HTMLElement
Element(标签类)
Node(节点类,Element只是其中的一个元素节点)
[图片上传失败...(image-45ab-1541561653878)]
为什么getElementById的上下文只能是document?(即getElementById为什么只能通过document来调用)?
因为只有在Document这个类上才有getElementById这个方法,其他类上(如:HTMLDivElement类)没有getElementById这个方法,而document是HTMLDocument这个类的一个实例,能通过document._proto_._proto_找到Document这个类的原型上公有的getElementById方法。
[图片上传失败...(image-585c72-1541561653878)]
基于面向对象创建数据
创建方式:两种
1.字面量创建方式
- var obj={};
2.实例创建方式(构造函数方式)
- var obj=new Array();
1、对于引用数据类型来说,两种创建方式是大致相同的,只不过,两种方法创建的语法不同。
两种创建方式在核心意义上没有差别,都是创建Array这个类的一个实例,但是在语法上是有区别的
1、字面量创建方式传递进来什么,都是给数组每一项加入的内容
2、构造函数创建方式
new Array(10):创建一个长度为10的数组,数组中的每一项都是空
new Array('10'):如果只传递一个实参,并且实参不是数字,相当于把当前值作为数组的第一项存储进来
new Array(10,20,30):如果传递多个实参,不是设置长度,而是把传递的内容当做数组中的每一项存储起来
2、对于基本数据类型来说,字面量方式创建出来的结果和实例方式创建出来的结果是有一定区别的,从严格意义上来讲,只有实例创建出来的结果才是标准的对象,数据类型值也是标准的基本数据类型,也是标准的内置类的实例;对于字面量方式创建出来的结果是基本数据类型的值,不是严格的实例,但是由于JS的松散特点,导致了可以使用 内置类.prototype上提供的方法;
构造函数设计模式
使用构造函数方式,主要是为了创建类和实例的,也就是基于面向对象思想来实现一些需求
在JS中,当我们使用new xxx()执行函数的时候,此时的函数就不是普通的函数了,而是变为一个类,返回的结果叫做当前类的实例,我们这种new xxx执行的方式称之为
构造函数设计模式
function fn(){
}
new fn();
构造函数执行时new都干了些什么?
在new Fn(),执行的时候,是先把函数执行了,也就是后面的Fn()先执行,形成一个私有作用域,形参赋值变量提升,在变量提升完了之后,new操作符才起了作用,这个时候,浏览器开始创建一个新的对象,让Fn中的this指向这个新创建的对象,然后让这个对象的_proto_指向Fn.prototype,然后JS代码才开始继续往下执行,开始往新创建的对象当中添加每个实例私有的属性和方法。JS代码执行完成后,会默认返回当前创建的这个对象。
[图片上传失败...(image-348e4d-1541561653878)]
普通函数执行与构造函数执行的区别
构造函数执行的时候,也是先形成一个私有作用域,形参赋值,变量提升,在代码从上而下执行之前,构造函数有特殊的操作:
浏览器会在当前的作用域中默认创建一个对象数据类型的值,并且会让当前函数中的this指向创建的这个对象。
然后JS代码再执行,代码执行完成后,即使函数中没有写return,在构造函数模式中:浏览器会默认的把创建的对象返回到函数外面
<font color=red>总结:</font>
- 构造函数执行期间,既具备函数执行的一面,也同时具备自己独有的操作:在构造函数执行期间,浏览器会默认创建一个对象,这个对象就是当前这个构造函数(类)实例,函数执行完成后,浏览器会默认的把这个实例返回。所以new Fn()执行,Fn是一个类,
返回的结果
就是Fn这个类的一个实例
构造函数执行后面的‘()’问题
构造函数执行如果不需要传递参数,函数后面的()可省略,如
new Fn()
可写为new Fn
;
注意
:
- 如果要在new Fn之后直接调用实例的方法,则必须要加小括号,即必须写成
new Fn().方法名
构造函数模式的返回值问题
构造函数模式中默认返回值是当前的实例,如果有return,返回分2种情况:
1、return 后面是一个基本数据类型的值,当前实例是不变的,例如return 100;我们的返回值还是当前类的实例;
2、return 后面是一个引用数据类型的值,当前实例会被返回的值给替换掉例如return {name:"珠峰"}我们的返回值就不再是当前类的实例了,而是对象 {name:"珠峰"};
A instanceof B
检测某一个实例是否属于这个类, 判断A实例是否属于B类
attr in object
检测attr是否是object的属性,不管是私有属性还是公有属性只要存在,用in来检测都是true
a.hasOwnProperty(attr)
attr是否是A实例的私有属性
对象数据类型的
- 普通对象
- 数组
- 正则
- Math数学函数
- 一般类的实例
- 函数的prototype属性(Function.prototype除外,它是函数数据类型的)
- 实例的_proto_属性
函数数据类型的
- 普通函数
- 所有的类(内置类和自定义类都是)
原型链模式
基于构造函数模式的原型链模式解决了方法或者属性公有的问题,把实例之间公有的属性和方法写在当前类的prototype属性上;
1、每一个函数数据类型都有一个天生自带的属性:prototype(原型),并且这个属性的属性值是一个对象数据类型的值(<font color=red>Function.prototype是函数数据类型的,但是它没有prototype属性(Function.prototype.prototype是undefined)。Array.prototype是一个数组</font>),浏览器默认为其开辟一个堆内存;
2、在浏览器给prototype开辟的这个堆内存中,浏览器天生给它加了一个constructor属性(构造函数),属性值是当前函数(类)本身;
3、每一个对象数据类型(普通对象、数组、正则、实例、protoype..)也天生自带一个属性:_proto_,属性值指向当前实例所属类的原型(prototype);
(IE中屏蔽了对象的__proto__属性,但是确实有,只是不让我们使用而已)
4、Object是JS中所有对象数据类型的基类(最顶层的类);
原型链模式中的this
原型模式中的this分两种情况:
1、 在类中this.xxx=xxx;this->当前类的实例
2、 原型链中提供的私有(公有)方法中的this问题:
总结
:看执行的时候"."前面是谁this就是谁。具体操作步骤如下
- 1、需要先确定this的指向(this)
- 2、把this替换成对应的的代码
- 3、按照原型链查找的机制,一步步的查找结果
如何往原型中批量添加属性和方法
重构原型
让某个构造函数的原型指向我们自己开辟的堆内存,但是自己开辟的堆内存当中是没有constructor属性的,所以要往自己开辟的堆内存中添加constructor属性,属性值为当前构造函数本身;
缺点:重构原型后,会导致之前添加的属性和方法都没有了,只能使用重构之后添加的属性和方法;
注意:
- 不要忘了重构之后要添加constructor属性指向当前构造函数;
- 内置类的原型不能重构,浏览器不允许我们这么做;
- 重构原型之前生成的实例的
__proto__
属性值依然指向重构之前的原型,而不是重构之后的原型,只有在重构原型之后生成的实例的__proto__
属性值才指向新的原型;
17、面向对象的理解和学习继承
面向对象编程
它是一种编程思想,让我们基于类和实例的概念来编程开发和学习。
类的继承、封装和多态
类的封装
把实现一个功能的代码,封装在一个函数当中,以后再想实现这个功能,只需要执行这个方法即可,不需要重复的编写代码,减少了页面中的代码冗余度,提高了代码的重复利用率,实现了
低耦合高内聚
的思想
类的多态
一个类的多种形态:重载、重写;
[重载]
方法名相同,参数不同,叫做方法的重载
JS中没有严格意义上的重载,如果方法名相同,那么就会保留最后一个方法,和传递的参数没有关系;
JS中的重载:根据传递的实参不同,来实现不同的功能。可以把这种方法叫做重载。
[重写]
JS中的重写:子类重写父类的方法
类的继承
子类继承父类的一些属性和方法
原型继承
<font color=red>原理</font>:让子类的原型指向父类的一个实例(很少单独使用)
Son.prototype=new Parent();
/继承父类之后,需要手动添加constructor属性
Son.prototype.constructor=Son;
<font color=red>特点</font>:它是把父类中私有+公有的属性和方法的都继承到了子类的原型上(子类公有的)
<font color=red>核心</font>:原型继承是让子类和父类之间增加了原型链的连接关系,以后子类的实例想要父类中的方法,需要利用原型链查找机制一级一级向上查找来使用
<font color=red>原型继承存在的问题:</font>
- 1、子类的原型指向了父类的一个实例,如果实例的某个属性的值是引用数据类型,那么我生成子类的一个实例f,我通过f来修改子类公有的这个引用数据类型的值,那么子类的其他实例也会受影响;
- 2、不能向父类的构造函数传递参数。如果父类给其实例定义的私有属性的值跟传入的参数有关系,那么子类继承过来之后,所有子类实例的公有属性的值都是一样的,
call继承
<font color=red>原理</font>:把父类当作普通函数在子类中执行,修改函数中的this为子类的实例。
function A(){
this.x=100;
}
function B(){
//一般都把call继承放在子类函数体中的第一行,这样做的好处就是子类私有的可以替换掉继承过来的结果;
A.call(this);
this.y=200
}
<font color=red>特点</font>:把父类私有的属性和方法克隆一份一模一样的作为子类私有的属性(父类公有的无法继承)
寄生组合式继承(call继承+Object.create继承)
<font color=red>总结</font>:寄生组合式继承完成了一个需求
- 子类公有继承了父类公有的
- 子类私有继承了父类私有的
- 而且子类还可以扩展自己的实例公有的属性和方法(扩展到创建的空对象上),而不影响到父类
Object.create():创建一个空对象,并把传入的参数当作空对象的原型;
<font color=red>原理</font>:把父类当作函数在子类中执行,修改函数中的this为子类的实例。用Object.create()创建空对象,并把传入的参数当作空对象的原型,把子类的原型指向空对象的原型。
function A(){
this.x=100;
}
function B(){
this.y=200
}
//创建一个空对象,让传入的参数当作空对象的原型,然后让B的原型指向这个空对象;
B.prototype=Object.create(A.prototype);
//为了保证构造函数的完整性,我们要给子类的原型手动设置constructor属性值
B.prototype.constructor=B;
//Object.create在IE低版本不兼容,所以我们要自己模拟一个create方法。
Object.myCreate=function(){
var Fn=new Function();
Fn.prototype=argument[0];
return new Fn();
}
//以下写法兼容所有浏览器;
B.prototype=Object.myCreate(A.prototype);
<font color=red>特点</font>:把父类私有的给子类私有,把父类公有的给子类公有。
ES6中的类和继承
ES6中的继承相当于寄生组合式继承
class Father{//定义一个类;
constructor(name,age){//构造函数定义实例的私有属性
this.name=name;
this.age=age;
}
getName(){//公有的函数和方法
console.log(this.name,this.age);
}
static like(){//static后写Father当做对象时的私有属性
console.log("我是Father函数的私有属性")
}
}
class Son extends Father{//子类Son继承父类Father
constructor(name,age,color){
注意:super()只能用在子类的构造函数之中,用在其他地方会报错
super(name,age);//super中的参数相当于把父类当作普通函数执行时传递给普通函数的参数
//下面为子类的私有属性
this.color=color;
}
//下面为子类公有的方法
getColor(){
console.log(this.color)
}
static my(){//static后写的是把Son当做对象时的私有属性
console.log("我是Son函数的私有属性")
}
}
关于super的两种情况:
注意,使用super的时候,必须显式指定是作为函数、还是作为对象使用,否则会报错。
1、作为函数执行时:
只能在子类的构造函数中第一行写super(),此时就相当于Father.call(this);否则会报错(比如在子类的公有方法中执行super);
2、作为普通对象调用时
在子类的普通方法中(公有方法或私有方法),super指向父类的原型,相当于Father.prototype,而通过super.getName(),getName方法中的this依然是子类的实例,而不是super。
在子类的静态方法中(把子类当作对象添加的方法),指向父类。
为什么要手动修改constructor?
var a,b;
(function(){
function A (arg1,arg2) {
this.a = 1;
this.b=2;
}
A.prototype.log = function () {
console.log(this.a);
}
a = new A();
b = new A();
})()
a.log();
// 1
b.log();
// 1
a,b,他们同为类A的实例。因为A在闭包里,所以现在我们是不能直接访问A的,那如果我想给类A增加新方法怎么办?
这个时候就可以通过a.constructor.prototype 来给其所属类添加公有的属性和方法了
18、JS中的DOM盒子模型与一个盒子垂直水平居中
CSS中的盒子模型
css中的盒子模型有两种,分别是 ie 盒子模型和标准 w3c 盒子模型
w3c中的CSS盒子模型
[图片上传失败...(image-e9c57d-1541561653878)]
width、height:不是盒子的宽高,而是盒子中内容的宽度和高度
盒子的宽=width+padding(left+right)+border(left/right);
CSS3盒子模型与IE中的盒子模型
[图片上传失败...(image-4b0dea-1541561653878)]
width和height不仅仅是内容的宽度,而是代表整个盒子的宽度(已经包含了padding和border),以后修改的padding或者border,只会影响盒子中内容的高度,盒子的大小不会改变
css3中的盒子模型属性box-sizing
box{
box-sizing:border-box;
}
box-sizing的可能值:content-box(default),border-box,inherit(从父类继承)。
content-box:border和padding不计算入width之内,盒子的宽度=width+padding+border
border-box:border和padding计算入width之内,盒子的宽度=width
如何选择<font color="red">w3c盒子模型</font>?
在网页的顶部加上 <font color="red">!DOCTYPE</font> 声明。假如不加 doctype 声明,那么各个浏览器会根据自己的行为去理解网页,即 ie 浏览器会采用 ie 盒子模型去解释你的盒子,而火狐会采用标准 w3c 盒子模型解释你的盒子,所以网页在不同的浏览器中就显示的不一样了。反之,假如加上了 doctype 声明,那么所有浏览器都会采用标准 w3c 盒子模型去解释你的盒子,网页就能在各个浏览器中显示一致了。
JS盒模型属性
通过JS盒子模型属性获取到的结果都是不带单位的,而且只能是正 数(会默认的进行四舍五入),而且只有scrollTop和scrollLeft可读写,其他都是只读属性;
clientWidth、clientHeigit(内容宽度+padding)
(只读属性,每次访问都会重新计算,最好用变量保存)
clientWidth:内容宽度Width+左右填充padding
clientHeight:内容高度height+上下填充padding
当前盒子可视区域的宽度和高度
可视区域:内容宽高+左右填充padding
和内容是否溢出,以及是否设置了overflow:hidden没有关系
document.documentElement.clientWidth || document.body.clientWidth:
- 获取当前浏览器可视窗口(一屏幕)的宽度
clientTop、clientLeft(盒子边框的高度和宽度)
clientTop:盒子上边框的宽度相当于border-top
clientLeft:盒子左边框的宽度相当于border-left
获取的就是边框的宽度
offsetWidth、offsetHeight(内容宽度+padding+border)
只读属性
offsetWidth:盒子宽度+pading+border
offsetHeight:盒子高度+pading+border
在clientWidth和clientHeight的基础上加上了左右或者上下边框的宽度,和内容是否溢出也没关系;
offsetParent、offsetLeft、offsetTop
offsetParent:当前元素的父级参照物(在同一个平面中,最外层的元素是里面所有元素的父级参照,和HTML层级结构没有必然的联系。一个页面中所有的父级参照物都是body。标准流中为body)
<font color=red>父级参照物可通过position: absolute relative fixed来改变;三个中任何一个都可以改变父级参照物,但是只能改变定位元素子元素的父级参照物,定位的元素的父级参照物不改变。<font>
**offsetLeft:当前元素距离其父级参照物的内边框的左偏移量 **
offsetTop:当前元素距离其父级参照物的内边框的上偏移量
在IE8当中,是从当前元素的外边框距离其父级参照物的外边框的偏移量
当前元素的外边框距离父级元素参照物内边框的偏移量,父级参照物默认为body
scrollWidth、scrollHeight、scrollTop、scrollLeft
[没有内容溢出时]
获取的结果和clientWidth、clientHeight是一样的
[有内容溢出的时候]
- scrollHeight:真实内容高度+上填充padding(因为内容溢出下padding已经计算了所以不用加)
- scrollWidth:真实内容宽度+左填充padding(因为内容溢出右padding已经计算了所以不用加)
scrollLeft/scrollTop:横向或纵向滚动条卷去的宽度/高度(只有这两个可读写)
存在最大最小值:最小为0,最大为卷去的高度/宽度 - 一屏幕的高度clientHeight
[图片上传失败...(image-6e371a-1541561653878)]
获取元素具体的样式值
1、ele.style.xxx
- 获取当前元素所有写在行内样式上的值(只有写在行内样式上的才能获取到)
2、getComputedStyle/currentStyle(IE8及以下使用currentStyle)都带单位;
- getComputedStyle(ele,当前元素的伪类一般为null),获取的是一个对象数据类型的值,如果需要获取某一项需要用点或者[]来访问获取结果的某个属性
- ele.currentStyle (currentStyle是元素的一个属性,而不是一个方法)获取结果也是对象数据类型的,获取某一项也要通过点或者[]来访问;
这两个属性获取的结果都是字符串;
如何让一个盒子水平垂直居中
1、用CSS
使用定位,需要知道盒子的具体宽高(兼容IE低版本)
position:absolute
top:50%
left:50%
margin:-高度的一半px 0 0 -宽度的一半px;
使用定位:不需要知道盒子的宽和高(不兼容IE低版本)
position:absolute;
top:0;
left:0;
right:0;
bottom:0;
margin:auto;
如何让一个宽度不固定的块级元素居中?
修改display为inline-block
父级的text-align:center
使用css3方法实现
position: absolute;
top:50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
2、用JS方法实现
首先让盒子先绝对定位,然后设置盒子的left和top值即可
left=(当前浏览器窗口的宽度-内容的宽度)/2+'px'
clientWidth- offsetWidth
left=((document.documentElement.clientWidth || document.body.clientWidth)-ele.offsetWidth)/2+'px'
top=(当前浏览器窗口的高度-内容的高度)/2+'px'
left=((document.documentElement.clientHeight || document.body.clientHeight)-ele.offsetHeight)/2+'px'
19、JS中的定时器与异步编程
setTimeout(fn,interval)
设定一个定时器,到达指定时间之后执行对应的方法(执行一次就结束了);
)
setInterval(fn,interval)
设置一个定时器,每间隔多少时间执行一次fn,直到定时器清除为止(执行很多次)
两个定时器的区别
共同点:
设置定时器时都会有一个返回值,代表当前是在浏览器中设置的第几个定时器(返回的是定时器的序号,不管是setTimeout还是setInterval,)只要遇到这两个其中一个,就算一个新的定时器,定时器的序号就会加1,即使清除了设置的定时器,也不会重新计算序号。
不同点:
setTimeout是隔一段时间之后执行一次函数
setInterval是每隔一段时间之后都执行一次函数;
用clearTimeout和clearInterval都可以清除两个中任何一个定时器,传入的参数为定时器的序号;
为什么我们要手动清除定时器?
定时器也是一个函数,函数执行完成后,返回了一个基本数据类型值(定时器的序列号)。没有产生堆内存的话就会在空闲时间被销毁掉,但是为什么setTimeout不会自动销毁?
因为当创建一个定时器的时候,浏览器会同时开启一个监听者,当setTimeout执行完成后,监听者始终知道这个定时器的地址,所以不能销毁,我们要手动去清除定时器。我们手动清除的时候其实就是在切断定时器和监听者之间的联系;这样定时器就会被销毁掉;
定时器中的this问题
不管在哪执行,定时器函数中的this是window;
var obj={
fn:function(){
//this:obj
setTimeout(function(){
//=>this:window 不管在哪执行,定时器中的this是window
},1000);
//=>想让定时器函数中的this是obj,使用bind方法改变为obj
setTimeout(function(){
//=>this:obj
}.bind(this),1000);
//也可以用变量保存的方式来改变this
var _this=this;
setTimeout(function(){
//=>_this:obj
_this.name
='xxx';
},1000);
setTimeout(()=>{
//=>this:obj 箭头函数中的this继承宿主环境(上级作用域中)的this
},1000);
}
};
obj.fn();
同步编程和异步编程
JS是单线程的,当前的任务没有完成,下面的任务是不进行处理的(同步的)
同步编程
当前的事情没有完成,继续处理当前的事情,只有当前的事件完成了,才会去做下一件事情。(JS中大部分都是同步编程的)如for循环
异步编程
规划一件事情,但不是当前马上去执行这件事,需要一定时间之后再执行,不会等到时间到了,任务执行完了,才继续完成下面的任务。而是把它放到等待任务队列中,同时开始计算等待时间,继续执行主任务队列中的任务,只有主任务队列中的任务都完成了,再到等待任务队列当中,看哪个先到时间了,就先执行哪个。如果主任务队列中的任务没完成,不会去执行等待任务队列当中的任务;
JS中的异步编程
在JS中的异步编程只有四种:
1、定时器都是异步编程的
2、所有的事件绑定都是异步编程、
3、Ajax读取数据的时候,我们一般都设置为异步编程
4、回调函数也是异步编程
定时器是有最小等待时间的,即使设置为0也不会马上执行,等待时间短的先执行;
同步异步编程的核心原理
JS中有两个任务队列(存放任务列表的空间就是任务队列)
1、主任务队列:同步执行任务;(从上到下执行)
2、等待任务队列:存放异步的任务;
<font color=red>原理</font>:规划一件事情,但不是当前马上去执行这件事,需要一定时间之后再执行,不会等到时间到了,任务执行完了,才继续完成下面的任务。而是把它放到等待任务队列中,开始计时,继续执行下面的操作,只有主任务队列中的任务都完成了,再到等待任务队列当中,看哪个先到时间了,就先执行哪个,如果都到时间了,那么就看哪个等待的时间短,就先执行哪一个。如果主任务队列中的任务没完成,不会去执行等待任务队列当中的任务;
JS中动画实现的原理
CSS3动画
在CSS3中提供了transition(过渡动画)/animation(帧动画)
优势:
性能好,实现起来简单,CSS能解决的动画绝对不用其他方式。
弊端:
不兼容大部分IE或者其他低版本浏览器(移动端的动画一般都是基于CSS3来完成的)
JavaScript动画
在JS中实现动画常用的有:
1、使用定时器驱动动画,
2、使用requestAnimationFrame来完成的动画
而所谓的canvas动画基本上也是基于这两种方案完成的(canvas本身是绘图)
Flash动画
非常早的动画处理方案,想要实现动画,需要把这部分制作成Flash影片,然后用Adobe Flash Player插件来播放;现在一些简单的DOM动画都告别了flash的时代,影片的播放也可以基于H5中的audio或者video完成;
JS中基于定时器的动画运动的两种形式
1、限定步长,不限制运动时间;
2、限定运动的总时间,不限制每一步走的长度;公式:
- time为定时器走一次的时间,zTime为运动总的时间
- 元素当前所在位置=(time/zTime*总路程+起始位置)
时间消耗测试
1、此方法只能测试运算时间在1毫秒以上的,
var startTime=new Date();
//需要测试消耗时间的代码
console.log(new Date()-startTime);
2、此方法可以测试运算时间在1毫秒以下的
console.time()
//需要测试消耗时间的代码
console.timeEnd()
20、JS中的回调函数
什么是回调函数
把一个函数当作实参值传递给另外一个函数,在另外一个函数中执行这个函数,这种处理机制就是回调函数机制;
什么时候用到回调函数?
凡是在某一个函数的某一个阶段需要完成某一件事情(而这件事情是不确定的),都可以利用回调函数机制,把需要处理的事情当作值传递进来
function fn(num,callBack){
typeof callBack==='function'?callBack():null;
//也可以使用以下默认方式(不过不严谨:可能传递的不是函数)
callBack&&callBack()
}
1、回调函数可以被执行多次;
2、还可以给callBack()传递参数值;
3、还可以把回调函数中的this进行修改;
4、我们还可以接收回调函数执行返回的值;
var ary=[12,23,34];
ary.sort(funciton(a,b){
/这里的function就是sort方法中的回调函数
})
回调函数中的this问题
回调函数中的 this一般都是window(严格模式下是undefined),原因:
我们一般执行回调函数的时候都是直接执行回调函数,没有指定执行主体,所以默认情况下都是window;
定时器中的this问题
只要不指明执行主体,定时器中的this就是window.(严格模式下也是window);
关于Each循环方法的封装
需求
兼容所有浏览器
类似于JQ中的EACH方法,我们需要支持对数组,类书组,纯对象的遍历任务
需要支持对原有数组的修改(回调函数中的返回值,可以修改原来数组中的某一项值)
在遍历中,通过回调函数返回值,来结束当前正在遍历的操作(回调函数中返回false,我们应该结束对数组的遍历)
21、ES6(JS新语法规范ES2016)基础知识及核心原理
(详细内容请参考阮一峰大神的的ES6入门:http://es6.ruanyifeng.com/#docs/intro)
使用Babel编译ES6
1、下载安装babel
npm install babel-cli -g
我们之所以可以使用babel命令,是因为在全局环境下会生成一些xxx.cmd的文件,而这里的xxx就是可以在doc窗口中执行的命令
执行babel命令后,可以完成一些编译或者其他任务,原因是执行babel命令后,会自动加载处理任务的文件;
配置.babelrc文件,安装在一些语言解析包
我们需要把.babelrc文件配置在当前项目的根目录下
注意:在电脑上不能直接创建没有文件名的文件,但是可以在webS中new->file来创建,或者使用命令创建
1、babelrc这个后缀名在某些ws中是不识别的,我们需要设置关联
2、在这个文件中编写内容:
{
"presets":[],//存的是我们编译代码时候需要依赖的语言解析包
"plugins":[]//存的是我们编译代码时候需要依赖的插件信息
}
3、安装依赖的语言解析包
在当前项目的根目录下安装
npm install babel-preset-latest
安装最新已经发布的语言标准解析模块
npm install babel-preset-stage-2
安装当前还没有发布但是已经进入草案的语言解析模块
4、完成最后.babelrc
{
"presets":[
"latest",
"stage-2"
],//存的是我们编译代码时候需要依赖的语言解析包
"plugins":[]//存的是我们编译代码时候需要依赖的插件信息
}
三、使用命令编译JS代码
基本上所有支持命令操作的模块都有一个命令
babel --help/babel -h 查看帮助
babel -V 查看版本号
babel -o 把某一个JS文件中的ES6代码进行编译
babel -d 把某一个文件夹中所有的JS文件中的ES6代码进行编译
babel -w 监听文件中代码的改变,当代码改变后,会自动进行编译
ES6增加的语法
let&&const
let
与var
的区别
let 变量名=变量值
使用let创建变量和使用var创建变量的区别
- 1、用var声明的变量会变量提升,用let声明的变量不会进行变量提升
用let创建变量
let xxx=xxx;
用let创建函数
let xxx=function(){}
创建自执行函数
;(function(){
})();
- 2、用let定义变量不允许在
同一个作用域中
重复声明一个变量(只要当前作用域中有这个变量,不管是用var还是用let声明的,再用let声明的话会报错:不能重复声明一个变量),但是可以重复定义(赋值)
let i=10;
let i=20;/会报错,
i=20;重复赋值不会报错
- 3、暂时性死区:在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。
if (true) {
// TDZ开始
tmp = 'abc'; // ReferenceError,报错之后下面都不会输出
console.log(tmp); // ReferenceError,报错之后下面都不会输出
let tmp; // TDZ结束
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
//下面也会报错出现TDZ
console.log(typeof x); // ReferenceError
let x;
//作为比较如果一个变量根本没有被声明,使用typeof反而不会报错。
console.log(typeof x);// "undefined"
- 4、ES6语法创建的变量(let)存在块级作用域
- [ES5]
window全局作用域
函数执行形成的私有作用域- [ES6]
除了有ES5中的两个作用域,ES6中新增加块级作用域(我们可以把块级作用域理解为之前学习的私有作用域,存在私有作用域和作用域链的一些机制)ES6中把大部分用{}包起来的都称之为块级作用域
;
const
const细节知识点和let类似
const声明的常量只要声明就必须赋值,而且变量的值是一定的,不能被修改;
注意
:并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指针,const只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。
const声明的变量也存在暂时性死区,即只能在声明的位置之后使用;
JS中创建变量的方式汇总
[ES5]
- var :创建变量
function:创建函数
ES5中创建变量或者函数:存在变量提升,重复声明等特征;[ES6]
- let创建变量
const:ES6中创建常量
ES6中创建的变量或者常量:都不存在变量提升,也不可以重复声明,而且还存在块级作用域;
class:创建一个类
import:导入
ES6中的解构赋值
按照原有值的结构,把原有值中的某一部分内容快速获取到(快速赋值给一个变量)
数组的解构赋值
解构赋值本身是ES6的语法规范,使用什么关键字来声明这些变量是无所谓的,如果不用关键字来声明,那么就相当于给window添加的自定义属性;(严格模式下必须使用关键字来声明,因为严格模式下不允许出现不用关键字声明的变量;),如果解构不到值,那么变量的值就是undefined;
let [a,b,c]=[12,23,34];
var [d,e,f]=[35,41,63];
console.log(a,b,c)//12,23,34;
console.log(d,e,f)//35,41,63;
[q,w,e]=[1,2,3];//相当于给window添加了三个属性:q,w,e值分别为1,2,3;(严格模式下会报错)
多维数组的解构赋值,可以让我们快速的获取到需要的结果
let [a,b,c]=[[45,36],12,[23,43,[1,2[4,[8]]]]23,34];
console.log(a)//[45,36]
console.log(b)//12
console.log(c)//[23,43,[1,2,[4,[8]]]]
//数组中不需要解构的值可用逗号(,)空开,一个逗号代表空开一项
let [,,,A]=[12,23,45];
console.log(A)//undefined
let [,,B]=[12,23,45]
console.log(B)//45
在解构赋值中,支持扩展运算符即<font color=red>...</font>,只要用了扩展运算符,就相当于新生成了一个数组或者对象,如果解构不到值的话,新生成的数组或者对象为空,而不是undefined,但是扩展运算符必须放在末尾
let [a,...c]=[12,1,4,83,34];
console.log(a)//12
console.log(c)//[1,4,83,34];
let [a,...b,c]=[12,1,4,83,34];//会报错,扩展运算符只能放在末尾;
对象的解构赋值
对象的简洁表示法:
const foo = 'bar';
const baz = {foo};
baz // {foo: "bar"}
// 等同于
const baz = {foo: foo};
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
let { foo, bar } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"
如果变量名与属性名不一致,必须写成下面这样。
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"
真正被赋值的是后者,而不是前者。
let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj;
f // 'hello'
l // 'world'
first//error: first is not defined
如果要将一个已经声明的变量用于解构赋值,必须非常小心。
// 错误的写法
let x;
{x} = {x: 1};//会报错
因为 JavaScript 引擎会将{x}理解成一个代码块,从而发生语法错误。只有不将大括号写在行首,避免 JavaScript 将其解释为代码块,才能解决这个问题。
// 正确的写法
let x;
({x} = {x: 1});
放在圆括号当中就可以避免 JavaScript 将其解释为代码块。
解构赋值中支持指定默认值
let [foo = true] = [];
console.log(foo);// true
let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
var {x: y = 3} = {};
y // 3
var {x: y = 3} = {x: 5};
y // 5
解构赋值的作用
1、快速交换两个变量的值
let a=12;
let b=13;
[a,b]=[b,a];
console.log(a);//13
console.log(b);//12
2、可以接收函数返回的多个值
let fn = function () {
let a = 12,
b = 13,
c = 14;
return [a, b, c];
};
let [a,b,c] = fn();
console.log(a, b, c);//=>12 13 14
<font color=red>...</font> 的三种身份:扩展运算符、展开运算符、剩余运算符
1、扩展运算符(注意,在解构赋值中,叫做扩展运算符,只能放在末尾)
只要用了扩展运算符,就相当于新生成了一个数组或者对象,如果解构不到值的话,新生成的数组或者对象为空,而不是undefined,但是扩展运算符必须放在末尾
数组中的扩展运算符
let [a,b,...c]=[12,1,4,83,34]
console.log(a);//12
console.log(b);//1
console.log(c);//[4,83,34]
对象中的扩展运算符
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
console.log(x);//1
console.log(y );//2
console.log(z );//{ a: 3, b: 4 }
2、剩余运算符
function(...arg){
...arg就相当于剩余运算符,可以把传递的所有参数都获取到,而且获取到的是一个数组
}
3、展开运算符
function fn1(){
}
function fn2(){
fn1(...arguments)
...arguments:这里的...就相当于展开运算符,把arguments展开,把里面的每一项分别传递给fn1当作参数,然后让fn1执行;
}
箭头函数
两种写法:1、表达式 2、函数体
表达式:
1.let fn=p=>p; 等价于 var fn=function(p){return p};
2.let fn=()=>n; 等价于 var fn=funciton(){return n};
3.let fn=(n,m)=>n+m;等价于 var fn=function(n,m){return n+m};
函数体:
let fn=(n,m)=>{
var total=n+m;
return total;
}
1、箭头函数中不支持arguments,但是用 剩余运算...arg 代替了arguments,arg是一个数组,可以直接使用数组方法
let obj={
name:'obj',
fn(){
//此方法的属性名为fn,属性值为一个函数,和下面的sum写法是一样的;
},
sum:function () {
}
};
let fn = (...arg)=> {
/console.log(arguments);//=>Uncaught ReferenceError: arguments is not defined
/=>不支持arguments,我们使用ES6中的剩余运算符...来获取传递的进来的所有参数值(优势:使用剩余运算符接收到的结果本身就是一个数组,不需要再转换了)
/console.log(arg instanceof Array);//=>true
return eval(arg.join('+'));
};
//=>也可以把FN简写成以下方式
//let fn = (...arg)=> eval(arg.join('+'));
console.log(fn(10, 20, 30, 40));
2、箭头函数中的this问题,可以默认为箭头函数中没有this,在箭头函数中出现的this都是宿主环境中(即上级作用域中)的this,与箭头函数点之前的执行主体没有任何关系;
ES6中的类和继承
ES6中创建类和实例用class,创建出来的类不存在变量提升;
ES5中创建类和实例,以及如何禁止用户把类当做普通函数执行:new.target
if (typeof new.target === 'undefined') {
throw new SyntaxError(`当前Person不能作为一个普通函数执行,请使用new Person来执行~~`);
}
class Father {//定义一个类;
constructor(name, age) {//构造函数定义实例的私有属性
this.name = name;
this.age = age;
}
getName() {//公有的函数和方法
console.log(this.name + "的年龄是" + this.age + "岁了");
}
static like() {//static后面写的是把Father当做对象时的私有属性
console.log("我是Father函数的私有属性")
}
}
class Son extends Father {//子类Son继承父类Father
constructor(name, age, color) {
super(name, age);//继承父类的私有属性,必须写
//下面为子类的私有属性
this.color = color;
super当作对象调用时,super就相当于父类的原型
}
//下面为子类公有的方法
getColor() {
console.log(this.color)
}
}
字符串中新增加的方法
字符串.includes(val)
:返回布尔值,字符串中是否包含val所代表的字符串;
字符串.startsWith(val)
:val是否在字符串的起始位置。
字符串.endsWith(val)
:val是否在字符串的尾部。
三个方法都返回一个布尔值
let s = 'Hello world!';
s.startsWith('Hello') // true
s.endsWith('!') // true
s.includes('o') // true
这三个方法都支持第二个参数,表示开始搜索的位置。
let s = 'Hello world!';
s.startsWith('world', 6) // true
s.endsWith('Hello', 5) // true
s.includes('Hello', 6) // false
上面代码表示,使用第二个参数n时,endsWith的行为与其他两个方法有所不同。它 针对前n个字符,而其他两个方法针对从第n个位置直到字符串结束。
repeat()
:返回一个新字符串,表示将原字符串重复n次。
'x'.repeat(3) // "xxx"
'hello'.repeat(2) // "hellohello"
'na'.repeat(0) // ""
模版字符串
模版字符串,也是字符串,可以直接使用字符串中的方法;
模版字符串的空格和换行,都是被保留的,如果想要消除空格可以使用trim方法;
$('#list').html(`
<ul>
<li>first</li>
<li>second</li>
</ul>
`.trim());
模版字符串中可以嵌入变量,需要将变量写在${}中,大括号内部可以放入任意的 JavaScript 表达式,可以进行运算,以及引用对象属性。
let x = 1;
let y = 2;
`${x} + ${y} = ${x + y}`
// "1 + 2 = 3"
`${x} + ${y * 2} = ${x + y * 2}`
// "1 + 4 = 5"
let obj = {x: 1, y: 2};
`${obj.x + obj.y}`
// "3"
模板字符串之中还能调用函数。
function fn() {
return "Hello World";
}
`foo ${fn()} bar`
// foo Hello World bar
ES6中新增加的迭代for of
forEach,for,for in,for of的区别
forEach
:不支持返回值,只是普通的循环遍历
for in
:key输出的值为字符串类型,包括把数组当成对象添加的属性也可以遍历出来
for of
:只返回具有数字索引的属性。这一点跟for...in循环也不一样。(不能遍历对象)
let arr = [3, 5, 7];
arr.foo = 'hello';
for (let i in arr) {
//for in是把arr当成对象遍历,i是属性名,包括arr的私有属性
console.log(i); // "0", "1", "2", "foo"
}
for (let i of arr) {
//for of是把arr当成数组遍历,i是数组中的每一项
console.log(i); // "3", "5", "7"
}
for of循环不会返回数组arr的foo属性
如果只想拿到索引,可用keys()方法
for (let index of arr.keys()) {
console.log(index);
}
// 0
// 1
// 2
如果两个都想拿到,可用entries()方法
for (let (index, elem) of arr.entries['ɛntrɪs]()) {
console.log(index, elem);
}
// 0 "3"
// 1 "5"
// 2 "7"
ES6中的模块导入和导出
//=>A模块(在A模块中导入B模块)
import Temp,{lib} from "./B";//=>把导入的Temp中的部分属性方法进行解构赋值
new Temp().init();
lib();//=>Temp.lib()
//=>B模块(导出)
export default class Temp {
init() {
console.log(`hello world`);
}
static lib(){//=>Temp.lib
}
}