JS学习5
正则表达式
正则表通常被用来检索、替换那些符合某个模式(规则)的文本,例如验证表单:用户名表单只能输入英文字母、数字或者下划线, 昵称输入框中可以输入中文(匹配)。此外,正则表达式还常用于过滤掉页面内容中的一些敏感词(替换),或从字符串中获取我们想要的特定部分(提取)等 。
在 JavaScript 中,可以通过两种方式创建一个正则表达式。
方式一:通过调用RegExp对象的构造函数创建
var regexp = new RegExp(/123/);
console.log(regexp);
方式二:利用字面量创建 正则表达式
var rg = /123/;
test()
正则对象方法,用于检测字符串是否符合该规则,该对象会返回 true
或 false
,其参数是测试字符串。
var rg = /123/;
console.log(rg.test(123));
//匹配字符中是否出现123 出现结果为true
console.log(rg.test('abc'));
//匹配字符中是否出现123 未出现结果为false
元字符
基本元字符
\d
一位数字(0~9)
var reg=/\d/
console.log(reg.text=("****"))//只要里面有一位数字则返回true
// /\d\d/则是内容包含两位数字就返回true
\D
一位非数字
var reg=/\D/
console.log(reg.text=("****"))//只要里面有一位非数字则返回true
var num=/\Dk\D/
//则需要两位非数字中间夹着k才返回true
\s
一位空白(空格、缩进、换行)
\S
一位非空白
\w
一位字母(大小写)、数字、下划线
\W
一位非字母、数字、下划线
.
一位任意内容(换行不算)
var reg=/./
\
转义字符
想要表示1.5,5.5这类个位数带个一位小数
var reg=/\d.\d/
//这类表达是错误的
//.代表的是一位非换行
var num=/\d\.\d/
//将.用转义字符转义成\.就能实现了
边界符
提示字符所处的位置,主要是有俩个字符。
^
表示匹配行首的文本(以谁开始)
var reg=/^\d/
//开头必须是一个数字
$
表示匹配行尾的文本(以谁结尾)
var reg =/\d$/
//结尾是数字
限定符
限定符 限定出现次数
一般都要和边界符配合使用,否则执行效果有问题
*
表示允许的次数是 0 至 正无穷次 有没有都行+
表示允许的次数是 1 至 正无穷次 至少有一个?
表示允许的次数是 0 至 1 最多有一次{n}
表示允许的次数是 n 次 ,n的数值 是自定义数值{n,}
表示允许的次数是 n 次以上,包括n次 最少是n次,多个不管也就是 n 至 正无穷 次{n,m}
表示允许的次数是 n 至 m 次 包括n和m
特殊符号
"()"
包含三重用途:
- 一是定义子表达式。
- 二是在完整的模式中定义子模式。
- 三是子表达式的引用。
|或,
表示分割,即或的含义。
如:/ab|cd|ef/
匹配含有ab
或cd
或ef
的字符串。
[ ]
表示字符类: 即括号里是个字符集
如/[abc]/
,表示和含有a,b,c任何一个字母的字符串都匹配。
捕获exec
懒惰特性,只能捕获遇到的第一个符合条件的内容
正则表达式的两个特性
● 正则表达式有两种特性:懒惰性和贪婪性
● 懒惰性:exec()捕获,只能捕获第一个符合正则的内容,使用第二次还是会从头开始捕获,永远捕获不到第二个及以后满足正则的内容。可以加上一个全局标识符g来解决。
● 贪婪性:
var reg = /\d{3,5}/
console.log(reg.exec("cd55025csd"))
/\d{3,5}/
配合exec含义是:捕获3-5个数字,但是结果是'55025'
,直接就捕获了5个数字,这就是它的贪婪性说明,它只要捕获3个数字就符合条件了,但是它捕获了5个。
贪婪性说明了exec
会捕获在满足条件的情况下捕获尽可能多的内容。
正则与字符串方法
replace()
方法,可以将字符串中指定内容替换为新内容。
参数:
- 被替换的内容,可以接收一个正则表达式作为参数
- 新的内容,默认只会替换第一个,可以设置正则表达式为全局匹配模式即可。
var str = "4h5s8H2s5h0s1d6G0";
var res = str.replace(/[A-z]/g,",");
console.log(res); // 4,5,8,2,5,0,1,6,0
match()方法,可以根据正则表达式,从一个字符串中将符合条件的内容提取出来。
默认情况下,match()只会找到第一个符合要求的内容,找到后就停止检索。
我们可以设置正则表达式为全局匹配模式,让该方法全局检索即可。
// 提取出字符串中的字母
var str = "4h5s8H2s5h0s1d6G0";
var res = str.match(/[a-z]/i);
var res2 = str.match(/[a-z]/ig);
console.log(res); // h,只提取出了一个,没有全部提取出来
console.log(res2); // 字母被全部提取出
search()
方法,可以搜索字符串中是否含有指定内容。
如果搜索到指定内容,则会返回第一次出现的索引,反之则返回-1。
search()
可以接收一个正则表达式作为参数,然后根据正则表达式检索字符串。
此方法只会查找第一个,即使设置了全局匹配模式也没用。
var str = "4h5s8h2s5h0s1d6g0";
var res = str.search(/[p]/);
console.log(res); // -1
this指向
谁调用我,this就指向谁
全局环境中的this
functiontest(){console.log(this); } test();
总结:在全局作用域中它的 this
执行当前的全局对象(浏览器端是 Window,node 中是 global)
严格模式‘use strict’
下的this
'use strict';functiontest(){console.log(this); }; test();// undefined
原因:this
并不会指向全局,而是 undefined
,这样的做法是为了消除 js 中一些不严谨的行为
改变this指向
1.Call
函数名call
(调用者, 参数1, …)
作用:函数被借用时,会立即执行,并且函数体内的this会指向借用者或调用者
function fn(name, age) {
this.name = name;
this.age = age;
}
const obj = {}
// 经过call,this指向obj,obj开始有name、age属性
fn.call(obj, '李四', 100)
// 以下this均指向window,name:undefined,age:undefined
fn.call()
fn.call(null)
fn.call(undefined)
结果:普通函数的this指向window,利用call方法将this指向了obj
2. apply
语法:函数名apply
(调用者, [参数, …])
作用:函数被借用时,会立即执行,并且函数体内的this会指向借用者或调用者
function fn(name, age) {
this.name = name;
this.age = age;
}
const obj = {}
// 经过apply,this指向obj,obj开始有name、age属性
fn.apply(obj, ['李四', 100])
// 以下this均指向window,name:undefined,age:undefined
fn.apply()
fn.apply(null)
fn.apply(undefined)
结果:普通函数的this指向window,利用apply方法将this指向了obj
bind
方法
语法:函数名.bind
(调用者, 参数, …)
作用:函数被借用时,不会立即执行,而是返回一个新的函数。需要自己手动调用新的函数来改变this指向
function fn(name, age) {
this.name = name;
this.age = age;
}
const obj = {}
// const newFn = fn.bind(obj, '李四', 100)
// newFn()
fn.bind(obj, '李四', 100)()
ES6
ES6定义变量
定义变量的关键自
-let 变量
-const 常量
let
/ const
和 var
的区别
- 预解析
var
会进行预解析
let
/const
不会,必须先定义后使用 - 重复变量名
var
可以定义重复变量名,重复定义没有意义
let
/const
不允许定义重复变量名 - 块级作用域
var
是没有块级作用域限制,只能被私有作用域限制使用方范围
let
/const
是可以被块级作用域限制使用范围定义范围
块级作用域:任何一个可以书写代码段的{}就是一个块级作用域
let
和 const
的区别
- 定义时赋值
let
可以定义时不赋值
const
定义时必须赋值,一经赋值,不允许改变 - 值得修改
let
定义的值可以被修改
const
定义的变量值不可以被修改
可以使用let进行循环绑定
for (let i = 0; i < lis.length; i++) {
lis[i].onclick = function () {
console.log(i)
}
}
/*
{
i = 0
lis[0].onclick = function () { console.log(i) }
}
{
i = 1
lis[1].onclick = function () { console.log(i) }
}
{
i = 2
lis[2].onclick = function () { console.log(i) }
}
{
i = 3
循环结束
}
ES6箭头函数
**ES6 *允许使用 “箭头”(=>)
简化函数的定义。
注意点:
-
形参个数如果为1个,可以省略小括号不写;
-
如果函数体里面只有一个语句,可以省略大括号不写, 并且他会默认返回 => 符号后面的数据。
-
如果函数体有多个语句,则不能省略大括号。
-
如果函数体只有一个语句,且返回一个对象,建议是,不要写简写的方式。
// function func(){
// console.log("hello");
// }
// 以上代码使用箭头函数书写为:
var func = () => {
console.log("hello");
};
func();
ES6解构赋值
解构赋值:主要用来从数组和对象中提取值,对变量进行赋值。
[]
:是专门解构数组使用的{}
:是专门解构对象使用的
ES6对象简写
1.省略同名的属性
name:name
写成name
2.省略方法的冒号和function
say:function () {}
写成 say(){}
ES6展开运算符
可以在函数调用/数组构造时,将数组表达式或者string
在语法层面展开,还可以在构造字面量对象时, 将对象表达式按key-value
的方式展开。
- 展开语法的场景
- 在函数调用时使用
- 在数组构造时使用
- 在构建对象字面量时,也可以使用展开运算符,这个是在ES2018(ES9)中添加的新特性
const names = ["aaa","bbb","ccc"]
const name = 'hgx'
const name2 = 'black'
const info = {name:'why',age:18}
//函数调用时
function foo(x,y,z){
console.log(x,y,z);
}
function foo2(x,y,z,...rest){ //这里的...是剩余参数
console.log(x,y,z);
console.log(rest);
}
foo(...names) //aaa bbb ccc
foo(...name) //h g x
foo(...name2) //b l a 换行 [ 'c', 'k' ]
//2.构造数组时
const newName = [...names,...name]
console.log(newName); //[ 'aaa', 'bbb', 'ccc', 'h', 'g', 'x' ]
//ES2018(ES9)我们在构建对象字面尽量的时候,可以使用展开元素安抚
const obj = {...info,address:'广州市'} //{ name: 'why', age: 18, address: '广州市' }
console.log(obj);
ES6模块化
1、私密不漏
2、重名不怕
3、依赖不乱
注意script引用记得加type=“module”(依赖不乱)
- 每个
js
文件都是一个独立的模块 - 导入其它模块使用
input
关键字 - 向外共享模块使用
export
关键字
面对对象
面向对象程序设计(Object-oriented programming,缩写:OOP)
是种具有对象概念的程序编程典范,同时也是一种程序开发的抽象方针。它可能包含数据、属性、与方法。对象则指的是类的实例。它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性,对象里的程序可以访问经常修改对象相关连的数据。
举个最简单点的例子来区分 面向过程和面向对象
有一天你想吃鱼香肉丝了,怎么办呢?你有两个选择
1、自己买材料,肉,鱼香肉丝调料,蒜苔,胡萝卜等等然后切菜切肉,开炒,盛到盘子里。
2、去饭店,张开嘴:老板!来一份鱼香肉丝!
这就是1是面向过程,2是面向对象。
创建对象的方式
//字面量
var obj1={
name:"蒸羊羔",
material:["111","222","333"],
setCook(){
}
}
var obj2={
name:"烧花鸭",
material:["111","222","333"],
setCook(){
}
}
//内置构造函数
var obj1 = new Object()
obj1.name = "蒸羊羔"
var obj2 = new Object()
obj2.name = "烧花鸭"
//工厂函数
function creatObj(name){
var obj = {}
obj.name=name
obj.material=[]
return obj
}
var obj1 = creatObj(“蒸羊羔”)
console.log(obj1)
var obj2 = creatObj(“烧花鸭”)
console.log(obj2)
//自定义构造函数
//new Object() new String() newArray()
function creatObj(name){
this.name = name
this.material=[]
this.cook=function(){
}
}
var obj1 = new creatObj("蒸羊羔")
面向对象的原型
系统会给对象添加一个 _proto_
属性,并指向构造函数的prototype原型对象。
对象都会有一个属性_proto_
指向构造函数的prototype原型对象,之所以对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有_proto_
原型的存在。实例的 _proto_
对象原型本质上指向构造函数的原型对象的prototype。
_proto_
对象原型和原型对象prototype是等价的_proto_
对象原型的意义就在于为对象的查找机制提供一个方向,或者说一个路线,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象prototype
ES6-class
传统的JS只有对象的概念,没有class类的概念,因为JS是基于原型的面向对象语言,原型对象特点就是将属性全部共享给新对象。
ES6引入了class类这个概念,通过class关键字可以定义类,这就是更符合我们平时所理解的面向对象的语言。
class Person{ //定义一个名为Person的类
// 构造函数,用来接受参数
constructor(x,y){
this.x = x; //this代表的是实例对象
this.y = y;
}
todoSome(){ //这是个类的方法,不需要加function,有多个方法也不用逗号隔开
alert(this.x + "的年龄是" +this.y+"岁");
}
}
export default Person;
继承
- 继承方式
- 原型链继承
- 构造函数继承
- 组合继承
- 原型式继承
- 寄生式继承
- 寄生组合式继承
- ES6
Student.prototype=new Person()
前后端交互
ajax
1.数据传输格式
① XML:适合重量级数据,通过标签传输,解析较复杂
② JSON:适合轻量级数据,通过字符串传输,解析较容易(经常使用)
2.语法
① 声明ajax对象:let xhr = new XMLHttpRequest();
② 预设请求方式、要访问的地址、是否异步:xhr.open(‘get/post’, url, true/false);
③ 发送请求:xhr.send();
④ 等待响应:通过事件处理函数onreadystatechange监听readystate变化
3.readyState取值
- ① 0:调用open方法之前
② 1:调用send方法之后,发送请求时
③ 2:send方法完成,已经接收到所有相应数据
④ 3:正在解析数据
⑤ 4:解析完成
get请求
let xhr = new XMLHttpRequest(); // 声明ajax对象
xhr.open('get', './get.php?username=xxx&password=yyy', true); // 预设get请求方式、拼接查询字符串的地址、是否异步
xhr.send(); // 向服务器发送请求
xhr.onreadystatechange = function() { // 等待数据相应
if (xhr.readyState === 4 && xhr.status === 200) { // 解析完成且状态码为200时,进行后续操作
console.log(xhr.responseText);
}
post请求
let xhr = new XMLHttpRequest(); // 声明ajax对象
xhr.open('post', 'post.php', true); // 预设post请求方式,要访问的地址,是否异步
xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded'); // 设置请求头部,用来解析传入的参数
xhr.send('username=xxx&password=yyy'); // 向服务器发送请求,需要传入queryString
xhr.onreadystatechange = function () { // 等待数据响应
if (xhr.readyState === 4 && xhr.status === 200) { // 解析完成且状态码为200时,进行后续操作
console.log(xhr.responseText);
}
}
同步异步
true表示异步请求
false表示同步请求
ajax请求1和ajax请求2,同时并发,谁也不用等谁,这就是异步。(a不等b,b也不等a)
如果ajax请求1在发送的时候需要等待ajax请求2结束之后才能发送,那么这就是同步。(a等待b,或者b等待a,只要发生等待,就是同步。)
请求方式
- get 偏向获取数据
- post偏向提交数据
- put 偏向更新
- delete偏向删除信息
- patch偏向部分修改
ajax封装
let $ = {
createXHR: function() {
if(window.XMLHttpRequest) {
return new XMLHttpRequest()
} else {
return new ActiveXObject()
}
},
ajax: function(params) {
// 初始化参数
let type = params.type ? params.type.toLowerCase() : 'get'
let isAsync = params.isAsync ? params.isAsync : 'true'
let url = params.url
let data = params.data ? params.data : {}
let dataType = params.dataType.toLowerCase()
// 用我们封装的方法动态生成XHR对象
let xhr = this.createXHR()
let str = ''
// 拼接字符串
Object.keys(data).forEach(key => str += `${key}=${data[key]}&`)
str = str.slice(0, -1)
// 如果是get请求就把携带参数拼接到url后面
if(type === 'get') url += `?${str}`;
// 返回promise对象,便于外部then和catch函数调用
return new Promise((resolve, reject) => {
// 创建请求
xhr.open(type, url, isAsync)
if(type === 'post') {
xhr.setRequestHeader('Content-Type', 'application/x-www-form-rulencoded')
xhr.send(str)
} else {
xhr.send()
}
xhr.onreadystatechange = function() {
if(xhr.readyState === 4) {
if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
let res = dataType === 'json' ? JSON.parse(xhr.responseText) : xhr.responseText
resolve(res) // 请求成功,返回数据
} else {
reject(xhr.status) // 请求失败,返回状态码
}
}
}
})
}
}
回调地狱
当一个回调函数嵌套一个回调函数的时候
就会出现一个嵌套结构
当嵌套的多了就会出现回调地狱的情况
比如我们发送三个 ajax
请求
- 第一个正常发送
- 第二个请求需要第一个请求的结果中的某一个值作为参数
- 第三个请求需要第二个请求的结果中的某一个值作为参数
Promise语法
主要用来解决异步回调地狱问题
Promise是ES6中的语法,一般情况下都会把promise藏在封装的底层
- Promise(承诺)的三种状态
== pending 正在进行
== fulfilled 成功履行承诺
== rejected 没有遵守承诺
承诺的状态一旦发生改变,那么这个状态不可逆
Promise的函数参数有两个形参 fulfill,reject,分别表示异步操作成功后的回调函数和异步操作执行失败后的回调函数。其实这里的“成功”和“失败”来描述并不准确,按照标准来讲,fulfill是将Promise的状态置为fullfiled,reject是将Promise的状态置为rejected。
promise的状态监听函数:
- then() :可以监听promise状态的改变
- catch() :可以监听promise状态改变为rejected
- finally() :只要状态改变了,就会进入这个函数之中
封装Ajax
function pajax(options){
return new Promise((resolve,reject)=>{
ajax({
...options,
success(res){
resolve(res)
},
error(err){
reject(err)
}
})
})
}
async和await语法
- promise 使用 .then 链式调用,但也是基于回调函数
- async/await 更加优雅的异步编程的写法
1.它是消灭异步回调的终极武器
2.它是同步语法,也就是用同步的写法写异步的代码
1.async
async function fn(){
return 100
}
console.log(fn()) //返回一个成功态的promise对象,result 是 100
fn().then(
data=>{
console.log(data)
}
) // 100
2.await
await 后面接 Promise
1.await p1相当于是 p1.then,并且只是成功态的then
2.await 和 then 的区别就是:then还需要传回调进去,但 await 可以直接得到值
(async function(){
const p1 = Promise.resolve(300) //一个成功态的promise对象,且传了result为300
const res = await p1 // return 值
console.log(res) // 300
})
fetch
Fetch API 提供了一个获取资源的接口(包括跨域请求),用于取代传统的XMLHttpRequest的,在 JavaScript 脚本里面发出 HTTP 请求。
语法:
fetch(url)
.then(...)
.catch(...)
cookie
创建cookie
1.浏览器没有cookie
2.服务器创建cookie
Cookie cookie = new Cookie("k1","v1");
response.addCookie(cookie);
3.通过响应头Set-Cookie通知客户端保存cookie
Set-Cookie:k1=v1
4.浏览器收到响应头后,查看set-cookie
响应头,检查下是否有这个cookie
,没有创建,有就修改。
Jsonp
Jsonp(JSON with Padding)
是 son 的一种”使用模式”,可以让网页从别的域名 (网站)那获取资料,即跨域读取数据。
为什么我们从不同的域(网站) 访问数据需要一个特殊的技术(JSONP)呢? 这是因为同源策略。
同源策略:同域名 同端口号 同协议
不符合同源策略,浏览器为了安全,会阻止请求
解决
- cors 由后端设置,
Access-Control-Allow-Origin
- jsonp:前后端协作
jsonp
原理:动态创建script
标签,src属性指向没有跨域限制
指向一个接口,接口返回的格式一定是 ****()
函数表达式
function test(obj){
console.log(obj)
}
BigInt.onclick = function(){
var oscript = document.createElement("script")
oscript.src=""//未来地址
document.body.appendChild(oscript)
}
闭包
函数内部返回函数,被外界引用,这个内部函数就不会被销毁回收
内部函数所用到的外部函数变量也不会被销毁
function outer(){
var name = "ks"
var age = 100
return function(){
return name+"111"
}
}
var func = outer()
闭包的产生条件
作用域嵌套
在父级作用域里生成了一个变量 var i=0 在子作用域里使用这个变量,这样声明的那个变量 i 就是 自由变量,这种作用域嵌套环境叫做 闭包环境。
在内存中存在和回收站相似的机制,叫做 垃圾回收机制。自由变量在函数关闭后被放在垃圾回收机制里,当下次调用时,再重新出来。
闭包的使用场景
- 模拟私有方法
- setTimeout循环
- 匿名自执行函数
- 结果要缓存场景
- 实现类和继承
使用闭包可以延长局部变量的生命周期,不让局部变量使用后立即释放,被删除
// 当声明变量 i 在outFn 函数外面时,输出结果为:1,2,3,4
var i=0;
function outerFn(){
return function innerFn(){
i++;
console.log(i);
}
}
var fn1 = outFn();
fn1();
fn1();
var fn2 = outFn();
fn2();
fn2();