小黑子的JavaScript入土过程第五章
- JavaScript系列教程第五章
- 5.1 初识正则表达式
- 5.2 元字符-基本元字符
- 5.3 元字符-边界符
- 5.4 元字符-限定符
- 5.5 元字符-特殊字符
- 5.6 正则表达式-exec() 捕获
- 5.7 正则表达式的两大特性
- 5.8 正则与字符串方法
- 5.9 密码强度验证案例
- 5.9 this 指向
- 5.10 改变this指向
- 5.11 ES6定义变量
- 5.12 ES6的箭头函数
- 5.13 ES6的结构赋值
- 5.14 ES6的对象简写
- 5.15 ES6展开运算符
- 5.16 ES6模块化语法
- 5.17 初识面向对象
- 5.18 创建对象的方式
- 5.19 构造函数注意问题
- 5.20 面向对象的原型
- 5.21 选项卡-面向对象
- 5.22 ES6的class
- 5.23 面向对象的继承
- 5.24 初识前后端交互
- 5.25 ajax
- 5.26 ajax 同步异步
- 5.27 ajax的请求方式
- 5.28 ajax的封装
- 5.29 回调地狱问题
- 5.30 Promise 基础语法
- 5.31 Promise封装ajax
- 5.32 async和await语法
- 5.33 fetch
- 5.34 cookie
- 5.35 jsonp
- 5.36 jsonp的应用
- 5.37 再谈函数
- 5.38 闭包
JavaScript系列教程第五章
5.1 初识正则表达式
正则表达式(英语:Regular Expression,在代码中常简写为regex、regexp或RE)使用单个字符串来描述、匹配一系列符合某个句法规则的字符串搜索模式。
搜索模式可用于文本搜索和文本替换。
什么是正则表达式?
- 正则表达式是由一个字符序列形成的搜索模式。
- 当你在文本中搜索数据时,你可以用搜索模式来描述你要查询的内容。
- 正则表达式可以是一个简单的字符,或一个更复杂的模式。
- 正则表达式可用于所有文本搜索和文本替换的操作。
为什么要用正则表达式?
用正则表达式,是为了让我们能够更方便、更灵活的搜索和替换文本。
如果说,之前我们用字符串搜索文本是精准查询的话,用正则表达式查询就是模糊查询,它的搜索范围可以更广。
案例:应用校验输入框的内容
<form action="">
<input type="text" required id="mytext">
<input type="emaill">
<input type="submit" value="submit">
</form>
<script>
//正值表达式一般写法
//1 字面量写法 :/ /
var reg = /abc/ //目的:将来检测字符串时是不是包含abc
console.log(reg)
//2.内置构造函数
var reg2 = new RegExp("abc")
console.log(reg2)
mytext.onblur = function() { //onblur失去聚焦时触发事件
console.log(mytext.value)
console.log(reg.test(mytext.value))//test()测试方法,测试mytextt.valuez中是否包含abc
}
</script>
5.2 元字符-基本元字符
5.2.1 \d 包含数字符合
- \d 一位数字(0-9)写一个就是包含一位就符合,写两个就是包含两位就符合,依次类推
<script>
var reg = /\d\d/
console.log(reg.test("abc"))
console.log(reg.test("123")) //字符串中包含也符合
console.log(reg.test("1"))
</script>
5.2.2 \D 包含至少一位非数字
- \D 一位非数字写一个就是包含一位就符合,写两个就是包含两位就符合,依次类推
<script>
var reg = /\D\D/
console.log(reg.test("abc"))
console.log(reg.test("123"))
console.log(reg.test("1"))
</script>
案例:
有k才可以
<script>
var reg = /\Dk\D/
console.log(reg.test("abc"))
console.log(reg.test("123"))
console.log(reg.test("1"))
console.log(reg.test("aka"))
</script>
5.2.3 \s 有一位空白(空格 缩进 换行)
- \s 一位空白(空格 缩进 换行)写一个就是包含一位就符合,写两个就是包含两位就符合,依次类推
<script>
var reg = /\s/
console.log(reg.test("a bc"))
console.log(reg.test("12\n3"))
console.log(reg.test("1"))
console.log(reg.test("a ka"))
</script>
</body>
5.2.4 \S 有一位非空白内容
- \S 至少有一位非空白内容,写一个就是包含一位就符合,写两个就是包含两位就符合,依次类推
<script>
var reg = /\S/
console.log(reg.test("a bc"))
console.log(reg.test("12\n3"))
console.log(reg.test(" "))
console.log(reg.test("\n\n\n"))
</script>
5.2.5 \w 有一位字母or数字or下划线
- \w 至少有一位字母or数字or下划线,写一个就是包含一位就符合,写两个就是包含两位就符合,依次类推
<script>
var reg = /\w/
console.log(reg.test("&"))
console.log(reg.test("12\n3"))
console.log(reg.test("wdnmd"))
console.log(reg.test("c_n_m"))
</script>
5.2.6 \W 有一位非字母or数字or下划线
- \W 至少有一位非字母or数字or下划线,写一个就是包含一位就符合,写两个就是包含两位就符合,依次类推
<script>
var reg = /\W/
console.log(reg.test("&"))
console.log(reg.test("12\n3"))
console.log(reg.test("wdnmd"))
console.log(reg.test("c_n_m"))
</script>
5.2.7 . 任意内容(换行不算)
<script>
var reg = /./
console.log(reg.test("&%^*#@4¥"))
console.log(reg.test("12\n3"))
console.log(reg.test("\n\n\n\n"))
console.log(reg.test("c_n_m"))
</script>
5.2.8 .\ 转义字符
<script>
var reg = /\d\.\d/ //要求写1.2 2.3类型的
console.log(reg.test("1.2"))
console.log(reg.test("1a3"))
console.log(reg.test("\n\n\n\n"))
console.log(reg.test("c_n_m"))
</script>
5.3 元字符-边界符
5.3.1 ^ 设定开头
^ 表示开头有才行
<script>
var reg = /^\d/ //表示开头有数字才可以
console.log(reg.test("1.2"))
console.log(reg.test("1a3"))
console.log(reg.test("\n\n\n\n"))
console.log(reg.test("c_n_m"))
</script>
5.3.2 $ 设定结尾
$表示结尾有才行
<script>
var reg = /\d$/
console.log(reg.test("1.2"))
console.log(reg.test("1a3"))
console.log(reg.test("12\n"))
console.log(reg.test("c_n_m"))
</script>
案例:
<script>
var reg = /^a\dc$/ //表示开头要有a,中间是一个任意的数字,结尾是c
console.log(reg.test("1.2"))
console.log(reg.test("1a3"))
console.log(reg.test("a6666666c"))
console.log(reg.test("a6c"))
</script>
5.4 元字符-限定符
5.4.1 * 实现0~多次
限定符* 表示出现0次或多次都可以
<script>
var reg = /\d*/
console.log(reg.test("abc"))
console.log(reg.test("ab1"))
console.log(reg.test("a6666666c"))
</script>
5.4.2 + 实现1~多次
<script>
var reg = /\d+/
console.log(reg.test("abc"))
console.log(reg.test("ab1"))
console.log(reg.test("a6666666c"))
</script>
5.4.3 ? 匹配任何包含0个或1个
等效于 {0,1}
<script>
var reg = /\d?/
console.log(reg.test("abc"))
console.log(reg.test("ab1"))
</script>
5.4.4 {n} 指定次数
{n}表示出现n次;
{n,}表示出现n次或多次
{n,m} 表示出现n到m的次数
<script>
var reg = /\d{3}/
console.log(reg.test("abc"))
console.log(reg.test("ab1"))
console.log(reg.test("a123"))
</script>
5.5 元字符-特殊字符
- ( ) 表示整体
- | 表示或
- 代表1个字符
[a-zA-Z0-9] 相当于\w
[0-9] 相当于 \d
案例:
1.
<script>
var reg = /[a-z]{3,5}/ //表示a-z之间出现至少3到5次
console.log(reg.test("abc"))
console.log(reg.test("def"))
console.log(reg.test("bbbbbbbbb1.234567"))
</script>
2.
<script>
var reg = /[^abc]/ //表示不在abc范围之内的
console.log(reg.test("abc"))
console.log(reg.test("def"))
console.log(reg.test("ab"))
</script>
5.6 正则表达式-exec() 捕获
<script>
var datester = "time is 2029-01-01 12:20:20"
var reg = /\d{4}-\d{1,2}-\d{1,2}/
var newdatestr = reg.exec(datester)//以reg的数字出现结构,获取datester的
console.log(newdatestr[0])
console.log(newdatestr[0].split("-")) //将其转换为数组
console.log(newdatestr[0].split("-").join("/")) //转换字符串
</script>
5.6.1 标识符
标识符 | Value |
---|---|
i | 执行对大小写不敏感的匹配。 |
g | 执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。 |
m | 执行多行匹配。 |
g案例:
<script>
var datester = "time is 2029-01-01 12:20:2 to 2030-11-01 12:20:2 "
var reg = /\d{4}-\d{1,2}-\d{1,2}/g
var newdatestr1 = reg.exec(datester)
console.log(newdatestr1[0])
var newdatestr2 = reg.exec(datester)
console.log(newdatestr2[0])
var newdatestr3 = reg.exec(datester)
console.log(newdatestr3[0])
</script>
i案例:
<script>
var reg = /[a-z]/i
console.log(reg.test("AA"))
console.log(reg.exec("AA"))
</script>
5.7 正则表达式的两大特性
- 懒惰,解决方案:使用全局标识符g
“惰性”表示匹配最短的字符串 - 贪婪 ,解决方案:使用特殊符号?
贪婪表示将匹配尽可能大的组,惰性表示将匹配最小的组
贪婪限定符 惰性限定符
* *?
+ +?
? ??
{ n} {n}?
{ n,} {n,}?
{ n,m} {n,m}?
var reg=/\d{1,4}/ //我要的是至少一次到至多4次
var str='aa1234bb'
console.log(reg.exec(str))
在上面这几串代码中,有时候仅仅只需要一次就足以,但是它会将所有符合的都捕获出来,在这里的话也就是四次。
对贪婪这个特性我们也是有解决方案的,那就是在正则表达式的后面,给他加上一个问号。如下
var reg=/\d{1,4}?/
在加上问号之后他就会转为一种非贪婪模式,这样子就会在我们碰到第一个符合的字符就会停下来,不会再往下继续操作了。
5.8 正则与字符串方法
5.8.1 replace()替换
<script>
var str = "adearfa"
var newstr = str.replace("a","*")
console.log(newstr)
var newstr = str.replace(/a/g,"*")
console.log(newstr)
</script>
5.8.2 search()查找索引
<script>
var str = "adearfa"
var newstr = str.replace("a","*")
console.log(str.search("a"))
console.log(str.search(/a/g))
</script>
但是,加了g的标识符,最后的a也没有查到索引,只能找到第一个
5.8.3 mach() 捕获内容
mach()匹配想要的内容,必esec()好用
<script>
var datester = "time is 2029-01-01 12:20:2 to 2030-11-01 12:20:2 "
console.log(datester.match(/\d{4}-\d{1,2}-\d{1,2}/))
console.log(datester.match(/\d{4}-\d{1,2}-\d{1,2}/g))
</script>
5.9 密码强度验证案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
* {
margin: 0;
padding: 0;
}
body {
display: flex;
justify-content: center;
align-items: center;
}
form {
width: 500px;
padding: 20px;
border: 3px solid #333;
}
form>label {
width: 100%;
height: 70px;
}
form > label > input {
width: 100%;
box-sizing: border-box;
font-size: 20px;
height: 50px;
}
form>p {
width: 100%;
margin-top: 10px;
display: flex;
justify-content: space-between;
}
form>p>span {
width: 30%;
background-color: #CCC;
color: #fff;
height: 30px;
font-size: 20px;
line-height: 30px;
text-align: center;
}
form>p>span:nth-child(1).active {
background-color: red;
}
form>p>span:nth-child(2).active {
background-color: orange;
}
form>p>span:nth-child(3).active {
background-color: green;
}
</style>
<body>
<form>
<label>
<input type="text">
</label>
<p>
<span>弱</span>
<span>中</span>
<span>强</span>
</p>
</form>
<script>
var oinput = document.querySelector("input")
var ospan = document.querySelectorAll("span")
var reg1 = /\d/
var reg2 = /[a-z]/i
var reg3 =/[!@#$%^&*()]/
oinput.oninput = function(){
// console.log(this.value)
var level = 0
if(reg1.test(this.value)) level++
if(reg2.test(this.value)) level++
if(reg3.test(this.value)) level++
for(var i=0;i<ospan.length;i++){
ospan[i].classList.remove("active")
if(i<level){
ospan[i].classList.add("active")
}
}
}
</script>
</body>
</html>
5.9 this 指向
tihs: 谁调用我,this就指向谁
- 在方法中,this 表示该方法所属的对象。
- 如果单独使用,this 表示全局对象。
- 在函数中,this 表示全局对象。
- 在函数中,在严格模式下,this 是未定义的(undefined)。
- 在事件中,this 表示接收事件的元素。
- 类似 call() 和 apply() 方法可以将 this 引用到任何对象。
单独使用 this
单独使用 this,则它指向全局(Global)对象。
在浏览器中,window 就是该全局对象为 [object Window];
方法中的 this
在对象方法中, this 指向调用它所在方法的对象。
<script>
var obj = {
name:"steve",
test:function(){
console.log("11111",this)
}
}
obj.test()
</script>
事件中的 this
在 HTML 事件句柄中,this 指向了接收事件的 HTML 元素:
<div id="box">
click
<span>11111</span>
</div>
<script>
box.addEventListener(
"click",function(evt){
console.log(evt.target)
console.log("11111",this)
}
)
</script>
5.10 改变this指向
<script>
var obj1 = {
name:"obj1",
getName:function(){
console.log("getName1",this.name)
}
}
var obj2 = {
name:"obj2",
getName:function(){
console.log("getName2",this.name)
}
}
//打算让obj1的this.name指向obj2的getName
obj1.getName()
obj2.getName()
</script>
5.10.1 call 和 apply 执行函数,并改变this执行为函数的第一个参数
obj1.getName.call(obj2)
obj1.getName.apply(obj2)
call于apply的区别:
call:支持多给参数
apply:两个参数,第二个参数是一个数组
var obj1 = {
name:"obj1",
getName:function(a,b,c){
console.log("getName1",this.name)
console.log("参数",a,b,c)
}
}
var obj2 = {
name:"obj2",
getName:function(){
console.log("getName2",this.name)
}
}
//打算让obj1的this.name指向obj2的getName
obj1.getName.call(obj2,1,2,3)
obj1.getName.apply(obj2,[1,2,3])
5.10.2 bind 改变this指向为函数的第一个参数,不会自动执行函数
bind也支持多个参数,要手动执行
var obj1 = {
name:"obj1",
getName:function(a,b,c){
console.log("getName1",this.name)
console.log("参数",a,b,c)
}
}
var obj2 = {
name:"obj2",
getName:function(){
console.log("getName2",this.name)
}
}
var fun1 = obj1.getName.bind(obj2,1,2,3)
console.log(fun1)
fun1()//手动执行
5.11 ES6定义变量
- 我们所说的ES5和ES6其实就是在js语法的发展过程中的一一个版本而已
- ECMAScript 就是js的语法
- 。以前的版本没有某些功能
- 。在ES5这个版本的时候增加了一些功能
- 。在ES6这个版本的时候增加了一些功能
- 因为浏览器是浏览器厂商生产的
- 。ECMAScript 发布了新的功能以后,浏览器厂商需要让自己的浏览器支持这些功能.
- 。这个过程是需要时间的
- 。所以到现在,基本上大部分浏览器都可以比较完善的支持了
- 。只不过有些浏览器还是不能全部支持
- 。这就出现了兼容性问题
- 。所以我们写代码的时候就要考虑哪些方法是ES5或者ES6的,看看是不是浏览器都支持
let与var的区别:
- 必须先定义再使用,不能像var一样可以在定义前使用
- 变量重名会报错,不能像var一样重名会覆盖
- 块级作用域{ },在局部定义只能作为局部变量使用。
let与const的区别:
- let声明变量,const声明常量
- let可以定义一个名而不赋予值,const则不行
特殊案例:
<script>
const obj = {
name:"steve"
}
obj.name = "xiaoming"
console.log(obj)
obj = {
name:"xiaoming"
}
console.log(obj)
</script>
前者能变是因为对象是一个复杂类型,改变的是栈里面的值,相当于把内容换了。
后者是改了地址,所以报错
5.12 ES6的箭头函数
<script>
var test1 = function () {
console.log(11111)
}
//箭头函数相当于上方使用
var test2 = () => {
console, log(22222)
}
test1()
test2()
</script>
1.( )只有一个形参的时候可以省略
<script>
var test = a=>{
console.log(111,a)
}
test("steve")
</script>
2.{ }可以省略只有一句代码,只有返回值的时候省略return
<script>
var test = a=>100*a;
console.log(test(10))
</script>
综合运用:
<script>
var list = ["aaa","bbb","ccc"]
var newlist = list.map(item=>`<li>${item}</li>`)
//将list的数组元素以li表现出来
console.log(newlist.join(""))
</script>
特殊案例:
当{}省略时,使用对象结构,会把对象的花括号当成函数的换括号
<script>
var test = ()=>{
name:"steve"
}
console.log(test())
</script>
可以修改成加个()
<script>
var test = ()=>({
name:"steve"
})
console.log(test())
</script>
3.箭头函数没有arguments用法
arguments是保存着所有实参的伪数组,不用写形参也可以。
<script>
var test = function (a, b, c) {
console.log(a, b, c)
console.log(arguments)
console.log(arguments[0], arguments[1], arguments[2])
}
test(1, 2, 3)
test(1,2,3)
</script>
4.箭头函数的this是父级作用域的
一般函数中直接用this.value是访问window的,而要打印需要临时变量储存
<script>
mytext.oninput = function(){
var that = this //用临时变量储存
setTimeout(
function(){
console.log(that.value)
},1000
)
}
</script>
箭头函数的this指向外面作用域,就不用像一般函数一样储存this,功能与其一样
<input type="text" id="mytext">
<script>
mytext.oninput = function(){
//var that = this
setTimeout(
()=>{
console.log(this.value)
},1000
)
}
</script>
5.函数的默认参数
当忘记写传参回答时,会导致未定义,可以导致奔溃
所以在设置参数时,可以赋予初始值,未传参就使用默认的
<script>
function test(a=1,b=2){
return a+b
}
console.log()
var test=(a=1,b=2)=>
{ return a+b}
console.log()
</script>
5.13 ES6的结构赋值
5.13.1 快速地从对象和数组中获取里面的成员
1.es6的获取只能按顺序获取,想要直接取中间或最后一个不行
<script>
var arr = ["steve","van","wot"]
let[x,y,z] = arr
console.log(x,y,z)
</script>
2.变量交换数值
<script>
var a=20
var b=10
var[b,a]=[a,b]
console.log(a,b)
</script>
3.多维度数组获取
<script>
var arr2 = [1,2,[3,4,[5]]]//多维度数组
var [q,w,[e,r,[t]]] = arr2//写出相同结构参数
console.log(t)//输入想求的数
</script>
4.复杂对象获取
<script>
var obj = {
name:"steve",
age:"100",
location:{
province:"magua",
city:"youbin"
},
hobby:[111,222,333]
}
let {
name,
age,
location:{
province,
city
},
hobby:[m,n,k]
} = obj
console.log(name,age,province,city,m,n,k)
</script>
5.14 ES6的对象简写
1.省去了对象元素冒号写法
<input type="text" id="myusername">
<input type="password" id="mypassword">
<button id="mybtn">login</button>
<script>
mybtn.onclick = function() {
let username = myusername.value
let password = mypassword.value
console.log(username,password)
// var obj = {
// username:username,
// password:password
// }
var obj = {
username,//省去了username:username写法
password
}
console.log("发给后端",obj)
}
</script>
2.省略函数
<script>
var obj = {
a: 1,
// getName: function () {
// console.log(this.a)
// }
getName() {
console.log(this.a)
}
}
obj.getName()
</script>
5.15 ES6展开运算符
5.15.1 … 展开数组
合并
<script>
var a = [1,2,3]
var b = [4,5,6]
var c = [...a,...b]
console.log(c)
</script>
5.15.2 …复制
<script>
var a = [1,2,3]
var b = [...a]
b[0] = "steve"
console.log(a,b)
</script>
5.15.3 …参数-实参-形参
1.如此写法,…arr只能写在最后
<script>
var test = (a,b,...arr)=>{
console.log(arr)
}
test(1,2,3,4,5)
var test = (...arr)=>{
console.log(arr)
}
test(1,2,3,4,5)
</script>
</body>
2.传入实参
<script>
var arr = [1,2,3]
var test = function(a,b,c){
console.log(a,b,c)
}
test(...arr)
</script>
3.判断应用
<script>
var arr = [12,21,4124,12541,51,25121,231]
var res = Math.max(...arr)
console.log(res)
</script>
5.15.4 …伪数组转换
<script>
function test(){
var arr = [...arguments]
console.log(arr)
}
test(1,2,3,4,5)
</script>
2.
<script>
var oli = document.querySelectorAll("li")
var oliarr = [...oli]
console.log(oliarr)
console.log(oliarr.filter)
</script>
5.15.5 …对象
<script>
var obj1 = {
name:"steve",
age:"100"
}
var obj2 = {
name:"wot",
location:"van"
}
var obj = {
...obj1,
...obj2
}
console.log(obj)
</script>
修改案例应用:
<h1>修改</h1>
<input type="text" id="myusername">
<input type="number" id="myage">
<button id="btn">修改</button>
<div id="box"></div>
<script>
var obj = {
name:"steve",
age:100,
location:"van",
id:"13516515416024"
}
function render({name,age,location}){
// console.log(obj)
box.innerHTML = `name:${name},age:${age}
,location:${location}`
}
render(obj)
btn.onclick = function(){
var name = myusername.value
var age = myage.value
var newobj = {
...obj,
name,
age
}
console.log(newobj)
//传给后端
//重新渲染页面
render(newobj)
}
</script>
5.16 ES6模块化语法
什么是模块化?
将一个js文件按照功能作用分解为多个js文件,按照顺序排列多个js文件,不可胡乱排版
例如:
A.js文件和B.js文件, C.js文件要用到A和B的比分说冒泡排序和选择排序,如果把C提到A与B的前面,那么按照程序从上往下读,就会报错
为什么要使用模块化
使用模块化可以对不同的功能点进行统一管理,降低耦合性,减少同名的影响。每一个模块都有单独的空间。
ES6模块化语法也有兼容性问题
// webpack
模块化
1.私密不漏
在html中,若要导入js文件的方法
js需导出,利用export{方法A1() }
html中引入,例子
<script type ="js的所属文件夹">
import{A1} from '该js文件的地址'
该方法
</script>
2.重名不怕
当多个js文件中有重名时,例子:
import{A1,test} from ‘该js文件的地址’
import{B1,test} from ‘该js文件的地址’
<script type ="js的所属文件夹">
import{A1,test as A_test} from '该js文件的地址'
import{B1,test as B_test} from '该js文件的地址'
使用
A_test()
B_test()
</script>
3.依赖不乱
默认导出
C.js文件中导出expor default C
html:
导入一个c方法时,可以瞎写,相当于自定义了一个变量引用,结果是一样的
<script type ="js的所属文件夹">
import CCCCCC from '该js文件的地址'
该方法
</script>
5.17 初识面向对象
- 首先,我们要明确,面向对象不是语法,是-一个思想, 是-种编程模式
- 面向:面(脸),向(朝着)
- 面向过程:脸朝着过程=》关注着过程的编程模式
- 面向对象:脸朝着对象=》关注着对象的编程模式
- 实现一个效果
- 。在面向过程的时候,我们要关注每一 个元素,每一 个元素之间的关系,顺序,。。。
- 。在面向过程的时候,我们要关注的就是找到一个对象来帮我做这个事情,我等待结果
- ●我们以前的编程思想是,每一个功能,都按照需求一 步- 步的逐步完成
创建对象的方式
- 因为面向对象就是一个找到对象的过程
- 所以我们先要了解如何创建一 个对象
调用系统内置的构造函数创建对象
- js给我们内置了一个Object构造函数
- 这个构造函数就是用来创造对象的
5.18 创建对象的方式
我们平时一般创建对象都是为了解决一个问题像是做一道菜,如下:
<script>
//字面量
var obj1 = {
name:"steve",
material:["1111","2222","3333"],
setCook(){
}
}
//内置构造函数 new Object()
var obj2 = new Object()
obj1.name = "steve"
</script>
但,要是有一大堆菜要做,我们要复制粘贴相同的代码,然后去修改,就非常麻烦。
5.18.1 工厂函数
使用工厂函数,输入想要改变的值
<script>
//工厂函数
function createObj(name){
var obj = {}
obj.name = name,
obj.material = []
return obj
}
var obj1 = createObj("steve")
console.log(obj1)
var obj2 = createObj("van")
console.log(obj2)
</script>
5.18.2 自定义函数
<script>
//自定义函数
//不同于本来有的函数new Object() new String() new Array()
function createObj(name){
this.name = name
this.marterial = []
this.cook = function(){
//this指向的就是函数自动创建的对象
}
//自动返回
}
var obj1 = new createObj("steve")
var obj2 = new createObj("van")
console.log(obj1,obj2)
</script>
5.19 构造函数注意问题
- 首字母大写,虽然不写没有问题但是不规范
案例:
<script>
function CreatObj(name){
this.name = name
}
var obj1 = new CreatObj("steve")
console.log(obj1)
</script>
-
构造函数不写return
-
构造函数能当普通函数用
<script>
function CreatObj(name) {
console.log(this)
this.name = name
}
var obj1 = new CreatObj("steve")
console.log(obj1,window.name)
</script>
5.19.1 this的指向问题
new的过程===实例化的过程
当实例对象已经生成时,this就是指向实例完的对象
而this有了name属性,创建一个对象obj1,就相当于把实例化对象的地址给了obj1,就是将this.name=输入的名赋值给了obj1而已
1.
<script>
function CreatObj(name) {
console.log(this)
this.name = name
}
var obj1 = new CreatObj("steve")//new的过程===实例化的过程
console.log(obj1,window.name)
</script>
obj1是能够调用cook这个方法的,调用时this实现谁调用我,我就指向谁的原则
,所以在这里this指向了obj1
<script>
function CreatObj(name) {
console.log(this)
this.name = name
this.cook = function(){
console.log(this.name)
}
}
var obj1 = new CreatObj("steve")
console.log(obj1,window.name)
obj1.cook()
</script>
5.20 面向对象的原型
原型的好处:共享内存
<script>
var data1 = {
title: "体育",
list: ["体育-1", "体育-2", "体育-3"]
}
var data2 = {
title: "综艺",
list: ["综艺-1", "综艺-2", "综艺-3"]
}
function CreateList(select, data) {
this.ele = document.querySelector(select)
this.title = data.title,
this.list = data.list
//this.render=
}
//原型
CreateList.prototype.render = function () {
//渲染页面
var h1 = this.ele.querySelector("h1")
var ul = this.ele.querySelector("ul")
//console.log(h1,ul)
h1.innerHTML = this.title
ul.innerHTML = this.list.map(item => `<li>${item}</li>`).join("")
}
var obj1 = new CreateList(".box1", data1)
//var obj2 = new CreateList(data1.title,data1.list)
//console.log(obj1)
obj1.render()
//console.log(obj2)
var obj2 = new CreateList(".box2", data2)
obj2.render()
</script>
原型
new构造函数的时候,复杂数据类型是重新开辟新的内存空间,会浪费内存和降低运行速度。
请看下面示例:
function Star(uname, age) {
this.uname = uname;
this.age = age;
this.sing = function () {
console.log('我会唱歌');
}
}
let ldh = new Star('刘德华', 18)
let zxy = new Star('张学友', 2)
console.log(ldh.sing === zxy.sing); // false
通过 === 符号,我们可以知道 这两个方法的内存地址是不一样的!
那么 在ES6出来之前,是怎么实现继承的同时又解决掉这个问题的呢?
答案是:原型!
构造函数通过原型分配的函数是所有对象所共享的。
Javascript规定,每一个构造函数都有一个 prototype属性,指向另一个对象。
注意这个 prototype就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。
我们可以把那些不变的方法,直接定义在 prototype对象上,这样所有对象的实例就可以共享这些方法
接下来改进一下我们的 Star构造函数:
function Star(uname, age) {
this.uname = uname;
this.age = age;
// this.sing = function () {
// console.log('我会唱歌');
// }
}
Star.prototype.sing = function (){
console.log('我会唱歌');
}
let ldh = new Star('刘德华', 18)
let zxy = new Star('张学友', 2)
console.log(ldh.sing === zxy.sing); // true
1.原型是什么?
一个对象,我们也称为 prototype为原型对象
2.原型的作用是什么
共享方法
总结: 一般情況下,我们的公共属性定义到构造函数里面,公共的方法我们放到原型对象身上
对象原型
function Star(uname, age) {
this.uname = uname;
this.age = age;
}
Star.prototype.sing = function (){
console.log('我会唱歌');
}
let ldh = new Star('刘德华', 18)
对象都会有一个属性 proto 指向构造函数的 prototype原型对象,之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有 proto際型的存在。
console.log(Star.prototype === ldh.__proto__); //true
console.log(Star.prototype.sing === ldh.__proto__.sing);//
console.log(ldh.sing === ldh.__proto__.sing);//true
个人总结就是:实例对象的 proto 指向(等于) 构造函数的 prototype
注意,方法的查找规则:
- 首先先看ldh对象身上是否有sing方法,如果有就执行这个对象上的sing
- 如果没有sing这个方法,因为有 proto的存在,就去构造函数原型对象 prototype身上去查找sing这个方法
原型的 constructor构造函数
对象原型(proto)和构造函数( prototype)原型对象里面都有一个属性 constructor属性, constructor我们称为构造函数,因为它指回构造函数本身。
constructor主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数。
重新指回原来的构造函数:
function Star(uname, age) {
this.uname = uname;
this.age = age;
}
Star.prototype = {
// 手动指回原来的构造函数
constructor: Star,
sing: function () {
console.log('我会唱歌');
},
movie: function () {
console.log("我会演电影");
},
}
let ldh = new Star('刘德华', 18)
console.log(ldh.constructor === ldh.__proto__.constructor); // true
console.log(ldh.constructor === Star.prototype.constructor); // true
铁三角:
在这里插入图片描述
原型链
示例:
function Star(uname, age) {
this.uname = uname;
this.age = age;
}
// prototype是一个对象,默认是带有constructor的,但是使用赋值操作后把对象覆盖了
Star.prototype = {
// 手动指回原来的构造函数
constructor: Star,
sing: function () {
console.log('我会唱歌');
},
movie: function () {
console.log("我会演电影");
},
}
//1.只要是对象就有 proto_原型,指向原型对象
console.log(Star.prototype.__proto__ === Object.prototype); //true
//2.我们Star原型对象里面的_ proto_原型指向的是 object. prototype
// 3. 我们Object.prototype原型对象里边的 __proto__原型 指向 null
console.log(Object.prototype.__proto__); // null
Object.prototype.gogo = function (){
console.log("gogo");
}
let ldh = new Star("刘德华",18)
console.log(ldh.__proto__.__proto__.gogo === Object.prototype.gogo); // true
object就是所有类的父类,上面没有了
原型对象的应用
// 原型对象的应用 扩展内置对象方法
Array.prototype.sum = function () {
let sum = 0
for (let i = 0; i < this.length; i++) {
sum += this[i]
}
return sum
}
console.log(Array.prototype);
let arr = [1,2,3,4,5,6,7,8,9]
console.log(arr.sum());//45
注意:数组和字符串内置对象不能给原型对象覆盖操作
Array prototype = {…} //错误示例
只能是
Array.prototype.xxx= function(){…}
的方式
总结
- 构造函数有原型对象 prototype
- 构造函数原型对象 prototype里面有 constructor指向构造函数本身
- 构造函数可以通过原型对象添加方法
- 构造函数创建的实例对象有 __proto__原型指向构造函数的原型对象
5.21 选项卡-面向对象
<style>
#div1 input {
background: white;
}
#div1 input.active {
background: yellow;
}
#div1 div {
width: 200px;
height: 200px;
background: #ccc;
display: none;
}
</style>
<script>
window.onload = function() {
//创建一个选项卡对象,在某一个div中
new Tabswitch('div1');
}
function Tabswitch(id) {
var _this = this;
var oDiv = document.getElementById(id);
//定义选项卡的两个属性,分别为上面的选择器和下面的显示内容
this.aBtn = oDiv.getElementsByTagName('input');
this.aDiv = oDiv.getElementsByTagName('div');
//循环初始化选项卡的各个选择按钮
//初始化两个地方,1是选项卡的index属性,另一个为选项卡的方法
for(var i = 0; i < this.aBtn.length; i++) {
this.aBtn[i].index = i;
this.aBtn[i].onclick = function() {
_this.fnClick(this);
};
}
}
//统一定义点击选项卡之后的方法
Tabswitch.prototype.fnClick = function(oBtn) {
// 将所有的选项卡状态还原
for(var i = 0; i < this.aBtn.length; i++) {
this.aBtn[i].className = '';
this.aDiv[i].style.display = 'none';
}
//选择,并操作
oBtn.classname = 'active';
this.aDiv[oBtn.index].style.display = 'block';
}
</script>
<div id="div1">
<input class="active" type="button" value="aaa" />
<input type="button" value="bbb" />
<input type="button" value="ccc" />
<div style="display: block;">aaa</div>
<div>afdafje</div>
<div>afjeiofje</div>
</div>
5.22 ES6的class
在ES6中,class (类)作为对象的模板被引入,可以通过 class 关键字定义类。
class 的本质是 function。
它可以看作一个语法糖,让对象原型的写法更加清晰、更像面向对象编程的语法。
5.22.1constructor 方法
constructor 方法是类的默认方法,创建类的实例化对象时被调用。
<script>
class CreateObj{
//构造函数
constructor(name){
this.name = name
}
say(){
console.log(this.name,"hello")
}
}
var obj = new CreateObj("steve")
console.log(obj)
obj.say()
</script>
5.23 面向对象的继承
5.23.1 构造函数继承
原理:子类的构造函数中执行父类的构造函数,并为其绑定子类的 this,让父类的构造函数把成员属性和方法都挂到子类的 this 上去,这样既能避免实例之间共享一个原型实例,又能向父类构造方法传参
1。
function Student (name,age ,classroom) {
Person. call(this, name ,age)
this. classroom = class room
}
function Parent(name) {
this.name = name
}
Parent.prototype.getName = function () {
return this.name
}
function Child() {
// 执行父类的构造函数,并修改 this 指向到子类, 使得父类中的属性能够赋到子类的 this 上
Parent.call(this, 'huohuo') // 既能避免实例之间共享一个原型实例,又能向父类构造方法传参
}
const child1 = new Child()
console.log(child1.name) // huohuo
child1.getName() // 报错, 找不到getName(), 构造函数继承的方式继承不到父类原型上的方法
解决了原型继承中“引用类型私有属性变公有”、“不可传递参数”两个问题
缺点:继承不到父类原型上的方法(所以不能单独使用咯 ~)
5.23.2 原型继承
原理:让子类的原型对象指向父类实例,当子类实例找不到对应的属性和方法时,就会往它的原型对象,也就是父类实例上找,从而实现对父类的属性和方法的继承
Student . prototype = new Person()
// 父类
function Parent() {
this.name = 'huohuo'
}
// 在父类原型上添加方法
Parent.prototype.getName = function () {
return this.name
}
// 子类
function Child() { }
// 继承 Parent
Child.prototype = new Parent() // 让子类的原型对象指向父类实例, 这样一来在Child实例中找不到的属性和方法就会到原型对象(父类实例)上寻找
// 解决重写原型导致的 constructor 丢失问题(增强对象)
Child.prototype.constructor = Child // 根据原型链的规则,顺便绑定一下constructor, 这一步不影响继承, 只是在用到constructor时会需要
// 创建一个实例化对象
const child = new Child() // 然后 Child实例 就能访问到父类及其原型上的 name属性 和 getName方法
child.name // 'huohuo'
child.getName() // 'huohuo'
缺点:
- 由于所有Child实例原型都指向同一个Parent实例,引用类型的私有属性公有化了,
因此对某个Child实例的父类引用类型做变量修改会影响所有的Child实例 - 在创建子类实例时无法向父类的构造函数传参(没有 super 功能)
5.23.3 组合继承
原理:使用原型链继承原型上的属性和方法,而通过构造函数继承实例属性(构造函数+原型对象
function Parent (name) {
this.name = name
}
Parent.prototype.getName = function () {
return this.name
}
function Child () {
// 构造函数继承
Parent.call(this, 'huohuo') // 既能避免实例之间共享一个原型实例,又能向父类构造方法传参
}
//原型链继承
Child.prototype = new Parent() // 父类的方法可以被子类通过原型拿到
Child.prototype.constructor = Child // 顺带绑定下 constructor (增强对象)
//测试
const child1 = new Child()
const child2 = new Child()
child1.name = 'foo'
console.log(child1.name) // foo
console.log(child2.name) // huohuo
child2.getName() // 'huohuo'
- 组合式继承的缺点:每次创建子类实例都执行了两次构造函数(Parent.call() 和 new Paren()),虽然这并不影响对父类的继承,但子类创建实例时,原型中会存在两份相同的属性和方法,这并不优雅
5.24 初识前后端交互
5.25 ajax
AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。 AJAX 不是新的编程语言,而是一种使用现有标准的新方法。 AJAX 是与服务器交换数据并更新部分网页的艺术,在不重新加载整个页面的情况下。
认识AJAX:
- AJAX即“Asynchronous Javascript And
XML”(异步JavaScript和XML),是指一种创建交互式网页应用的网页开发技术。 - AJAX = 异步 JavaScript和XML(标准通用标记语言的子集)。
- AJAX 是一种用于创建快速动态网页的技术。
通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
传统的网页(不使用 AJAX)如果需要更新内容,必须重载整个网页页面。
5.25.1 AJAX的优势
1.不需要插件的支持,原生js就可以使用
2.用户体验好(不需要刷新页面就可以更新数据)
3.减轻服务端和带宽的负担
4.缺点:搜索引擎的支持度不够,因为数据都不在页面上,搜索引擎搜索不到
5.25.2 AJAX的使用
●在js中有内置的构造函数来创建ajax对象
●创建ajax对象以后,我们就使用ajax对象的方法去发送请求和接受响应
1.创建一个ajax对象
2.配置链接信息
const xhr = new XMLHttpRequestO
// xhr 对象中的 open方法是来配置请求信息的
// 第一个参数是 本次请求的请求方式get / post / put / ...
//第二个参数是本次请求的ur1
// 第三个参数是本次请求是否异步, 默认true 表示异步, false表示同步
// xhr. open('请求方式’,'请求地址’, 是否异步)
xhr . open(' get','. /data. php')
- 上面的代码执行完毕以后,本次请求的基本配置信息就写完了
3.发送请求
const xhr = new XMLHttpRequest()
xhr . open(' get', ' ./data. php')
//使用xhr对象中的send 方法来发送请求
xhr. send(
- 上面代码是把配置好信息的ajax对象发送到服务端
案例:
<body>
<form action="1-1-1.php" method="get">
<input type="text" name="names" value=""><br>
<input type="button" value="提交">
</form>
</body>
<script>
//获取DOM对象
var inp = document.getElementsByTagName('input');
//绑定点击事件
inp[1].onclick = function(){
//获取文本值
var v = inp[0].value;
//获取ajax对象
var xhr = new XMLHttpRequest();
//监听状态变化
xhr.onreadystatechange = function(){
//判断状态值
if(xhr.readyState == 4){
//获取服务器返回信息
alert(xhr.responseText);
}
}
//打开链接
xhr.open('get','1-1-2.php');
//发送连接
xhr.send();
}
</script>
5.25.3 一个基本的ajax请求
- 一个最基本的ajax请求就是上面三步
- 但是光有上面的三个步骤,我们确实能把请求发送的到服务端
- 如果服务端正常的话,响应也能回到客户端
- 但是我们拿不到响应
- 如果想拿到响应,我们有两个前提条件
- 本次HTTP请求是成功的,也就是我们之前说的http状态码为200 ~ 299
- ajax对象也有自己的状态码,用来表示本次ajax请求中各个阶段
5.25.4 ajax 状态码
- ajax状态码- xhr. readyState
- 是用来表示一个ajax请求的全部过程中的某一个状态
- 。 readystate === 0:表示未初始化完成,也就是open方法还没有执行
- 。readystate === 1: 表示配置信息已经完成,也就是执行完open之后
- 。readystate === 2: 表示send方法已经执行完成
- 。readystate === 3: 表示正在解析响应内容
- 。readystate === 4:表示响应内容已经解析完毕,可以在客户端使用了
- 这个时候我们就会发现,当-个ajax请求的全部过程中,只有当readystate === 4的时候,我们才可以正常使用服务端给我们的数据
- 所以,配合http状态码为200 ~ 299
- 。一个ajax对象中有一个成员叫做xhr. status
- 。这个成吊就是记录太次请求的http状太码的
- 两个条件都满足的时候,才是本次请求正常完成
5.25.5 readyStateChange
- 在ajax对象中有一个事件,叫做readystateChange 事件
- 这个事件是专门用来监听ajax对象的readyState 值改变的的行为
- 也就是说只要readystate 的值发生变化了,那么就会触发该事件
- 所以我们就在这个事件中来监听ajax的readystate 是不是到4了
const xhr = new XMLHttpRequest()
xhr. open('get', ' . /data. php')
xhr . send()
xhr. onreadystateChange = function () {
//每次readystate 改变的时候都会触发该事件
//我们就在这里判断readystate 的值是不是到4
//并且http 的状态码是不是200 ~ 299
if (xhr. readyState === 4 && /^2\d{2|$/. test(xhr. status)) {
//这里表示验证通过
//我们就可以获取服务端给我们响应的内容了
}
}
5.26 ajax 同步异步
<script>
var xhr = new XMLHttpRequest()
xhr.open("GET", "1.json", true)
//true 表示异步请求
//false表示同步请求
xhr.onload = function () {
if (xhr.status === 200) {
console.log(xhr.responseText)
}
}
xhr.send()
console.log("1111111111111111111")
</script>
5.27 ajax的请求方式
5.27.1 $.ajax()返回其创建的 XMLHttpRequest 对象
$.ajax() 只有一个参数:参数key/value对象,包含各配置及回调函数信息。
如果你指定了dataType选项,那么需要保证服务器返回正确的MIME信息,(如 xml 返回 “text/xml”)。
实例:
保存数据到服务器,成功时显示信息。
$.ajax({
type: "post",
dataType: "html",
url: '/Resources/GetList.ashx',
data: dataurl,
success: function (data) {
if (data != "") {
$("#pager").pager({ pagenumber: pagenumber, pagecount: data.split("$")[1], buttonClickCallback: PageClick });
$("#anhtml").html(data.split("$")[0]);
}
}
});
5.27.2 通过远程 HTTP GET 请求载入信息
相比于复杂的$.ajax而言,GET请求功能则显得更加简单,
请求成功时可调用回调函数。当然如果需要在出错时执行函数,
那么还请使用$.ajax。
实例:
$.get("test.cgi", { name: "John", time: "2pm" },
function(data){
alert("Data Loaded: " + data);
});
5.27.3 通过远程 HTTP POST 请求载入信息
POST请求功能也相对比较简单,请求成功时可调用回调函数。如果需要在出错时执行函数,那么请使用$.ajax请求。
实例:
$.post("/Resources/addfriend.ashx", { "fid": fids, "fname": fnames, "tuid": tuids, "tuname": tunames }, function (data) {
if (data == "ok") {
alert("添加成功!");
}
})
5.27.4 通过 HTTP GET 请求载入 JSON 数据
实例:
$.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=?",
function(data){
$.each(data.items, function(i,item){
$("<img/>").attr("src", item.media.m).appendTo("#images");
if ( i == 3 ) return false;
});
});
5.28 ajax的封装
5.28.1ajax的get 请求
- 步骤:
1、创建xhr
2、监听xhr.onreadystatechange事件 ajax状态发生变化的事件
3、判断ajax请求的状态
4、设置请求方式和url
5、发送请求
<script>
//使用xhr对象发送get方式请求
// 创建xhr
let xhr = new XMLHttpRequest();
// 监听xhr.onreadystatechange事件 ajax状态发生变化的事件
xhr.onreadystatechange = function () {
// 固定写法 readyState 表示 ajax的状态
//ajax的状态总共有(0 1 2 3 4 )五种状态 状态为4表示请求完成 status === 200表示成功
if (xhr.readyState == 4 && xhr.status === 200) {
// xhr.responseText; // 接收响应结果
let res = xhr.responseText; // 结果是JSON字符串
// 转化为数组对象
JSON.parse(res)
}
}
// 调用xhr.open()函数 设置请求方式和url
// 查询字符串的写法 key=value&key=value.... 字符串和接口之间用?隔开
xhr.open('GET', '接口文档url地址')
// 调用xhr.send()函数 发送请求
xhr.send()
</script>
5.28.2 ajax 的post请求
- 步骤:
1、创建xhr
2、监听xhr.onreadystatechange事件 ajax状态发生变化的事件
3、判断ajax请求的状态
4、设置请求方式和url
5、告诉服务器请求头(前端提交的是什么类型数据)
6、发送请求
<script>
// 创建xhr对象
let xhr = new XMLHttpRequest();
// 注册事件
xhr.onreadystatechange = function () {
if (this.readyState === 4 && this.status === 200) {
let res = JSON.parse(this.responseText)
}
}
// 调用open 设置请求方式和url
xhr.open('POST', '接口文档url地址');
// 请求头 必须写在open和send之间
// 告诉服务器 前端提交数据是什么类型 表示查询字符串类型
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
// send里面写 查询字符串 格式:key=value&key=value....
xhr.send('查询字符串')
</script>
5.28.3 封装ajax 请求
步骤:
-
第一大步:
1、创建xhr
2、监听xhr.onreadystatechange事件 ajax状态发生变化的事件
3、判断ajax请求的状态
4、接受响应结果 并且转为数组对象形式
5、调用参数里面的 success函数 -
第二大步:
1、处理参数中data数据 遍历对象
2、将data数据转为查询字符串
3、判断请求方式时 get 还是 post 并 设置请求方式 发送请求 -
第三大步:
调用封装的函数 并且 传参
<script>
// 封装一个函数
function ajax(option) {
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (this.readyState === 4 && this.status === 200) {
let res = JOSN.parse(this.responseText);
option.success(res) //这里调用success函数
}
};
// 处理data数据 将对象形式的 处理查询字符串形式的
// 因为下面open 或者 send只能加字符串形式
let arr = []
for (let i in option.data) {
// i表示对象的key
// option.data[i]表示对象的值
arr.push(i + '=' + option.data[i]); // ['bookname=aaa', 'author=bbb', ...]
}
//join() 把数组转化为字符串,元素是通过指定的分隔符进行分隔的,如果没有默认为逗号
let querystring = arr.join('&'); // bookname=aaa&author=bbb&publiser=ccc
// toUpperCase() 转大写
let method = option.type.toUpperCase(); // 把请求方式转成大写 识别 post / POST
// 判断请求
if (option.type === 'GET') {
xhr.open('GET', option.url + '?' + querystring);
xhr.send();
} else if (option.type === 'POST') {
xhr.open('POST', option.url);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
xhr.send(querystring);
}
};
//调用 传参 数数就是数据
ajax({
type: 'GET', //或者 ‘post’
url: '接口文档 url地址,
data: {数据,根据接口文档提供属性},
success: function (res) {}
});
</script>
5.29 回调地狱问题
回调地狱
将函数作为参数传入,一个嵌套着一个,到最后代码排列成了 一个三角形, 造成了可阅读性差,代表代码的可维护性 差、可迭代性差、可扩展性差。
回调函数
回调函数不是由该函数的实现而直接调用的,而是在特定的事件或条件发生时由另外的一方法调用执行的,用于对该事件或条件进行响应。
- 当一个回调函数嵌套一个回调函数的时候
- 就会出现一个嵌套结构
- 当嵌套的多了就会出现回调地狱的情况
- 比如我们发送三个ajax请求
。第一个正常发送
。第二个请求需要第一个请求的结果中的某一 个值作为参数
。第三个请求需要第二个请求的结果中的某- 个值作为参数
案例:
//ajax通过get方法获取json数据
// 参数 过去数据的地址 即json文件的地址
// 目标属性名 目标值 通过此属性名 获取相应的数据 同属性值进行判断
// 从而获取相应的结果
// 回调函数参数
function find(url,targetName,target,result,callback){
// 通过简写的ajax 的get方法来进行数据的获取
$.get(url, (data) => {
//判断目标
for(let i = 0; i<data.length;i++){
if(data[i][targetName] == target){
// 将查找到的结果作为函数的参数进行回调
if(data[i][result]){
callback(data[i][targetName],data[i][result])
break;
}
}
}
})
}
// 回调地狱
// 查找 id = 5 的学生的姓名 分数
find("data.json","id","5","name",function(target,result){
find("data2.json","name",result,"score",function(target,result){
console.log(`${target}同学分数为${result}`)
})
})
//在此数据较少的情况下,呈现的效果不明显,当数据量庞大后,代码三角的特性还是很明显的,这样的代码可读性差,后期的维护也麻烦,但是回调的思想是会经常用到的,所以可以采取其他的方法解决回调地狱可读性差等缺陷问题
5.30 Promise 基础语法
- promise是一一个ES6的语法
- 承诺的意思,是一个专门用来解决异步回调地狱的问题
5.30.1 Promise的状态
promise有三种状态:
- pending (初始化状态)
- fulfilled (成功)
- rejected (失败)
Promise对象的状态改变,只有两种可能:从pending变为resolved、从pending变为rejected,之后状态不会在改变了且状态不可逆。
语法:
new Promise(function (resolve, reject) {
// resolve 表示成功的回调
// reject 表示失败的回调
}). then(function (res) {
// 成功的函数
}). catch(function (err) {
// 失败的函数
})
5.30.2 resolve reject
1.new Promise 实例 要return
2.传入函数 要有resolve reject 俩参数
3.成功执行resolve 失败 reject
4.then 监听结果并且接收结果
不使用Promise的话模拟一个callback Hell
function loadImg(src, callback, fail){
var img = document.createElement('img')
img.onload = function () {
callback(img)
}
img.onerror = function () {
fail()
}
img.src = src
}
var src = 'www.s.com/index/logo.png'
loadImg(src, function (img) {
console.log(img.width)// callback Hell 我们还要做好多事情 这样看的就很不友好
}, function () {
console.log('failed')
})
使用Promise
function loadImg(src) {
const promise = new Promise(function(resolve,reject){
img.onload = function () {
resolve(img)
}
img.onerror = function () {
reject()
}
img.src = src
})
return promise
}
var src = ''
var result = loadImg(src)
result.then()
result.then()
5.30.3 catch捕获
catch通常用于最后统一捕获,我我们try catch一样
Error和eject都可以捕获
result.then(function(img){
// dosomthing
}).then(function(){
// dosomthing
}).catch(function(error){
console.log(error)
})
5.30.4 多个串联
如果我们希望我们的需求按顺序加载(例如,先加载用户信息,然后再通过用户信息渲染好友列表之类的)
我们需要在.then之后return 另外一个Promise 就可以了
var src1 = 'www.xxx.com/1.jpg'
var src2 = 'www.xxx.com/2.jpg'
var img1 = loadImg(src1)
var img2 = loadImg(src2)
//链式操作
img1.then(function(img){
console.log('图片一加载完成', img)
return img2 //接下来就是对img2进行异步操作了
}).then(function(img){
console.log('图片二加载完成', img)
}).catch(function(er){
console.log(er)
})
5.30.5 Promise.all
Promise.all 接收一个Promose对象数组 待全部完成之后 一起执行success。(有点像JS中短路操作的 且&&)
.then方法接收的datas是一个数组,依次包含多个Promise返回值
Promise.all([result1,result2]).then(datas => {
console.log(datas[0])
console.log(datas[1])
})
5.30.6 Promise.race
和all不一样的是 数组中只要有一个完成 就执行success
(参考JS短路操作的 或||)
Promise.race([result1,result2]).then(data => {
console.log(data)
})
5.31 Promise封装ajax
function ajax(options) {
//这个options时传入给ajax的配置参数
return new Promise((resolve, reject) => {
//返回一个promise对象 resolve成功是的处理,reject失败时的处理
if (!options.url) { // 需要请求的路径
console.log("请确认你的url路径");
return;
}
let method = options.method || "GET"; //请求方式如果没有就默认为get
let async = options.async || true; //ajax是否异步请求默认位true
let xhr = new XMLHttpRequest();
if (method === "GET") {
xhr.open(method, options.url + "?" + Math.random(), async); //防止缓存
xhr.send(null);
} else if (method === "POST") {
xhr.open(method, options.url, async);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send(options.data);
}
// xhr.responseType = options.type || "";
xhr.onreadystatechange = () => {
if (xhr.responseText) {
//有数据说明相应成功
resolve(xhr.responseText);
}
};
xhr.onerror = err => {
reject(err);
};
}).catch(e => {});
}
5.32 async和await语法
async/await是一个es7的语法
这个语法是回调地狱的终极解决方案
语法:
async function fn() {
const res = await promise 对象
}
- 这个是一个特殊的函数方式
- 可以await 一个promise对象
- 可以把异步代码写的看起来像同步代码
- 只要是一一个promiser对象,那么我们就可以使用async/await来书写
5.32.1 async
async是一个加在函数前的修饰符,被async定义的函数会默认返回一个Promise对象resolve的值。因此对async函数可以直接then,返回值就是then方法传入的函数。
// async基础语法
async function fun0(){
console.log(1);
return 1;
}
fun0().then(val=>{
console.log(val) // 1,1
})
async function fun1(){
console.log('Promise');
return new Promise(function(resolve,reject){
resolve('Promise')
})
}
fun1().then(val => {
console.log(val); // Promise Promise
})
5.32.2 await
-
await 也是一个修饰符,只能放在async定义的函数内。可以理解为等待。
await
修饰的如果是Promise对象:可以获取Promise中返回的内容(resolve或reject的参数),且取到值后语句才会往下执行;如果不是Promise对象:把这个非promise的东西当做await表达式的结果。
案例:
async function fun(){
let a = await 1;
let b = await new Promise((resolve,reject)=>{
setTimeout(function(){
resolve('setTimeout')
},3000)
})
let c = await function(){
return 'function'
}()
console.log(a,b,c)
}
fun(); // 3秒后输出: 1 "setTimeout" "function"
function log(time){
setTimeout(function(){
console.log(time);
return 1;
},time)
}
async function fun(){
let a = await log(1000);
let b = await log(3000);
let c = log(2000);
console.log(a);
console.log(1)
}
fun();
// 立即输出 undefined 1
// 1秒后输出 1000
// 2秒后输出 2000
// 3秒后输出 3000
async/await 的正确用法:
// 使用async/await获取成功的结果
// 定义一个异步函数,3秒后才能获取到值(类似操作数据库)
function getSomeThing(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('获取成功')
},3000)
})
}
async function test(){
let a = await getSomeThing();
console.log(a)
}
test(); // 3秒后输出:获取成功
5.33 fetch
XMLHttpRequest是一个设计粗糙的API,配置和调用方式非常混乱,而且基于事件的异步模型写起来不友好。
兼容性不好polyfill: https://github.com/camsong/fetch-ie8
Fetch API提供了一个 JavaScript 接口,用于访问和操纵HTTP的请求和响应等。提供了一个全局 fetch()方法来跨网络异步获取资源。
fetch与ajax的区别:
1、fetch没有办法原生监测请求的进度,而ajax基于原生的XHR开发,可以监测;
2、和ajax相比,fetch有着更好更方便的写法;
3、fetch只对网络请求报错,对400、500都当做成功的请求,而ajax不会。
5.33.1 fetch示例
给fetch() 提供一个参数指明资源路径,会返回一个包含响应结果的promise。当然它只是一个 HTTP 响应,为了获取JSON的内容,我们需要使用 json() 方法:
默认发送GET请求
fetch('http://example.com/movies.json')
.then(response => response.json())
.then(data => console.log(data));
带参数的GET请求:
var id=1
fetch(`https://www.easy-mock.com/mock/5f507e38a758c95f67d6eb42/fetch/getmsg?id=${id}`)
.then(response => response.json())
.then(data => console.log(data));
发送POST请求
var data={
id:'1',
}
fetch('https://www.easy-mock.com/mock/5f507e38a758c95f67d6eb42/fetch/postmsg',{
method:'POST',
body:data
})
.then(response => response.json())
.then(data => console.log(data));
5.33.2 Fetch()语法
fetch(input,{init});
input——定义要获取的资源,可以是:
- 一个 USVString 字符串,包含要获取资源的 URL。
- 一个 Request 对象。
5.34 cookie
什么是 Cookie
Cookie 是一些数据, 存储于你电脑上的文本文件中。
当 web 服务器向浏览器发送 web 页面时,在连接关闭后,服务端不会记录用户的信息。
Cookie 的作用就是用于解决 “如何记录客户端的用户信息”:
- 当用户访问 web 页面时,他的名字可以记录在 cookie 中。
- 在用户下一次访问该页面时,可以在 cookie 中读取用户访问记录。
Cookie 以名/值对形式存储,如下所示:
username=John Doe
当浏览器从服务器上请求 web 页面时, 属于该页面的 cookie 会被添加到该请求中。服务端通过这种方式来获取用户的信息。
5.34.1 JavaScript 创建Cookie
JavaScript 可以使用 document.cookie 属性来创建 、读取、及删除 cookie。
JavaScript 中,创建 cookie 如下所示:
document.cookie="username=John Doe";
还可以为 cookie 添加一个过期时间(以 UTC 或 GMT 时间)。默认情况下,cookie 在浏览器关闭时删除:
document.cookie="username=John Doe; expires=Thu, 18 Dec 2043 12:00:00 GMT";
可以使用 path 参数告诉浏览器 cookie 的路径。默认情况下,cookie 属于当前页面。
document.cookie="username=John Doe; expires=Thu, 18 Dec 2043 12:00:00 GMT; path=/";
5.34.2 JavaScript 读取 Cookie
在 JavaScript 中, 可以使用以下代码来读取 cookie:
var x = document.cookie;
document.cookie 将以字符串的方式返回所有的 cookie,类型格式: cookie1=value;
cookie2=value; cookie3=value;
5.34.3 JavaScript 修改 Cookie
在 JavaScript 中,修改 cookie 类似于创建 cookie,如下所示:
document.cookie="username=John Smith; expires=Thu, 18 Dec 2043 12:00:00 GMT; path=/";
旧的 cookie 将被覆盖。
5.34.4 JavaScript 删除 Cookie
删除 cookie 非常简单。您只需要设置 expires 参数为以前的时间即可,如下所示,设置为 Thu, 01 Jan 1970 00:00:00 GMT:
document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 GMT";
注意,当您删除时不必指定 cookie 的值。
5.34.5 Cookie 字符串
document.cookie 属性看起来像一个普通的文本字符串,其实它不是。
即使在 document.cookie 中写入一个完整的 cookie 字符串, 当您重新读取该 cookie 信息时,cookie 信息是以名/值对的形式展示的。
如果设置了新的 cookie,旧的 cookie 不会被覆盖。 新 cookie 将添加到 document.cookie
中,所以如果您重新读取document.cookie,您将获得如下所示的数据:
cookie1=value; cookie2=value;
document.cookie 属性看起来像一个普通的文本字符串,其实它不是。
即使在 document.cookie 中写入一个完整的 cookie 字符串, 当您重新读取该 cookie 信息时,cookie 信息是以名/值对的形式展示的。
如果设置了新的 cookie,旧的 cookie 不会被覆盖。 新 cookie 将添加到 document.cookie
中,所以如果重新读取document.cookie,将获得如下所示的数据:
cookie1=value; cookie2=value;
5.34.6 JavaScript Cookie 实例
在以下实例中,我们将创建 cookie 来存储访问者名称。
首先,访问者访问 web 页面, 他将被要求填写自己的名字。该名字会存储在 cookie 中。
访问者下一次访问页面时,他会看到一个欢迎的消息。
在这个实例中我们会创建 3 个 JavaScript 函数:
- 设置 cookie 值的函数
- 获取 cookie 值的函数
- 检测 cookie 值的函数
5.35 jsonp
JjsonpISON With Padding)是json的一-种使用模式",可以让网页从别的域名(网站) 那获取资料,即跨域读取
数据。
为什么我们从不同的域(网站)访问数据需要一个特殊的技术( JSONP )呢?这是因为同源策略。
const script = document. createElement('script')
script.src = ' . /kerwin. txt'
document . body. appendChild(scri pt)
注意:
1.后端接口形式必须**() ,需要后端配合
2. jsonp 缺点
(1) onload 删除sciprt标签
(2)只能get请求, 不能post put delete
5.36 jsonp的应用
5.36.1 服务端 JSONP 格式数据
如客户想访问 : https://www.runoob.com/try/ajax/jsonp.php?jsoncallback=callbackFunction。
假设客户期望返回数据:[“customername1”,“customername2”]。
真正返回到客户端的数据显示为: callbackFunction([“customername1”,“customername2”])。
服务端文件 jsonp.php 代码为:
<?php
header('Content-type: application/json');
//获取回调函数名
$jsoncallback = htmlspecialchars($_REQUEST ['jsoncallback']);
//json数据
$json_data = '["customername1","customername2"]';
//输出jsonp格式的数据
echo $jsoncallback . "(" . $json_data . ")";
?>
5.36.2 客户端实现 callbackFunction 函数
function callbackFunction(result, methodName)
{
var html = '<ul>';
for(var i = 0; i < result.length; i++)
{
html += '<li>' + result[i] + '</li>';
}
html += '</ul>';
document.getElementById('divCustomers').innerHTML = html;
}
5.37 再谈函数
函数有返回值,而且返回值必须是复杂类型,而且要赋值给外面的变量。
function test(){
var name ="kerwin"
console.log(name)
var obj = {
a:1,
b:2
return obj
}
var obj1 = test()
var obj2=test()
5.38 闭包
闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment,词法环境)的引用的组合。换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。在 JavaScript 中,闭包会随着函数的创建而被同时创建。
function FetchContainer(url){
return function(path){
return fetch(url+path)
var fetcha = FetchContainer("http://www.a.com")
fetcha("/aaa").then(res=>res.json()).then(res=>console.log(res))
fetcha("/bbb").then(res=>res.json()).then(res=>console.log(res))
fetcha =null
这样子蟹后面可以加而且
回收和方便
不用就回收
输入后才请求发送
mysearch.oninput =(function (){
var timer
null
return function () {
if (timer) {
clearTimeout(timer)
timer = setTimeout(function () {
console.log("发ajax请求")
},500)
})()