目录
AJAX
Demo:
https://blog.csdn.net/qianqianstd/article/details/73437961
天气接口们:
https://blog.csdn.net/x_iya/article/details/52189750
城市的 ID:
直接百度中国城市天气预报编码
https://www.cnblogs.com/oucbl/p/6138963.html#_label21
XMLHttpRequest
在atom中运行ajax报错XMLHttpRequest is not defined:
这里的运行实际是运行的 node
, window
属于浏览器环境中的全局变量,在 node
中肯定拿不到的.可以改成 global
你是装了nodejs吧,你的ctrl + b只是把js代码在nodejs环境下运行了好像,document window只在浏览器里面存在,nodejs没有这些。你需要在html里面引入这个js,然后在浏览器中打开那个html吧。
var test;
if(window.XMLHttpRequest){
test = new XMLHttpRequest();
}else if(window.ActiveXObject){
test = new window.ActiveXObject();
}
使用:
open()
初始化 HTTP 请求参数,例如 URL 和 HTTP 方法,但是并不发送请求。
send()
发送 HTTP 请求,使用传递给 open() 方法的参数,以及传递给该方法的可选请求体。
原生ajax的四个过程,
new,open,send, onreadystatechange。
异步刷新实现原理:
当xmlHttpRequest对象的readyState更新时,就会自动执行onreadystatechange绑定的js事件(体现异步),在该事件中操作DOM更新html页面(体现局部刷新)。
Ajax:readyState(状态值)和status(状态码)的区别
readyState,是指运行AJAX所经历过的4种状态,无论访问是否成功都将响应的步骤,可以理解成为AJAX运行步骤。
status,是指无论AJAX访问是否成功,由HTTP协议根据所提交的信息,服务器所返回的HTTP头信息代码。
总体理解:可以简单的理解为state代表一个整体的状态。而status是这个大的state下面具体的小的状态。
eval() 函数
可将字符串转换为代码执行,并返回一个或多个值
为什么eval要添加括号呢?
由于json是以{}的方式来开始以及结束的,在JS中,它会被当成一个语句块来处理,所以必须强制性的将它转换成一种表达式。
加上圆括号的目的是迫使eval函数在处理JavaScript代码的时候强制将括号内的表达式(expression)转化为对象,而不是作为语句(statement)来执行。举一个例子,例如对象字面量{},如若不加外层的括号,那么eval会将大括号识别为JavaScript代码块的开始和结束标记,那么{}将会被认为是执行了一句空语句。
console.log(eval("{}"); // undefined
console.log(eval("({})");// object[Object]
解析对象数据
let {data:{forecast:allWeather}} = obj;
模板字符串中换行<br/>
ejs
导入外部文件
在index.ejs文件中导入public文件夹下的table.css以及table.js两个文件
在servers->app.js中写上这句
//获取放置在public文件夹下的静态文件,
app.
use(express.
static(
__dirname +
'/public'));
这句话的意思是,将静态文件目录设置为项目根目录+/public
关于app.use()这个方法具体的介绍,这里有篇文章,写的很好app.use(express.static)方法详解
这样,就可以在ejs文件中导入外部静态文件了。
这里需要注意一点,在导入写URL时,只需要写public后面的路径就好,不需要再加上“public”了。如下:
<link rel="stylesheet" type="text/css" href="table.css"/>
<script type="text/javascript" src="table.js"></script>
<script type="text/javascript" src="E:\studyMaterial\学习\前端笔记\4\ajax.js"></script>
在Express中:
Express的结构如下:
|—node_modules——用于安装本地模块。
|—public————用于存放用户可以下载到的文件,比如图片、脚本文件、样式表等。
|—routes————用于存放路由文件。
|—views————-用于存放网页的模板。
|—app.js————应用程序的启动脚本。
|—package.json——项目的配置文件。
要把本地文件放在public中,在public中新建一个images文件,引用文件里的图片,就可以在浏览器中显示
读取天气信息代码
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<script type="text/javascript">
// let xmlHttp=new XMLHttpRequest();
if(window.XMLHttpRequest){
xmlHttp = new XMLHttpRequest();
}else if(window.ActiveXObject){
xmlHttp = new window.ActiveXObject();
}
xmlHttp.open("get","http://wthrcdn.etouch.cn/weather_mini?citykey=101010100");
xmlHttp.onreadystatechange=callback;
xmlHttp.send();
function callback(){
if(xmlHttp.readyState==4&&xmlHttp.status==200){
let result=xmlHttp.responseText;// 接收返回的结果
let obj=eval(`(${result})`);// 转换为对象
// 取得对象中所有未来的天气信息
let {data:{forecast:allWeather}}=obj;
console.log(allWeather);
let str="";
for(weather of allWeather){
let {date,high,low,type}=weather;
str+=`${date},${high},${low},${type}<br/>`;
}
// 建立一个div
let div=document.createElement("div");
div.innerHTML=str;
document.body.appendChild(div);
}
}
</script>
<title></title>
</head>
<body>
</body>
</html>
启动服务
之前构建的项目是我们后面要一直使用的,因此必须每次编写代码前,先启动这个项目对应的服务。
在命令行中进入项目根目录E:\studyMaterial\work\web,执行:gulp --watch
之后就可以通过localhost:3000来访问你编写的页面和内容,默认访问的是server-views-index.ejs的页面。
在atom中调试js
Script-run script
Ctrl+shift+B
2.1、集合操作
Java语言里提供三种集合:List、Set、Map
List:允许重复数据
Set:不允许重复数据
Map:键值对形式
而在ES6种,也出现了Set和Map集合。
Set也是不允许重复数据出现。
let set = new Set([1,2,3,4,3,4,7,8,8,9]); console.log(set); |
Set集合可以动态添加、删除和清空数据。
let set = new Set([1,2,3]); set.add(22); set.delete(3); console.log(set.has(1)); console.log(set);
// 注意,Set集合取得的key是值,而不是下标 // for (let key of set.keys()) { // console.log(key); // }
for (let [key,value] of set.entries()) { console.log(key,value); } |
Map集合是按照 key -> value 键值对的形式保存的。
因此加入数据和判断数据是否存在都依赖于key来完成。
let map = new Map(); // 添加数据 map.set('a',20); map.set('b',30); map.set('a',10); map.set('c',30); // 取得数据 console.log(map.get("b")); // 删除数据 map.delete('a'); console.log(map); // 清空数据 // map.clear(); // console.log(map);
// 循环,也包含keys()和values()方法分别取得所有的key和value // entries就是直接循环map的结果,因此一般不用 for (let [key,value] of map) { console.log(key,value); } |
Map集合也可以像Set集合一样,在建立的时候就进行初始化。
let map = new Map([["a",1],["b",2],["c",3],[2,4]]); console.log(map); |
集合在ES6里还提供了两个Weak类型(WeakSet和 WeakMap)
这种类型只能放对象类型,不支持clear清空,不支持size取得数量,因此开发中基本不用。
let set= new WeakSet(); let obj = {a:1} set.add(obj); |
2.2、函数扩展
在ES6中,函数支持参数设置默认值的功能了,这样就可以实现一些类似重载方法的操作。
function add(a,b=0,c=0) { return a + b + c; }
console.log(add(3)); |
对于默认值的设置,注意对象中默认值的使用。
function add(a,b ={a:1,b:2}) { return a + b.a + b.b; }
console.log(add(3,{a:1})); |
函数的参数也可以设置为可变参数。
function add(...value) { let sum = 0; for (let temp of value) { sum += temp; } return sum; }
console.log(add(3)); |
在ES6中,函数扩展的最重要的一个功能就是箭头函数,其目的就是为了简化代码。
// function test(a) { // return a * 2 ; // }
let test = a => a*2;
console.log(test(5)); |
如果参数和返回值并不是这么标准,也可以进行扩展和设置。
// function test(a,b) { // return a * b ; // }
// 多个参数需要加入逗号 // let test = (a,b) => a*b;
// 如果没有参数需要加入括号 // let test = () => 5;
// 如果返回值部分不止一行,而有多行代码,加入 {} 即可。 let test = (a,b) => { let c = a* b; return c ; }
console.log(test(3,5)); |
下面通过箭头函数改造刚才的AJAX操作。
// 调用AJAX的操作 let xmlHttp = new XMLHttpRequest(); // 设置远程接口地址 xmlHttp.open("get","http://wthrcdn.etouch.cn/weather_mini?citykey=101010100"); // 设置回调函数 xmlHttp.onreadystatechange = () => { // 判断是否正确返回 if (xmlHttp.readyState == 4 && xmlHttp.status == 200) { // 接收返回的结果 let result = xmlHttp.responseText; // 转换为对象,使用eval let obj = eval(`(${result})`); // console.log(obj); // 取得对象中所有未来的天气信息 let {data:{forecast:allWeather}} = obj; console.log(allWeather); // 建立一个div let div = document.createElement("div"); let str = "" ; // 使用for ... of 循环取得里面的所有数据 for (let weather of allWeather) { let {date,high,low,type}= weather; // 把结果拼接成为字符串 str += `${date} ,${low} - ${high}, ${type}<br/>`; } div.innerHTML = str;
// 把div放入到页面上 document.body.appendChild(div); } } ; // 提交 xmlHttp.send(); |
2.3、面向对象
在ES6中,为了让JavaScript更像Java,加入了class和extends关键字,以及super关键字,实现了面向对象中的继承功能和多态功能,但是不支持封装。
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<script type="text/javascript">
class Person{
constructor(name,age){
this.name=name;
this.age=age;
}
say(){
console.log(`姓名:${this.name} 年龄:${this.age}`);
}
}
class Student extends Person{
constructor(name,age,school){
super(name,age);
this.school=school;
}
}
let s=new Student("张三", 18, "大学");
s.say();
</script>
<title></title>
</head>
<body>
</body>
</html>
在ES6的面向对象里也有get/set的概念,但是这里的getter和 setter并不是为了解决封装问题的。而是改变输入输出字符串格式。
class Person { constructor (name,age) { this.name = name ; this.age = age; }
get preName() { return "pre" + this.name ; } set preName(name) { this.name = name + "新设置" ; }
say() { console.log(`姓名:${this.name},年龄:${this.age}`); } }
let p = new Person("张三",30); p.say(); console.log(p.preName); p.preName = "李四"; p.say(); |
还支持static静态功能。
参考:https://www.cnblogs.com/signheart/p/6599838.html
普通对象,同时拥有静态和非静态属性、方法
* 可以用实例化
* 注意:
* 1.静态方法/属性使用类名访问
* 2.非静态方法/属性使用实例名访问
class Person { constructor (name,age) { this.name = name ; this.age = age; }
// static a = 10;
static sayProto() { console.log("静态方法打印" + this.age); }
say() { console.log(`姓名:${this.name},年龄:${this.age}`); } } // 类名直接调用 // Person.sayProto(); Person.a = 10; console.log(Person.a);
// let p = new Person("张三",30); // p.sayProto(); |
2.4、异步处理
Promise
Promise实现的就是延时操作以及异步处理,它比回调的优势就在于支持设置链式的调用模式。
参考https://blog.csdn.net/MrJavaweb/article/details/79475949?utm_source=copy
Promise 是异步编程的一种解决方案,比传统的解决方案–回调函数和事件更合理和更强大。
Promise ,简单说就是一个容器,里面保存着某个未来才回结束的事件(通常是一个异步操作)的结果。从语法上说,Promise是一个对象,从它可以获取异步操作的消息。
Promise 对象的三种状态:
pending:进行中
fulfilled :已经成功
rejected 已经失败
状态改变:
Promise对象的状态改变,只有两种可能:
从pending变为fulfilled
从pending变为rejected。
这两种情况只要发生,状态就凝固了,不会再变了,这时就称为resolved(已定型)
Promise 实例生成以后,可以用then 方法分别指定resolved状态和rejected状态的回调函数。
promise.then(function(value){
//success
},function(error){
//failure
});
function test() { return new Promise((resolve,reject) => { //执行代码*** setTimeout(resolve,1000);
//resolve(返回值); }); }
test().then(function() { console.log("真正执行的操作"); }).then(function() { console.log("真正执行的操作02"); }); |
改造显示实时天气信息的代码。
// ajax.js
function ajax(url){
return new Promise((resolve,rejext)=>{
if(window.XMLHttpRequest){
xmlHttp = new XMLHttpRequest();
}else if(window.ActiveXObject){
xmlHttp = new window.ActiveXObject();
}
xmlHttp.open("get",url);
xmlHttp.onreadystatechange=()=>{
if(xmlHttp.readyState==4&&xmlHttp.status==200){
resolve(xmlHttp.responseText);// 接收返回的结果
}
};
xmlHttp.send();
})
}
function getResultOver(result){
let obj=eval(`(${result})`);// 转换为对象
// 取得对象中所有未来的天气信息
let {data:{forecast:allWeather}}=obj;
console.log(allWeather);
let str="";
for(weather of allWeather){
let {date,high,low,type}=weather;
str+=`${date},${high},${low},${type}<br/>`;
}
// 建立一个div
let div=document.createElement("div");
div.innerHTML=str;
document.body.appendChild(div);
}
ajax("http://wthrcdn.etouch.cn/weather_mini?citykey=101010100").then(function(result){
getResultOver(result);
ajax("http://wthrcdn.etouch.cn/weather_mini?citykey=101010200").then(function(result){
getResultOver(result);
ajax("http://wthrcdn.etouch.cn/weather_mini?citykey=101010100").then(function(result){
getResultOver(result);
});
});
});
Promise除了可以处理线性操作以外,还支持并行操作的处理,可以通过以下两个方法完成:
- all
- race
let url1 = "https://img10.360buyimg.com/mobilecms/s280x280_jfs/t19729/347/2648537104/157629/10c9f7a/5b051777N2b948713.jpg!q90.webp"; let url2 = "https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1027887115,2954985598&fm=26&gp=0.jpg"; let url3 = "https://img13.360buyimg.com/mobilecms/s220x220_jfs/t1/4087/26/6653/286936/5ba3798aE7e237db6/cc2f37bbade6307a.jpg!q90!cc_110x110.webp";
// 实现一个图片加载的功能 // 取得网上的某一张图片。 function loadImage(url) { return new Promise((resolve,reject) => { // 页面上建立一张图片 let img = document.createElement("img"); img.src = url ; // 当下载完成后,执行显示操作 img.onload = function() { alert(url); resolve(img); } }); } // 当加载完成后,把所有图片一起显示出来 function showImages(imgs) { imgs.forEach(function(item){ document.body.appendChild(item); }); }
Promise.all([ loadImage(url1) , loadImage(url2) , loadImage(url3) ]).then(showImages); |
Race
let url1 = "https://img10.360buyimg.com/mobilecms/s280x280_jfs/t19729/347/2648537104/157629/10c9f7a/5b051777N2b948713.jpg!q90.webp"; let url2 = "https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1027887115,2954985598&fm=26&gp=0.jpg"; let url3 = "https://img13.360buyimg.com/mobilecms/s220x220_jfs/t1/4087/26/6653/286936/5ba3798aE7e237db6/cc2f37bbade6307a.jpg!q90!cc_110x110.webp";
// 实现一个图片加载的功能 // 取得网上的某一张图片。 function loadImage(url) { return new Promise((resolve,reject) => { // 页面上建立一张图片 let img = document.createElement("img"); img.src = url ; // 当下载完成后,执行显示操作 img.onload = function() { // alert(url); resolve(img); } }); } // 当加载完成后,把所有图片一起显示出来 function showImage(img) { document.body.appendChild(img); }
Promise.race([ loadImage(url3), loadImage(url2) , loadImage(url1) ]).then(showImage); |
Generator
import "babel-polyfill";
它可以实现按步骤执行的操作。
className 属性:
object.className=classname
参考:
generator(生成器)是ES6标准引入的新的数据类型。一个generator看上去像一个函数,但可以返回多次。
ES6定义generator标准的哥们借鉴了Python的generator的概念和语法
函数在执行过程中,如果没有遇到return语句(函数末尾如果没有return,就是隐含的return undefined;),控制权无法交回被调用的代码。
function* fib(max) { var t, a = 0, b = 1, n = 0; while (n < max) { yield a; [a, b] = [b, a + b]; n ++; } return; } |
直接调用一个generator和调用函数不一样,fib(5)仅仅是创建了一个generator对象,还没有去执行它。
调用generator对象有两个方法,一是不断地调用generator对象的next()方法:
var f = fib(5); f.next(); |
next()方法会执行generator的代码,然后,每次遇到yield x;就返回一个对象{value: x, done: true/false},然后“暂停”。返回的value就是yield的返回值,done表示这个generator是否已经执行结束了。
如果done为true,则value就是return的返回值。
当执行到done为true时,这个generator对象就已经全部执行完毕,不要再继续调用next()了。
第二个方法是直接用for ... of循环迭代generator对象,这种方式不需要我们自己判断done:
for (var x of fib(10)) { console.log(x); // 依次输出0, 1, 1, 2, 3, ... } |
因为generator可以在执行过程中多次返回,所以它看上去就像一个可以记住执行状态的函数,利用这一点,写一个generator就可以实现需要用面向对象才能实现的功能。
generator还有另一个巨大的好处,就是把异步回调代码变成“同步”代码。
回调越多,代码越难看。
try { r1 = yield ajax('http://url-1', data1); r2 = yield ajax('http://url-2', data2); r3 = yield ajax('http://url-3', data3); success(r3); } catch (err) { handle(err); } |
使用此功能,可以实现下一步的操作。
这种选择还可以实现死循环。
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<style media="screen">
div{
width: 200px;
height: 200px;
border: 1px solid;
margin-right: 5px;
float: left;
}
.selected{
background-color: rgb(213,19,0);
}
.not_sel{
background-color: rgb(77,77,77);
}
</style>
<script type="text/javascript">
function* test(){
let allDiv=document.querySelectorAll("div");
//下一步功能
// for(let div of allDiv){
// allDiv.forEach(item=>item.className="not_sel");
// div.className="selected";
// yield "next";
// }
//死循环功能
while(true){
for(let div of allDiv){
allDiv.forEach(item=>item.className="not_sel");
div.className="selected";
yield "next";
}
}
return over;
}
let generator=test();
function changeNext(){
generator.next();
}
</script>
<title></title>
</head>
<body>
<div ></div>
<div ></div>
<div></div>
<br>
<p style="clear:both">
<input type="button" id="btn" onclick="changeNext();" value="next">
</p>
</body>
</html>
该功能还可以用在保存抽奖次数。
如果使用传统方式,一般会将抽奖次数通过全局变量来保存,但是这种方式安全性比较低。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="default-src *; style-src * 'unsafe-inline'; script-src * 'unsafe-inline' 'unsafe-eval'; img-src * data: 'unsafe-inline'; connect-src * 'unsafe-inline'; frame-src *;"> <title>ES6实战</title> <style></style> </head> <body> <p style="clear:both"> <input type="button" id="btn" οnclick="draw();" style="clear:both;" value="抽奖"> </p> <script type="text/javascript"> var count = 5; function draw() { if (count > 0) { count--; // 写一个逻辑,判断是否中奖 // 剩余次数 console.log(`剩余${count}次机会`); } } </script> </body>
</html> |
改为使用Generator就安全多了。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="Content-Security-Policy" content="default-src *; style-src * 'unsafe-inline'; script-src * 'unsafe-inline' 'unsafe-eval'; img-src * data: 'unsafe-inline'; connect-src * 'unsafe-inline'; frame-src *;"> <title>ES6实战</title> <style></style> </head> <body> <p style="clear:both"> <input type="button" id="btn" οnclick="generator.next();" style="clear:both;" value="抽奖"> </p> <script type="text/javascript"> function draw(count) { // 写一个逻辑,判断是否中奖 // 剩余次数 console.log(`剩余${count}次机会`); } let generator = checkDraw(5); function* checkDraw(count) { while(true){ if (count > 0) { count--; yield draw(count); } else { yield "a"; } } } </script> </body> </html> |
练习:
基于上面学习的所有内容实现一个 10 选 1 的彩票抽奖:
要求:
- 每10分钟开奖1次。
- 在未开奖之前,可以选择任意一个数作为自己选择的彩票号码。
- 设置用户在每次开奖阶段只有3次选号机会。
- 到抽奖时间判断用户是否中奖,并提示中奖金额。
监听select
Select中属性onchange = function()
var index = nSel.selectedIndex; // 选中索引
var value= nSel.options[index].value;
标签选择id ”#”
生成随机数:
Math.random(); 结果为0-1间的一个随机数(包括0,不包括1)
//可以取到端点
定时器:
1.倒计定时器:timename=setTimeout("function();",delaytime);
2.循环定时器:timename=setInterval("function();",delaytime);
//重复执行某个方法 var t1 = window.setInterval(hello,1000); var t2 = window.setInterval("hello()",3000); //去掉定时器的方法 window.clearInterval(t1); |
清除生成器状态
Generator.prototype.return()
返回给定的值并结束生成器。
debug:
let定义的generator改成var定义后可以在函数中改变状态
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Security-Policy" content="default-src *; style-src * 'unsafe-inline'; script-src * 'unsafe-inline' 'unsafe-eval'; img-src * data: 'unsafe-inline'; connect-src * 'unsafe-inline'; frame-src *;">
<title>ES6实战</title>
<style>
</style>
<script type="text/javascript">
var selectNum=-1;
var money=0;
function draw(count) {
// 写一个逻辑,判断是否中奖
// 剩余次数
console.log(`剩余${count}次机会`);
}
var generator = checkDraw(3);
function* checkDraw(count) {
while(true){
if (count > 0) {
count--;
console.log("count "+count);
yield true;
} else {
yield false;
}
}
return 0;
}
function changeCard(allCards){
var index=allCards.selectedIndex;
var times=generator.next().value;
console.log("generator "+times);
if(times){
if(index==0)return;
selectNum=allCards.options[index].value;
var selectDiv=document.querySelector("#select");
selectDiv.innerHTML="您选择的号码为"+selectNum;
}
}
function getAnswer(){
var answer=parseInt(Math.random()*10+1);//可以取到端点
var answerDiv=document.querySelector("#answer");
answerDiv.innerHTML="中奖号码为"+answer;
if(answer==selectNum){
money+=1;
var moneyDiv=document.querySelector("#money");
moneyDiv.innerHTML="获得金额"+money;
generator.return();
generator = checkDraw(3);//重置抽奖次数
}
}
window.setInterval("getAnswer()",10000);//10秒
</script>
</head>
<body>
<p style="clear:both">
选择号码:
<select class="cards" onchange="changeCard(this);">
<option value="-1">请选择号码</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
</select>
<div id="show">
<div id="select"></div>
<div id="answer"></div>
<div id="money">获得金额0</div>
</div>
<br>
<!-- <input type="button" id="btn" onclick="getAnswer();" style="clear:both;" value="抽奖"> -->
</p>
</body>
</html>