ES6 2天-3天
NodeJS 2天-3天
express 2天-3天
webpack 1天
MySQL数据库 2天
实战项目 2天
严格模式是为了避免一些混杂模式的出现 混杂模式其实就是普通模式 我们之前写的代码
有的时候 在JS中使用的语法不需要那么守规则
严格模式的定义 在页首定义一个字符串 ‘use strict’
那么一下的代码执行严格模式
严格模式和常规的普通模式之间的区别是什么
1.在严格模式中定义变量必须使用变量声明符
// 定义严格模式
// 'use strict';
// 严格模式 定义变量必须加上变量声明符
age = 18;
console.log(age);
'use strict';
// 如果说定义变量的时候 没有声明符 那么是修改变量
age = 18;
console.log(age);
2.在严格模式中不能删除变量
// 在严格模式中不能删除变量
// var num = 123;
// console.log(delete num);
// console.log(num);
// 严格模式中 不能删除
'use strict';
var num1 = 456;
console.log(delete num1);
3.在严格模式中 八进制必须使用0o开头
// 在严格模式中 八进制必须使用0o开头
// console.log(012);
// 严格模式中 必须是0o
'use strict';
// console.log(012);
console.log(0o123);
4.在严格模式中this不能指向window
// 在严格模式中this不能指向window
// function fun1(){
// console.log(this);
// }
// fun1();
// 严格模式中的this
'use strict';
function name() {
console.log(this);
}
name();
5.eval函数有自己独立的作用域
// eval函数有自己独立的作用域
// console.log('2+3'); // 2+3
// console.log(eval('2+3')); // 5
// eval('var num = 135');
// console.log(num);
// 严格你模式中 eval是有自己的作用域 不涉及全局
'use strict';
eval("var sex = '男'");
// console.log(sex);
console.log(eval('2+8'));
JSON主要的作用是传输数据 作为数据交互 或者是使用JSON作为配置文件使用
JSON独立于语言和平台 任何语言和平台都支持JSON的序列化和反序化
序列化 : 将对象转化成JSON 使用方法JSON.stringify
反序列化 : 将JSON串转化成对象 使用方法JSON.parse
// JSON的序列化
var obj1 = {
name : 'Eric',age : 18,sex : '男'};
var obj2 = [{
name : '奥迪',color : '黑色'},{
name : '奔驰',color : '粉色'}];
// 序列化
// stringify方法
var json1 = JSON.stringify(obj1);
var json2 = JSON.stringify(obj2);
console.log(typeof json1);
console.log(typeof json2);
console.log(json1);
console.log(json2);
// 反序列化
// parse方法
var newObj1 = JSON.parse(json1)
var newObj2 = JSON.parse(json2)
console.log(typeof newObj1);
console.log(typeof newObj2);
console.log(newObj1);
console.log(newObj2);
// 编辑一个JSON
var json = '{"name" : "宝马","color":"白色","price":9.9}';
console.log(json);
var obj = JSON.parse(json);
console.log(obj);
let变量其实let就是变量声明符 和var的效果是相同的 都是用来声明变量使用的
只不过他们之间有一点区别 let会更加先进一些 主要有三点不同
// let变量不能重复定义
// var num = 200;
// var num = 300;
// console.log(num);
let num = 600;
let num = 800; // 一旦变量重复定义 那么会产生报错
console.log(num);
块作用域 这里的块指的是大括号{} 也就是说 在大括号中定义的let变量那么在大括号的外部无法访问
那么可以产生块作用域的语句都有哪些 if for while do…while switch
// 块作用域
{
var name = 'Eric';
var age = 18;
}
console.log(name);
console.log(age);
// let 块作用域
let blacklist = '禁用';
{
console.log(blacklist);
let username = "admin";
let password = "123";
}
// console.log(username);
// console.log(password);
if (true){
let address = "吉林长春";
}
console.log(address);
var变量是存在变量提升的 但是let变量不存在
// 变量提升
// console.log(username);
// var username = "admin";
// let变量
console.log(password);
let password = '123';
// 暂时性死区
let username = "admin";
if (true){
console.log(username); //直接报错 不做变量提升
let username = "root";
}
小案例
<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>Documenttitle>
head>
<body>
<button>按钮1button>
<button>按钮2button>
<button>按钮3button>
<button>按钮4button>
<button>按钮5button>
<button>按钮6button>
<button>按钮7button>
<button>按钮8button>
body>
html>
<script>
// var oBtns = document.querySelectorAll('button');
// for (var i = 0; i < oBtns.length;i++){
// oBtns[i].asd = i;
// oBtns[i].onclick = function () {
// alert(this.asd);
// }
// }
var oBtns = document.querySelectorAll('button');
for (let i = 0; i < oBtns.length;i++){
oBtns[i].onclick = function () {
alert(i);
}
}
script>
常量是一个一经定义就不再改变的量 不能修改 不能重新定义 不能删除
他也是块作用域 除了不能修改 所有的属性几乎和let是一样的
修改指的是 不能修改堆栈空间 如果常量是引用类型 只是修改其中堆空间的内容 是可以存在的
建议 : 常量在定义的时候 一定要赋值
// 常量
// 定义常量 使用const关键字
const PI = 3.14;
console.log(PI);
// 重新定义
// const PI = 3.14159;
// console.log(PI);
// 块作用域
// if (true){
// const PI = 3.1415;
// console.log(PI);
// }
// console.log(PI);
// 不能修改
PI = 3.14159;
console.log(PI);
解构赋值就是将一个集合中的元素或者说是属性 一个一个的取出来赋给一个或者是多个变量
每一个数据解构的解构赋值都是不同的 数组的数据解构和对象的数据解构不同 解构方式也不同
数组的解构赋值 按照顺序进行解构 字符串和数组的数据解构相同 都是有序的 所以和数组保持一致
对象的解构赋值 按照键名进行解构
数组的解构赋值是按照数组的方式 使用数组进行解构 按照下标的顺序 变量的名字自定义
完全解构就是将数组中所有的值全部取出 赋给所有的变量 变量的数量和数组元素的数量相等
/*
数组的解构赋值
1.使用数组解构数组 中括号
2.数组的解构按照下标的顺序
3.变量可以自定义
*/
// let a,b,c,d,e = arr;
// console.log(a);
// console.log(e);
let [a,b,c,d,e] = arr;
console.log(a);
console.log(b);
console.log(c);
console.log(d);
console.log(e);
缺省情况有两种 变量大于数组元素数量 数组元素数量大于变量
但是缺省情况都不会报错 只是对数组的不完全解构
/*
缺省情况
数组元素的数量大于变量的数量
这种情况 不错报错 只是不完全解构 还是按照数组的下标进行解构
数组元素的数量小于变量的数量
这种情况 也不会报错 但是多余的变量会被赋值为undefined
*/
// let [a,b] = arr;
// console.log(a);
// console.log(b);
let [a,b,c,d,e,f] = arr;
console.log(a);
console.log(b);
console.log(c);
console.log(d);
console.log(e);
console.log(f); //undefined
在解构赋值的时候 如果说变量的数量多余数组元素 那么可以给变量设定默认值
// 数组解构
let arr = [22,66,88];
// 默认值从后往前设定默认值
let [a = 33,b = 55,c = 77,d = 99] = arr;
console.log(a);
console.log(b);
console.log(c);
console.log(d);
主要的目的是尽量少的使用栈空间的内存
// 占位符解构
let arr = [11,33,55,666,88,99];
let [,,,num] = arr;
console.log(num);
字符串的数据解构和数组是完全相同的 每一个字符都有自己的下标
都是有序的数据结构 可以迭代
// 字符串解构赋值
let str = "YES";
// console.log(str[0]);
// 完全解构
// let [a,b,c] = str;
// console.log(a);
// console.log(b);
// console.log(c);
// 不完全解构
// let [a,b,c,d] = str;
// console.log(a);
// console.log(b);
// console.log(c);
// console.log(d);
// 默认值
// let [a,b,c,d = 'M'] = str;
// console.log(a);
// console.log(b);
// console.log(c);
// console.log(d);
let [,a] = str;
console.log(a);
对象解构赋值 因为对象是无序的 所以说对象不能按照顺序进行解构 但是对象有键名
所以说 我们可以按照键名进行解构赋值 解构的时候 和数组不同
1.使用对象解构对象 使用大括号{}
2.按照键名解构赋值
3.可以不按照顺序进行解构
let obj = {
name : 'Eric',
age : 18,
sex : '男'
};
// console.log(obj);
// 如果说解构对象的时候 按照键名 不按照顺序
// let {name : uname,sex : usex,age : uage} = obj;
// console.log(uname);
// console.log(usex);
// console.log(uage);
// 缺省情况
// let {name : uname,age : uage} = obj;
// console.log(uname);
// console.log(uage);
// let {name : uname,sex : usex,age : uage,address : addr} = obj;
// console.log(uname);
// console.log(usex);
// console.log(uage);
// console.log(addr);
// 默认值解构
let {
name : uname,sex : usex,age : uage,address : addr = '吉林长春'} = obj;
console.log(uname);
console.log(usex);
console.log(uage);
console.log(addr);
特殊的对象解构赋值
// 特殊的解构赋值方式
let {
name,sex,age} = obj;
console.log(name);
console.log(age);
console.log(sex);
// 函数参数解构赋值 参数是数组得情况下
function fn1([a,b,c] = array) {
// console.log(array);
console.log(a);
console.log(b);
console.log(c);
}
// 定义一个数组 作为实参
let arr = [11,33,88];
fn1(arr)
// 参数是对象的情况下
let obj = {
name : 'Eric',age : 18};
// 对象的解构赋值和外面有点不一样
function fn2({
name,age} = {
name : 'Mary',age : 16}) {
// console.log(object);
console.log(name);
console.log(age);
}
fn2();
fn2(obj);
在ES6中新增了对象属性和方法的简写方式 他是ES6新语法 大大简化ES6的对象语法模式
属性的简写 : 当属性的值是一个标识符的时候 属性名和属性值保持一致的情况下 我们可以省略其一
let name = "admin";
let age = 18;
let sex = '男';
let address = '吉林长春';
// 定义一个对象
// let obj = {
// name : name,
// age : age,
// sex : sex
// }
// 对象属性的简写
let obj = {
name,
age,
sex,
address
}
console.log(obj);
方法的简写 : 省略function 直接使用方法名 然后形参列表
// 方法的简写
let obj1 = {
name : 'Eric',
say : function () {
console.log(this);
}
}
let obj2 = {
name,
say(){
console.log(this);
}
}
console.log(obj1);
console.log(obj2);
obj1.say();
obj2.say();
模板字符串是ES6中新增的语法解构 主要的作用是处理字符串拼接问题 和引号嵌套问题
在ES5中 定义字符串有两种方式 单引号和双引号 但是单双引号嵌套的时候 比较麻烦
在ES6中 我们可以使用反引号`` 声明字符串 但因引号在反引号中可以随意嵌套
因为单双引号在反引号中只是普通的字符 不能再作为引号使用
在末班字符串中 我们可以识别标识符 使用${标识符}
// let str1 = '截至3月10日09时,全国累计报告"确诊病例"102126例,现有确102152诊病例469例,境外输入5104例;海外现有确诊病例25002193例。'
let str1 = `你好 世界`;
console.log(str1);
let str2 = `截至3月10日09时,全国累计报告"确'诊"病"'例"10'2"1'2"6例,现有确102152诊病例469例,境外输入5104例;海外现有确诊病例25002193例。`;
console.log(str2);
// 识别标识符
let obj = {
name : '水经注',
price : 88.88,
page : 1300,
author : '郦道元'
};
let {
name,price,page,author} = obj;
let str = `推荐一本${
author}水利工程的书籍,名字叫做${
name},一共${
page}页,价值${
price}元`
console.log(str);
箭头函数 他是ES6的新语法 针对函数来说 现在应用非常广泛
在ES5中传统的函数体
function [函数名]([形参列表]){
函数体;
return 返回值;
}
函数名([实参列表])
在传统的函数中 相对来说比较麻烦
箭头函数简化了函数的编辑模式 主要的作用就是简单 灵活
箭头函数的结构
(形参列表)=>{
函数体
}
箭头函数和常规函数的区别是什么
1.箭头函数只能定义匿名函数
// 定义箭头函数
// 1.只能定义匿名函数
let fun1 = ()=>{
console.log('这是第一个箭头函数');
}
fun1();
2.箭头函数没有原型对象 不能定义构造
// 2.箭头函数没有原型对象 不能定义构造
function Car(name,color) {
this.name = name;
this.color = color;
}
let c = new Car('奥迪','黑色');
console.log(c);
// 使用箭头函数
let Cat = (name,color)=>{
this.name = name;
this.color = color;
console.log(this.color);
}
let a = new Cat('Tom','灰色'); //箭头函数 不是一个构造器
// a is not a constructor
3.箭头函数不能使用arguments new super
arguments主要的作用是传递不定参数使用 在箭头函数中无法使用
// 3.箭头函数中不能使用arguments
function fun2(){
console.log(arguments.length);
console.log(arguments);
// arguments.forEach(element => {
// console.log(arguments);
// });
// 判断数据类型
console.log(Object.prototype.toString.call(arguments));
}
fun2(1,2,3,4,5,6,7,8,9);
// 箭头函数使用arguments
let fun3 = ()=>{
console.log(arguments.length);
console.log(arguments);
}
fun3(1,2,3,4,5,6,7,8,9);
4.箭头函数特殊情况
a.只有一个参数的时候可以省略小括号
b.函数体只有一句话的时候 可以省略大括号
c.如果说函数体只有一句话 并且是返回值 省略大括号之后必须省略小括号
d.当省略大括号 返回值是对象的时候 需要使用小括号包裹
// 省略方式
// let fun1 = ()=>{
// console.log('你好 北京');
// }
// fun1();
// let fun2 = (m,n)=>{
// console.log('你好 北京');
// }
// fun2();
// 只有一个参数的时候 我们可以省略小括号(形参列表)
let fun3 = m=>{
console.log(m + 200);
}
fun3(200);
// 函数体只有一句话的时候 可以省略大括号
// let fun4 = ()=>{
// console.log('你好 朝阳');
// }
// fun4();
// 箭头函数省略大括号
let fun5 = ()=>console.log('你好 五方桥');
fun5();
// 如果说省略了大括号 但是这句话是返回值的时候
// let fun6 = ()=>{
// return '你好 中公';
// }
// console.log(fun6());
// 箭头函数省略return
let fun7 = ()=>'你好 优就业';
console.log(fun7());
// 如果说省略大括号 并且返回值是一个对象的时候 会出现错误
// let fun8 = ()=>{name : 'Eric',age : 18};
// console.log(fun8());
// 使用小括号包裹
let fun9 = ()=>({
name : 'Eric',age : 18})
console.log(fun9());
5.箭头函数没有自己的this
// 箭头函数没有自己的this
let obj1 = {
name : 'Mary',
say : function(){
console.log(this);
}
}
obj1.say();
// 使用箭头函数
// let obj2 = {
// name : 'Eric',
// say : ()=>{
// console.log(this);
// }
// }
// obj2.say();
// let obj3 = {
// name : 'Eric',
// say : function(){
// return {
// name : 'MM',
// say : function () {
// console.log(this);
// }
// }
// }
// }
let obj3 = {
name : 'Eric',
say : function(){
return {
name : 'MM',
say : ()=>{
console.log(this);
}
}
}
}
obj3.say().say();
不能修改 其他和let一样
使用反引号声明的字符串 可以随意嵌套引号 识别标识符 ${标识符}
主要的作用是 简单灵活
与普通函数之间的区别是什么
1.只有一个参数省略小括号
2.只有一句话的时候 省略大括号
3.生路大括号的时候 如果这句话是返回值 那么省略return
4.省略大括号的时候 返回值是对象的时候 那么必须使用小括号包裹
####################################################################
扩展运算符指的是…标识符 这个东西是一种新的运算模式 主要的作用有三点
使用该运算符 我们可以直接使用…后面跟上自定义的标识符 进行解开数据结构
let obj = {
name : 'Eric',
age : 18,
sex : '男'
}
let arr = [11,33,55,77,'你好',3.14,null,undefined,{
name:'Mary'}];
// 使用扩展运算符解开数组
console.log(...arr);
如果是单层数据 可以实现深拷贝 但是如果说是多层
比如说数组里面的对象或者是对象中的数组 那么我们只能实现浅拷贝
// 浅拷贝
let arr1 = [1,2,3,{
name:'Mary'}];
let arr2 = [...arr1];
console.log(arr2);
arr1.push(4)
console.log(arr1);
console.log(arr2);
arr1[3].age = 18;
console.log(arr1);
console.log(arr2);
let obj = {
name : 'Eric',
age : 18,
sex : '男'
}
let newObj = {
...obj
}
console.log(newObj);
也就是剩余参数 在常规函数中 我们使用arguments传递不定参数 但是在箭头函数中 不允许使用arguments
可以使用剩余参数 参数的名字自定义 参数前面加上…
// rest剩余参数
// let sum = (a,b)=>{
// return a + b;
// }
// console.log(sum(1,2,3));
// 传递不定参数 使用rest运算符
/*
在使用的时候 形参前面加上... 形参的名字自定义
他可以接受多个参数 那么参数会组成一个数组 带入到函数体中
在函数体中使用的时候 不要在加上...
使用的时候注意 在一个函数中 只能有一个rest参数
*/
let sum = (...item)=>{
// console.log(item);
let total = 0;
item.forEach(element => {
total += element
});
return total;
}
console.log(sum(1,2,3,4,5,6));
// rest运算符可以和普通参数一起进行传递 但是注意 普通参数只能写在前面
let sum = (a,...item)=>{
// console.log(item);
let total = 0;
item.forEach(element => {
total += element
});
return total;
}
console.log(sum(1,2,3,4,5,6));
总结
arguments和rest参数之间的区别
1.rest参数必须在形参列表中定义形参 但是arguments在形参列表中不能写任何内容
2.rest参数可以和普通参数一起传递
3.rest参数可以应用于箭头函数 但是arguments 不能
Symbol是ES6新增的一种数据类型 他是JS中的第七个数据类型
它主要的作用是保证数据的唯一性 使用的时候就像函数的调用
即使同时声明两个Symbol 那么他们的结果也是不一样的
Symbol只有一个作用 保证数据唯一 主要是保证对象中的键的唯一性
1.Symbol是唯一的 不重复
2.Symbol可以传递一个参数 作为标识 但是即使是参数一样 两个Symbol也不相等
3.Symbol不能和任何数据类型进行运算
// 使用Symbol 保证唯一性
let symbol1 = Symbol();
let symbol2 = Symbol();
console.log(symbol1 == symbol2);
console.log(symbol1);
console.log(typeof symbol1);
console.log(symbol2);
console.log(typeof symbol2);
// 因为Symbol无法区分 那么我们可以在Symbol中传递一个参数 作为标识
// 但是注意 即使我们传递相同的参数 那么他们也不相等
let name = Symbol('姓名');
let username = Symbol('姓名');
let age = Symbol('年龄');
console.log(name == username);
console.log(name);
console.log(username);
console.log(age);
// Symbol不能和任何数据类型进行运算
// console.log(age + 12);
// console.log(age + 1.2);
let name = Symbol('姓名');
let age = Symbol('年龄');
let school = Symbol('学校');
let address = '地址';
// 使用Symbol
// 第一种方式 我们直接使用 使用中括号
let obj = {
[name] : 'Eric',
[age] : 18,
[school] : '吉林大学',
[address] : '吉林长春'
};
console.log(obj);
// 第二种 追加属性
let obj = {
[name] : 'Eric',
[age] : 18,
}
obj[school] = '吉林大学';
obj[address] = '吉林长春';
console.log(obj);
// 第三种 使用方法defineProperty
// 内置三个参数 第一个参数是原对象 第二个参数是Symbol 第三个参数是描述
// 描述还是一个对象 对象中一个键 value 值是我们新增的内容
let obj = {
};
Object.defineProperty(obj,name,{
value : '康柏松'
});
console.log(obj);
set是一个无序的 不重复的集合 它主要的特点就是不重复 主要用来去重
我们使用set集合的时候 需要new 也就是说他是一个对象
使用的时候 必须先进行实例化 可以传递一个参数 参数是可选
如果传递参数 参数是数组 返回的结果是set集合 去重之后的数组
// set集合
let arr = [11,33,99,88,66,99,88,66,99,88,66,99,88,66,99,88,66];
// 实例化set集合
let set = new Set(arr);
let set1 = new Set();
console.log(set);
console.log(set1);
// 如果使用set去重数组
let arr2 = [...set];
console.log(arr);
console.log(arr2);
add : 添加一个set元素
delete : 删除一个set元素
has : 查看元素是否存在
size属性 : 查看set中的元素数量
clear : 清空set集合
let arr = [2,4,6,8,4,6,8,4,6,8,4,6,8,4,6,8];
let set = new Set(arr);
console.log(set);
// 添加set元素
set.add(9);
set.add('Eric');
console.log(set);
// 查看一个set元素是否存在
console.log(set.has(8));
console.log(set.has(9));
console.log(set.has(0));
console.log(set.has('Eric'));
// 删除一个set元素
set.delete('Eric');
console.log(set);
// 查看set集合的大小
console.log(set.size);
// 清空set集合
set.clear();
console.log(set);
map也是一个无序的不重复的集合 他的结构类似于对象 set结构类似与数组 map值键值对组成的
使用的方法和set几乎是一样的 都是通过实例化来创建
实例化Map 可以内置一个参数 也可以不传递参数 参数是可选的 不传递参数 是一个空的map集合
如果说传递参数 参数是一个数组 并且是一个二维数组 数组中的每一个元素都是一个子数组
子数组中内置两个元素 第一个元素作为键 第二个元素作为置
// map使用
let arr = [
['name','Eric'],
['age',18],
['name','康柏松'],
['oldName','Eric'],
['rename','Eric'],
['sex','男']
]
let map = new Map(arr);
let map1 = new Map();
console.log(map);
console.log(map1);
set : 添加一个map元素 内置两个参数 第一个是键 第二个是值
delete : 删除一个map元素
has : 查看元素是否存在
size属性 : 查看map中的元素数量
get : 获取一个map集合元素
clear : 清空map集合
iterator是一个ES6中新增的可迭代任何可迭代数据类型的工具
主要的作用是迭代数组 字符串 set集合 map集合 注意 不能迭代对象
原理 首先创建一个迭代指针 每一次调用next方法 我们返回指向的数据
如果说数据存在 那么返回 {value : 指向的数据(值), done : false}
如果说指针到了最后 无数据可以指向 那么返回 {value : undefined , done : true}
// 定义一个数组 准备迭代
let arr = [66,88,99];
// 定义一个字符串 准备迭代
let str = "YES";
// 定义iterator函数
function myIterator(object){
// 定义迭代指针
let index = 0;
// 尅是迭代
return {
next : ()=>{
// if (object.length > index){
// return {value : object[index++],done : false};
// }else{
// return {value : undefined,done : true};
// }
return object.length > index ? {
value : object[index++],done : false} : {
value : undefined,done : true};
}
}
}
let iter = myIterator(arr);
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
let strIter = myIterator(str);
console.log(strIter.next());
console.log(strIter.next());
console.log(strIter.next());
console.log(strIter.next());
使用iterator 方法名叫做[Symbol.iterator] 使用它可以迭代数组 字符串 集合 不能迭代对象
// 使用iterator迭代数组
let arr = [66,88,99];
// 使用对象中的[Symbol.iterator]方法进行迭代 返回的结果调用next方法
let arrIter = arr[Symbol.iterator]();
console.log(arrIter.next());
console.log(arrIter.next());
console.log(arrIter.next());
console.log(arrIter.next());
// 使用iterator迭代字符串
let str = 'YES';
let strIter = str[Symbol.iterator]();
console.log(strIter.next());
console.log(strIter.next());
console.log(strIter.next());
console.log(strIter.next());
// 使用iterator迭代set集合
let set = new Set(arr);
let setIter = set[Symbol.iterator]();
console.log(setIter.next());
console.log(setIter.next());
console.log(setIter.next());
console.log(setIter.next());
// 使用iterator迭代map集合
let map = new Map([['name','Eric'],['age',18]]);
let mapIter = map[Symbol.iterator]();
console.log(mapIter.next());
console.log(mapIter.next());
console.log(mapIter.next());
// iterator不能迭代对象
let obj = {
name : 'Eric',age : 18};
let objIter = obj[Symbol.iterator](); // 报错 : obj[Symbol.iterator] is not a function
语言的底层已经对该功能有了部分的封装 我们直接使用封装后的语句即可
iterator的语法糖是for…of语句
// 使用for...of迭代数组
let arr = [66,88,99];
for (let value of arr){
console.log(value);
}
// 使用for...of迭代字符串
let str = 'YES';
for (let value of str){
console.log(value);
}
// 使用for...of迭代set集合
let set = new Set(arr);
for (let value of set){
console.log(value);
}
// 使用for...of迭代map集合
let map = new Map([['name','Eric'],['age',18]]);
for (let value of map){
console.log(value);
}
// for...of不能迭代对象
let obj = {
name : 'Eric',age : 18};
for (let value of obj){
// 报错 : obj is not iterable
console.log(value);
}
// iterator迭代对象
let obj = {
name : 'Eric',age : 18};
obj[Symbol.iterator] = function(){
// 定义指针
let index = 0;
// 首先 我们将对象中的所有的键 全部取出 作为一个数组
let keys = Object.keys(this);
// 开始迭代
return {
next : ()=>{
if (keys.length > index){
return {
value : this[keys[index++]] , done : false};
}else{
return {
value : undefined , done : true};
}
}
}
}
let objIter = obj[Symbol.iterator]();
console.log(objIter.next());
console.log(objIter.next());
console.log(objIter.next());
for…of , for…in和forEach之间的区别
for…of是iterator的语法糖 可以迭代iterator能迭代的所有数据类型 主要是数组 字符串 set map
for…in是for循环的语法结构 可以迭代数组 对象 主要是是唯一一个可以迭代对象的流程与句
forEach不同于前二者 前二者都是流程与句 而forEach是方法 数组中的方法 只能遍历数组 因为是数组的方法
所以说只能针对数组
ES6提供的解决异步编程的方案之一,Generator函数是一个状态机, 可控制函数执行过程
可暂停函数(惰性求值), 内部yield可暂停,外部调用next方法可启动,每次返回的是yield后的表达式结果
定义generator需要在function后面加上* ,调用函数不会打印任何函数数据,而是会返回一个指针对象
调用指针对象中的next方法,来执行generator函数中的内容,返回结果和iteraator相似
// function mmm(){
// yield '你好 世界1';
// yield '你好 世界2';
// yield '你好 世界3';
// yield '你好 世界4';
// }
// 使用generator
function* myGenerator(){
yield '你好 世界';
yield '你好 中国';
yield '你好 北京';
yield '你好 朝阳';
yield '你好 中公';
yield '你好 优就业';
yield '你好 Web1116';
return 'Game Over';
}
let go = myGenerator();
// 调用函数的执行过程 使用go中的next方法
// 每一次调用执行yield后面的表达式 比如说第一次调用 出现你好 世界 返回的也是一个对象 和iterator比较相似
// 当第二次调用next方法的时候 那么出现的就是第二个yield后面的表达式
// 以此类推 一直到指针指向最后数据
console.log(go.next());
console.log(go.next());
console.log(go.next());
console.log(go.next());
console.log(go.next());
console.log(go.next());
console.log(go.next());
console.log(go.next());
<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>Documenttitle>
<style>
*{
padding: 0px;
margin: 0px;
}
#app{
width: 1200px;
margin: 0px auto;
}
#app div{
width: 100%;
height: 40px;
font-size: 20px;
line-height: 40px;
border: 1px solid;
text-align: center;
margin-top: 2px;
}
button{
width: 1200px;
height: 40px;
font-size: 20px;
line-height: 40px;
border: none;
outline: none;
background: rgb(247, 247, 247);
color: rgb(80, 120, 255);
display: block;
margin: 20px auto;
}
button:hover{
background: rgb(238, 238, 238);
color: rgb(80, 120, 255);
}
style>
head>
<body>
<div id="app">
<div>加载出1——20条数据div>
div>
<button id="btn">点击加载更多button>
body>
html>
<script>
function* loadData(){
yield '加载出21——40条数据';
yield '加载出41——60条数据';
yield '加载出61——80条数据';
yield '加载出81——100条数据';
yield '加载出101——120条数据';
yield '加载出121——140条数据';
yield '加载出141——160条数据';
return '没有更多数据了';
}
// 抓取节点
let btn = document.querySelector('#btn');
let app = document.querySelector('#app');
// 调用函数loadData
let go = loadData();
btn.onclick = function(){
// 创建div节点 将数据存储在div节点中
let data = go.next().value;
if (data == undefined){
return false;
}else{
let div = document.createElement('div');
div.innerHTML = data;
app.appendChild(div);
}
}
script>
Promise 是异步编程的一种解决方案,比传统的回调函数和事件更合理、更强大
ES6的Promise是一个构造函数, 用来生成Promise实例, Promise实例是异步操作管理者
Promise代表了未来某个将要发生的事件(通常是一个异步操作) 有了Promise对象
可以将异步操作以同步的流程表达出来, 避免了层层嵌套的回调函数(回调地狱)
Promise本身还是在使用回调函数(只不过比回调函数多了一种状态管理)
创建promise对象,实例化Promise
参数是回调函数,内置两个参数,resolve和reject
promise三个状态
初始化状态 pending
成功状态 fullfilled
失败状态 rejected
/*
Promise是一个构造函数 所以说创建promise我们需要实例化
实例化promise内置一个参数 参数是一个回调函数
回调内置两个参数
第一个参数是Promise的成功状态
第二个参数是Promise的失败状态
两个参数其实都是函数
返回的结果是Promise实例
Promise有三种状态
第一种状态 许诺状态 也是初始化状态
第二种状态 成功状态
第三种状态 失败状态
*/
let promise = new Promise((resolve,reject)=>{
// 初始化
let num = 10;
// console.log(resolve());
// console.log(typeof resolve);
// console.log(reject);
// console.log(typeof reject);
// 初始化完毕之后 我们对初始化的条件进行判断
if (num > 10){
// 如果说成功 也就是初始化条件成立 那么我们调用resolve成功状态
// 注意 这是一个函数 所以说调用的时候 别忘了加括号 也可以传递实参
resolve('成功了');
}else{
// 如果失败 调用reject失败状态
// 他也是一个函数 也可以传递实参
reject('失败了')
}
})
then : 触发resolve方法执行then
// 我们定义好了promise之后 我们需要对Promise对象进行触发
// 触发使用Promise对象中的then方法 then方法可以内置一个或者两个参数 都是回调函数
// 第一个回调函数 其实就是resolve函数
// 第二个回调函数 其实就是reject
promise.then((data)=>{
console.log('这是触发Promise' + data);
},(err)=>{
console.log('这是触发Promise' + err);
})
catch : 触发reject方法执行catch
// 使用异常处理链式调用
let promise = new Promise((resolve,reject)=>{
let num = 10;
if (num > 19){
resolve('成功');
}else{
// console.log(aaa);
reject('失败');
}
})
// 使用then进行触发
// promise.then(data=>console.log(data),err=>console.log(err));
// 我们一般情况下 then中只会出现一个参数 参数是成功
// 失败的情况下 我们会使用catch进行触发
promise.then((data)=>{
console.log(data);
}).catch((err=>{
console.log(err);
}))
// 特点
// let a = promise.then(data=>console.log(data));
// 当我们then触发了Promise之后 我们返回的结果依然是一个promise
// console.log(a);
// a.catch(error=>console.log(error));
在使用then的时候是无法捕获异常的,但是在catch中可以捕获异常
try : 可能出现异常的代码
catch : 如果try出现错误,代码执行catch
finally : 不管会不会发生异常,都会执行finally中的代码
// 异常处理
/*
try : 可能出现异常的代码
catch : 如果try出现错误,代码执行catch
finally : 不管会不会发生异常,都会执行finally中的代码
在程序中 我们通常把可能会出现错误的代码存储在try中
如果说 try中出现了错误 那么不会报错 执行执行catch中的代码
finally 无论代码是否错误 都会执行
*/
// console.log(num);
// let num = 100;
try{
// 如果说num存在报错信息 那么不执行 直接执行catch
console.log(num);
}catch{
// 如果说try中没有报错信息 这里不会执行
console.log('num没有定义 直接执行我');
}finally{
console.log('我都执行');
}
then方法的第一个参数,成功时的回调函数,分两种情况
返回了一个普通的数据(非promise),这个值会作为参数传递给下一个then的成功回调
返回了一个promise,下一个then的执行,取决于这个promise状态的改变
all : 执行所有的promise
race : 执行最快的promise
ES6的新的数据类型 它主要的作用是保证数据的唯一性
两个Symbol不可能相等 即使是传递了相同的参数 也不能相等
Symbol不能和其他数据类型进行运算
是一个无序的不重复的集合 结构类似与数组 使用的时候需要实例化
内置的方法 add delete has size属性 clear
是一个无序的不重复的集合 结构类似与对象 使用的时候需要实例化
内置的方法 set delete get has size属性 clear
他是为了所有的可迭代的数据类型添加的一个接口访问机制 也就是说 他是可以迭代数据类型的
迭代数组 字符串 set集合 map集合 不能迭代对象
使用方法[Symbol.iterator]
但是如果说为了更方便的使用 我们可以使用语法糖
for…of 迭代语句
for…of迭代语句 他可以迭代数组 字符串 集合 其实他是iterator的语法糖 可以迭代iterator所有可迭代的数据类型
它主要的作用是解决异步问题 他是可以控制函数的执行过程 进行惰性求职 可以暂停函数
内置由yield组成 外部使用next调用
一旦程序触碰到yield 那么程序会陷入阻塞状态
他是ES6中解决异步编程方案之一 通俗一点说 就是将异步变成同步操作
他是一个构造函数 使用的时候需要实例化 比我们之前使用的回调更加合理 避免回调地狱
它本身有三种状态
1.初始化状态
2.成功状态
3.失败状态
我们使用的时候 需要先进行实例化 内置一个参数 参数是回调函数
回调内置两个参数 第一个参数是成功状态 第二个参数是失败状态 他们都是函数
定义之后 我们使用then catch方法进行触发promise
then触发成功状态 catch触发异常 失败
触发之后返回值还是一个Promise
####################################################################
Promise 是异步编程的一种解决方案,比传统的回调函数和事件更合理、更强大
ES6的Promise是一个构造函数, 用来生成Promise实例, Promise实例是异步操作管理者
Promise代表了未来某个将要发生的事件(通常是一个异步操作) 有了Promise对象
可以将异步操作以同步的流程表达出来, 避免了层层嵌套的回调函数(回调地狱)
Promise本身还是在使用回调函数(只不过比回调函数多了一种状态管理)
创建promise对象,实例化Promise
参数是回调函数,内置两个参数,resolve和reject
promise三个状态
初始化状态 pending
成功状态 fullfilled
失败状态 rejected
/*
Promise是一个构造函数 所以说创建promise我们需要实例化
实例化promise内置一个参数 参数是一个回调函数
回调内置两个参数
第一个参数是Promise的成功状态
第二个参数是Promise的失败状态
两个参数其实都是函数
返回的结果是Promise实例
Promise有三种状态
第一种状态 许诺状态 也是初始化状态
第二种状态 成功状态
第三种状态 失败状态
*/
let promise = new Promise((resolve,reject)=>{
// 初始化
let num = 10;
// console.log(resolve());
// console.log(typeof resolve);
// console.log(reject);
// console.log(typeof reject);
// 初始化完毕之后 我们对初始化的条件进行判断
if (num > 10){
// 如果说成功 也就是初始化条件成立 那么我们调用resolve成功状态
// 注意 这是一个函数 所以说调用的时候 别忘了加括号 也可以传递实参
resolve('成功了');
}else{
// 如果失败 调用reject失败状态
// 他也是一个函数 也可以传递实参
reject('失败了')
}
})
then : 触发resolve方法执行then
// 我们定义好了promise之后 我们需要对Promise对象进行触发
// 触发使用Promise对象中的then方法 then方法可以内置一个或者两个参数 都是回调函数
// 第一个回调函数 其实就是resolve函数
// 第二个回调函数 其实就是reject
promise.then((data)=>{
console.log('这是触发Promise' + data);
},(err)=>{
console.log('这是触发Promise' + err);
})
catch : 触发reject方法执行catch
// 使用异常处理链式调用
let promise = new Promise((resolve,reject)=>{
let num = 10;
if (num > 19){
resolve('成功');
}else{
// console.log(aaa);
reject('失败');
}
})
// 使用then进行触发
// promise.then(data=>console.log(data),err=>console.log(err));
// 我们一般情况下 then中只会出现一个参数 参数是成功
// 失败的情况下 我们会使用catch进行触发
promise.then((data)=>{
console.log(data);
}).catch((err=>{
console.log(err);
}))
// 特点
// let a = promise.then(data=>console.log(data));
// 当我们then触发了Promise之后 我们返回的结果依然是一个promise
// console.log(a);
// a.catch(error=>console.log(error));
在使用then的时候是无法捕获异常的,但是在catch中可以捕获异常
try : 可能出现异常的代码
catch : 如果try出现错误,代码执行catch
finally : 不管会不会发生异常,都会执行finally中的代码
// 异常处理
/*
try : 可能出现异常的代码
catch : 如果try出现错误,代码执行catch
finally : 不管会不会发生异常,都会执行finally中的代码
在程序中 我们通常把可能会出现错误的代码存储在try中
如果说 try中出现了错误 那么不会报错 执行执行catch中的代码
finally 无论代码是否错误 都会执行
*/
// console.log(num);
// let num = 100;
try{
// 如果说num存在报错信息 那么不执行 直接执行catch
console.log(num);
}catch{
// 如果说try中没有报错信息 这里不会执行
console.log('num没有定义 直接执行我');
}finally{
console.log('我都执行');
}
then方法的第一个参数,成功时的回调函数,分两种情况
返回了一个普通的数据(非promise),这个值会作为参数传递给下一个then的成功回调
返回了一个promise,下一个then的执行,取决于这个promise状态的改变
let promise = new Promise((resolve,reject)=>{
let arr = '获取一级栏目数据';
if (arr.length > 0){
resolve(arr);
}
})
// 触发Promise
// 因为promise触发得到的结果是一个promise对象 所以说 我们可以继续触发
// 前一个then中的回调函数中的返回值 作为新的promise的resolve状态 成功状态
let a = promise.then(data=>{
console.log(data);
return data + '中的食品';
}).then(data=>{
console.log(data);
return data + '中的元宵';
}).then(data=>{
console.log(data);
return data + '只提供一人份';
}).then(data=>{
console.log(data);
return data + '豆沙馅的'
}).then(data=>{
console.log(data);
})
all : 执行所有的promise
let p1 = new Promise((resolve,reject)=>{
setTimeout(() => {
console.log('p1');
resolve('这是第一个promise');
}, 3000);
})
let p2 = new Promise((resolve,reject)=>{
setTimeout(() => {
console.log('p2');
resolve('这是第二个promise');
}, 1000);
})
let p3 = new Promise((resolve,reject)=>{
setTimeout(() => {
console.log('p3');
resolve('这是第三个promise');
}, 2000);
})
// 使用Promise中的all方法 执行所有的Promise
// all方法是Promise对象中的静态方法
// 内置一个参数 参数是一个数组 数组中的每个元素是你要执行的所有的promise
// 执行所有的Promise 但是没有顺序
let a = Promise.all([p1,p2,p3]);
// Promise.all([p1,p2,p3]).then(data=>console.log(data));
a.then(d=>console.log(d))
race : 执行最快的promise
let p1 = new Promise((resolve,reject)=>{
setTimeout(() => {
// console.log('p1');
resolve('这是第一个promise');
}, 3000);
})
let p2 = new Promise((resolve,reject)=>{
setTimeout(() => {
// console.log('p2');
resolve('这是第二个promise');
}, 1000);
})
let p3 = new Promise((resolve,reject)=>{
setTimeout(() => {
// console.log('p3');
resolve('这是第三个promise');
}, 2000);
})
// race方法 执行最快的Promise
// 传参方式和all一样
// 只执行一个Promise 执行速度最快的
let a = Promise.race([p1,p2,p3]);
a.then(d=>console.log(d));
<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>Documenttitle>
head>
<body>
<div id="app">
<input type="text" id="user">
<input type="button" id="btn" value="搜索">
div>
body>
html>
<script src="./jquery.js">script>
<script>
// let p1 = new Promise((resolve,reject)=>{
// $.ajax({
// url : './index.php',
// type : 'get',
// data : {user : 'Eric'},
// success : (res)=>{
// resolve(res);
// },
// error : (err)=>{
// reject(err);
// }
// })
// })
// p1.then(d=>{
// console.log(d);
// return d;
// }).then(d=>{
// console.log(d);
// $.ajax({
// url : './server.php',
// type : 'get',
// data : {user : d},
// success : (res)=>{
// console.log(res);
// }
// })
// })
$('#btn').click(function(){
let search = $('#user').val();
let p = new Promise((resolve,reject)=>{
$.ajax({
url : './index.php',
type : 'get',
success : (data)=>{
resolve(data);
}
})
})
p.then(data=>{
let arr = JSON.parse(data);
let result = arr.filter(element=>element.sex == search);
// console.log(result);
return result;
}).then(data=>{
let second = 10;
let result = data.filter(element=>element.age > 10);
console.log(result);
})
})
script>
谈谈你对promise的理解
Promise用来解决异步回调问题,由于js是单线程的,很多异步操作都是依靠回调方法实现的,
这种做法在逻辑比较复杂的回调嵌套中会相当复杂;也叫做回调地狱;
promise用来将这种繁杂的做法简化,让程序更具备可读性,可维护性;
promise内部有三种状态,pedding,fulfilled,rejected;
pedding表示程序正在执行但未得到结果,即异步操作没有执行完毕,fulfilled表示程序执行完毕,且执行成功,
rejected表示执行完毕但失败;这里的成功和失败都是逻辑意义上的;并非是要报错。
其实,promise和回调函数一样,都是要解决数据的传递和消息发送问题,
promise中的then一般对应成功后的数据处理,catch一般对应失败后的数据处理。
async…await是基于promise的generator语法糖,它用来等待promise的执行结果,常规函数使用await没有效果;
async修饰的函数内部return不会得到预期的结果,会得到一个promise对象;
await等待的promise结果是resolve状态的内容,reject状态的内容需要使用try…catch获取,
await关键字必须要出现在async修饰的函数中,否则报错。
主要来说 async函数的特点是
1.获取promise中的resolve状态或者是reject状态
2.async函数的返回值是一个promise
3.程序一旦触碰到await 程序陷入阻塞状态 知道将await右面的表达式执行完毕
// async函数的返回值都是一个promise
// async function fn(){
// console.log('你好');
// }
// console.log(fn());
// async函数中的await能够获取promise的状态 不需要then和catch
let promise = new Promise((resolve,reject)=>{
resolve('成功')
})
async function fn(){
let v1 = await promise
console.log(v1);
}
fn();
// async函数中存在await 如果程序碰到await 那么陷入阻塞状态
let p1 = new Promise((resolve,reject)=>{
setTimeout(() => {
resolve('这是p1 1s');
}, 1000);
})
let p2 = new Promise((resolve,reject)=>{
setTimeout(() => {
resolve('这是p2 3s');
}, 3000);
})
let p3 = new Promise((resolve,reject)=>{
setTimeout(() => {
resolve('这是p3 2s');
}, 2000);
})
// 这种方式 最先执行的是p2
// p1.then(d=>console.log(d));
// p2.then(d=>console.log(d));
// p3.then(d=>console.log(d));
// 如果说 我们使用async函数
async function test(){
let v1 = await p1;
console.log(v1);
let v2 = await p2;
console.log(v2);
let v3 = await p3;
console.log(v3);
}
test();
ES6中的面向对象是和其他语言基本比较相似 JS正常来说是基于对象语言
很多程序都是基于对象开发 不是面向对象 但是在ES6中 我们引入了其他语言中的面向对象
面向过程就是 你需要什么 写什么
面向对象 他就是 你直接封装好一个功能 哪里需要的时候 在哪里调用
面向对象的好处
1.避免代码冗余
2.便于后期维护
类 : 类泛指一类事务 比较抽象
对象 : 所有实际存在的实体
对象和类之间的关系是什么 : 包含与被包含的关系
类是对象的模板 对象是类的实例
类是对象的模具 对象是类的铸件
类的组成是由成员组成
定义类 使用关键字class 后面跟上类名 类名的命名是帕斯卡命名法
后面直接跟上大括号 注意 没有形参列表
类的内部组成主要是成员
成员属性 : 属性是一个事务自身的特征 特性 一般是形容词或者名词
在类中 类似于变量 但是不同与变量 因为成员在类中是全局的
成员方法 : 方法是一个事务的行为 一般是动词
在类中类似与函数 但是不同于函数 因为他也是全局的
定义一个类
class Car{
// 定义汽车属性
name = '红旗';
color = '黑色';
// 定义汽车方法
start(){
console.log(`这是一辆${
this.color}的${
this.name}车`);
}
}
类定义好了是不能直接使用的 那么我们需要将类实例化 创建出一个对象
使用new 关键字 进行实例化 注意 实例化类名的时候 有实参列表
class Car{
// 定义汽车属性
name = '红旗';
color = '黑色';
// 定义汽车方法
start(){
console.log(`这是一辆${
this.color}的${
this.name}车`);
}
}
let c = new Car();
console.log(c.name);
console.log(c.color);
c.start();
以上定义的类有一个缺点 上述的类中 只能有一个对象 是一个死类 所以说 不适合开发中使用
如果说定义一个活类 那么就需要不确定的数据
如果说需要活着的数据 那么我们就需要向类中进行传参
构造器 : 他是一个类中的内置方法 方法的名字是固定的 constructor
即使你不定义 这个方法也是存在的 只不过方法是空的而已
其实这个方法是一个魔术方法 不需要我们手动调用 在满足条件的情况下 会被自动调用
那么构造器 在我们实例化的同时 被调用
它主要的作用是 : 主要用来初始化类的成员 尤其是成员属性
初始化的数据主要是传参
<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>Documenttitle>
head>
<body>
body>
html>
<script>
// class Cat{
// constructor() {
// console.log('类已经被实例化了');
// }
// }
// let c = new Cat();
// let a = new Cat();
// let b = new Cat();
// console.log(c);
// console.log(typeof c);
class Cat{
constructor(name,color,age) {
this.name = name;
this.color = color;
this.age = age
// 初始化成功
// console.log(this.name,this.age,this.color);
}
// 定义方法
eat(food){
console.log(`${
this.name}在吃东西 吃的是${
food}`);
}
}
let c = new Cat('Tom','灰色',3);
// console.log(c);
c.eat('奶酪');
script>
继承指的是一个类中有另一个类的属性和方法 虽然没有定义 但是可以使用另一个类中的属性和方法
继承使用关键字 extends
继承
class Worker{
// 基类
constructor(name){
this.name = name;
}
kq(){
console.log(this.name + '的考勤信息');
}
jx(){
console.log(this.name + '的绩效');
}
}
class Boss extends Worker{
// 派生类
}
let w = new Worker('Eric');
w.kq();
w.jx();
let b = new Boss('Mary');
b.jx();
b.kq();
方法的重写
class Worker{
// 基类
constructor(name){
this.name = name;
}
kq(){
console.log(this.name + '的考勤信息');
}
jx(){
console.log(this.name + '的绩效');
}
}
class Boss extends Worker{
// 派生类
skq(){
console.log(this.name + '审核考勤');
}
sjx(){
console.log(this.name + '审核绩效');
}
// 方法的重写 覆写 重载
// 直接命名相同的名字 覆盖父类中的方法
jx(){
console.log(this.name + '说 我的绩效和你的不一样 我是按年的');
}
}
let w = new Worker('Eric');
w.kq();
w.jx();
let b = new Boss('Mary');
b.jx();
b.kq();
b.sjx();
b.skq();
继承中可以继承方法 也可以进行方法的重写 但是有一点瑕疵
如果说父类的构造器无法满足子类的时候 我们怎么重写
不能直接重写 需要去继承父类的构造器 然后在加以补充
调用父类的构造器 使用super方法进行调用 继承的时候 必须继承父类构造器中的所有参数
class Worker{
constructor(name){
this.name = name;
}
jx(){
console.log(this.name + '的绩效');
}
}
class Boss extends Worker{
constructor(name,money) {
// Worker.constructor(name)
super(name);
this.money = money;
}
sjx(){
console.log(this.name + '审核绩效');
}
jx(){
console.log(this.name + '说 我的绩效和你的不一样 我是按年的' + '我的工资是' + this.money);
}
}
let w = new Worker('Eric');
w.jx();
let b = new Boss('Mary',60000);
b.jx();
b.sjx();
静态方法是比较特殊的方法 他不需要实例化 直接调用就可以
他的运行速度比较快 大约是普通的对象方法的1.5倍 快了55%
一般情况下 在我们不需要实例化的情况下 或者是不考虑封装的情况下 我们可以使用静态方法 因为快
静态方法需要使用bstatic进行修饰
在调用的时候 直接使用类名.方法名()
class Person{
static name = 'MM';
constructor(name,age) {
this.name = name;
this.age = age;
}
eat(){
console.log('吃饭');
}
// 定义静态方法
static say(name){
// console.log(name + '说话');
// console.log(this.name + '说话');
console.log(Person.name + '和' + name + '在说话');
}
}
let p = new Person('Mary',13);
// 使用静态方法
Person.say('Eric');
// Person.eat();
// 使用eat需要实例化
// let p = new Person('Mary',13);
// console.log(p);
// p.say('nnn');
注意事项
1.静态方法不需要实例化
2.静态方法速度比较快
3.静态方法不能使用对象调用
4.静态方法中不能使用this
5.静态方法中不能使用非静态成员 如果使用 先定义静态
它主要的作用是将异步操作变成同步操作 ES6中的异步问题解决方案之一 他是一个构造函数
他有三种状态 初始化状态 成功状态 失败状态
定义好promise之后 我们使用promise对象中的then方法进行调用
使用catch方法进行处理异常
链式操作 前一个成功状态函数的返回值作为新的promise的resolve状态
all方法 : 执行所有的promise
race方法 : 执行最快的Promise
他是真正意义上的解决异步问题之一 使用async定义函数 他是promise和generator的语法糖
async定义的函数返回值是一个Promise async函数中有await 而await只能存在在async修饰的函数中
它主要的作用 阻塞程序 当程序触碰到await的时候 程序陷入阻塞状态 直到执行完毕await后面的表达式
和yield比较相似 但是不同的是 下一个yield需要next方法进行调用 但是await是自动执行的
await可以直接获取promise中的成功状态 失败状态
面向对象的概念 实现的就是封装代码
面向对象的三大特性 封装 继承 多态
定义对象使用class 继承使用extends 构造器使用constructor 继承构造器使用super
静态使用static 实例化使用new
###########################################################################
模块其实就是一个JS文件 暂时我们这样理解 也就是说 我们之前看到的jquery 他就是一个模块
在ES5中 我们引入和使用模块需要在html中进行使用 JS文件是不能单独运行的 现在也不能
我们之前在html中使用模块的时候 使用script标签中的src属性 其实这个属性特别不好
使用的方式不是特别安全
在ES6中 我们使用的是新的模块操作 使用的时候 我们可以使用import进行引入
我们可以在JS中引入JS
以前 我们一个JS想使用另一个JS文件 那么必须在html中产生依赖
现在 我们可以在一个JS中引入另一个 也可以在html中进行引入
除了引入之外还有暴露 保证数据的安全性
如果说使用这种模块机制 必须满足两个条件
1.必须在服务器中运行
2.如果在html中 那么script标签中必须加上type=‘module’
使用export进行暴露数据
一个模块就是一个独立的 js文件 该模块中的所有变量 外部无法获取
如果外部希望可以读取模块中的内容 export 关键字 暴露 export
如果不暴露 那么html中不能引入
var username = 'Eric';
var age = 18;
var obj = {
username,age};
var arr = [1,2,3];
var fun = ()=>{
console.log('这是函数');
}
export {
obj,arr,fun
}
使用import进行引入数据
使用export命令定义了模块的对外接口 其他的文件
就可以使用import这个命令去加载这个模块
<script type="module">
import {
obj,fun,username} from "./1.js";
console.log(obj);
console.log(username);
script>
includes : 是否包含字符串
startsWith : 是否以某个字符开头
endsWith : 是否以某个字符结尾
repeat : 重复字符串指定的次数
let str = 'http://www.ujiuye.com/index.html';
// 字符串的扩展
// includes : 检测是否包含字符或者字符串
// 返回值是布尔值
console.log(str.includes('h'));
console.log(str.includes('u'));
console.log(str.includes('a'));
console.log(str.includes('ujiuye'));
// 重复字符串
// repeat : 重复字符串 内置一个参数 参数是重复的次数
let string = '*'
console.log(string.repeat(50));
console.log(string.repeat(5));
// startsWith : 检测字符串是否以某个字符或者字符串开头
console.log(str.startsWith('h'));
console.log(str.startsWith('s'));
console.log(str.startsWith('http'));
console.log(str.startsWith('https'));
console.log(string.repeat(50));
// endsWith : 检测字符串是否以某个字符或者字符串结尾
console.log(str.endsWith('.html'));
console.log(str.endsWith('.htm'));
console.log(str.endsWith('.jpg'));
Number.isFinite : 判断是否是有限大
Number.MAX_VALUE : 数字最大值
Number.isInteger : 判断是否是整数
Number.parseInt : 将字符串转换为整型
Math.trunc : 向下取整
// 数组的扩展
// JS中的数字的最大值
// console.log(99999999999999**9);
console.log(Number.MAX_VALUE);
console.log(1.7976931348623157e+309); // Infinity 无穷大
console.log('*'.repeat(50));
// Number.isFinite 判断数组是否有限大
console.log(Number.isFinite(99999999));
console.log(Number.isFinite(1.7976931348623157e+309));
console.log(Number.isFinite(Infinity));
console.log(Number.isFinite(9999**99));
console.log('*'.repeat(50));
// isInteger : 判断数字是否是整数
console.log(Number.isInteger(5));
console.log(Number.isInteger(5.1));
console.log(Number.isInteger(5.0));
console.log(5.0);
console.log(Number.isInteger(5.));
console.log(Number.isInteger(5.0000000000000001));
console.log(5.000000000000001);
console.log('*'.repeat(50));
// Number.parseInt : 强制转化为整形
console.log(Number.parseInt('1.2'));
console.log(Number.parseInt('a1.2'));
console.log(Number.parseInt('1q2.2'));
console.log(Number.parseInt('12.2'));
console.log(Number.parseFloat('1.2'));
console.log(Number.parseFloat('a1.2'));
console.log(Number.parseFloat('1q2.2'));
console.log(Number.parseFloat('12.2a1'));
console.log('*'.repeat(50));
// trunc : 向下取整
console.log(Math.trunc(3.1));
console.log(Math.trunc(3.9));
console.log(Math.trunc(3.9999999999999999));
Array.from : 将伪数组对象或者可遍历的对象 转换成真数组
Array.of : 将一系列的值转换为数组
find : 找出第一个满足条件的元素
findIndex : 找出第一个满足条件元素的索引
<!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>
<body>
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>
<button>按钮6</button>
<button>按钮7</button>
<button>按钮8</button>
</body>
</html>
<script>
// 数组的扩展
// of : 将一组数据组成一个数组 其实就是创建数组
// 内置参数若干 每一个参数 最后都是数组的元素
let arr = Array.of(22,44,66,88,99,33);
console.log(arr);
console.log(Object.prototype.toString.call(arr));
// from : 将伪数组转化为真数组
let oBtns = document.getElementsByTagName('button');
let oBtn = document.querySelectorAll('button');
// console.log(Object.prototype.toString.call(oBtn));
console.log(Object.prototype.toString.call(oBtns));
// 将伪数组转化为真数组
let oBtnsArr = Array.from(oBtns);
console.log(Object.prototype.toString.call(oBtnsArr));
oBtnsArr.forEach(element=>console.log(element));
oBtn.forEach(element=>console.log(element));
// find : 查找数组中第一个符合条件的元素 和filter比较相似
// filter的返回值是数组 即使没有符合条件的 返回的也是空数组
// find返回的是一个数组元素 只返回一个 如果说没有符合条件的 返回undefined
let result1 = arr.filter(element=>element%3==0);
console.log(result1);
let result2 = arr.find(element=>element%3==0);
console.log(result2);
// findIndex : 查找数组中第一个符合条件的元素的下标 如果说数组元素不存在 那么返回-1
// 传参方式和find相同
let index = arr.findIndex(element=>element%7==0);
console.log(index);
</script>
Object.is : 判断v1,v2数据是否完全相等
Object.assign : 将源对象source的属性复制到新的目标target对象上
Object.keys : 取出对象中所有的键 作为一个数组
Object.valuess : 取出对象中所有的值 作为一个数组
Object.create : 以一个对象为原型创建另一个对象
Object.defineProperties : 给对象添加属性
// 对象扩展
// Object.is : 判断v1, v2数据是否完全相等
// 只要是对象类型的数据 两个对象就不可能完全相等 除非是拷贝
let arr1 = [1,2,3];
let arr2 = [1,2,3];
console.log(Object.is(arr1,arr2));
let str1 = '123';
let str2 = '123';
console.log(Object.is(str1,str2));
console.log(arr1 == arr2);
console.log(str1 == str2);
// Object.assign : 将源对象source的属性复制到新的目标target对象上
let obj = {
name : 'Eric',age : 18};
let newObj = {
sex : '男'};
Object.assign(newObj,obj);
console.log(newObj);
// Object.keys : 取出对象中所有的键 作为一个数组
let keys = Object.keys(obj);
console.log(keys);
// Object.valuess : 取出对象中所有的值 作为一个数组
let values = Object.values(obj);
console.log(values);
// Object.create : 以一个对象为原型创建另一个对象
// 实质上就是实现了对象的继承
// 内置两个参数 第一个参数是原型对象 object
// 第二个参数是描述 是一个对象
// 对象中的键是添加的属性名字 键名 对象的值还是一个对象
// 对象中有4个属性
// value : 属性值
// writable : 属性是否可写 是否可以修改 默认为false
// configurable : 是否可删除 默认为false
// enumerable : 是否可枚举 其实就是遍历 默认为false
let object = {
name : 'Eric',age : 18,sex : '男'}
let newObject = Object.create(object,{
address : {
value : '吉林长春',
writable : true,
configurable : true,
enumerable : true
},
school : {
value : '吉林大学',
writable : false,
configurable : false,
enumerable : false
}
})
console.log(newObject);
console.log(newObject.name);
// 修改对象属性
newObject.address = '河北廊坊';
newObject.school = '北京大学';
console.log(newObject);
// 删除对象属性
// delete newObject.address;
// delete newObject.school;
// console.log(newObject);
// 遍历对象
for (let key in newObject){
console.log(newObject[key]);
}
// Object.defineProperties : 给对象添加属性
let obj = {
firstName : '大郎',
lastName : '武'
};
// 内置两个参数 第一个参数是原型对象 第二个参数是描述 描述是一个对象
// 对象中的键是新增的属性的名字 也就是键名
// 对象中的值还是一个对象
// 对象中有两个方法 是魔术方法【不需要手动调用 在满足条件的情况下 自动调用】
// get : 名字固定 在我们获取新增属性的时候 会被自动调用
// set : 名字固定 在我们改变 设置新增属性的时候 会被自动调用
// set方法可以接受一个参数 参数是我们修改的内容 自动传参
Object.defineProperties(obj,{
fillName : {
get(){
// console.log('调用了fillname属性');
return this.lastName + this.firstName;
},
set(name){
// console.log('你在试图改变fillname' + name);
this.lastName = name.slice(0,1);
// console.log(this.lastName);
this.firstName = name.slice(1);
}
}
});
console.log(obj.lastName);
console.log(obj.firstName);
// obj.fillName;
console.log(obj.fillName);
// obj.fillName = '武松';
obj.fillName = '关羽';
console.log(obj.fillName);
克隆其实就是拷贝 就是复制 这是主要是针对引用数据类型来说的 也就是说在底层存储的时候 使用堆栈存储的
我们主要针对的对象是 数组 对象 因为只有他们才是引用类型 堆栈空间存储
拷贝涉及到两种拷贝
浅拷贝指的是 我们在拷贝被对象的时候 只是拷贝对象的地址 不是拷贝存储
等同于给一个人去两个名字 那么当一个发生改变的时候 另一个也会收到影响
// 浅拷贝
let obj = {
name : 'Eric','age' : 18,sex : '男',arr : [33,66,99],friend : {
name : 'MM',age : 17}};
// let arr = [22,44,66,88];
let data = [
// 22,44,66,88,
{
name : 'Eric','age' : 18,sex : '男'},
{
name : 'Mary','age' : 16,sex : '女'},
[22,44,66,88]
];
// 对对象进行浅拷贝
// let newObj = obj;
// console.log(obj);
// console.log(newObj);
// newObj.school = '吉林大学';
// console.log(obj);
// console.log(newObj);
// 浅拷贝模式之...
// let newArr = [...arr];
// console.log(arr);
// console.log(newArr);
// newArr.push(99);
// console.log(arr);
// console.log(newArr);
// let newData = [...data];
// console.log(data);
// console.log(newData);
// newData[4].school = '吉林大学';
// console.log(data);
// console.log(newData);
// 浅拷贝之JSON
// let str = JSON.stringify(data);
// let newData = JSON.parse(str);
// console.log(data);
// console.log(newData);
// newData.push({name : 'Jack','age' : 22,sex : '男'});
// console.log(data);
// console.log(newData);
// newData[2].push(99);
// console.log(data);
// console.log(newData);
// newData[0].school = '吉林大学';
// console.log(data);
// console.log(newData);
// let str = JSON.stringify(obj);
// let newObj = JSON.parse(str);
// console.log(obj);
// console.log(newObj);
// newObj.arr.push(88);
// console.log(obj);
// console.log(newObj);
// obj.friend.sex = '女';
// console.log(obj);
// console.log(newObj);
深拷贝是将地址和内存都进行拷贝 之前的对象不会影响到之后的对象
// 深拷贝
let obj = {
name : 'Eric','age' : 18,sex : '男'}; // [object Object]
let arr = [33,66,99]; // [object Array]
let data = [
{
name : '知行合一',author : '王阳明',price : 99,arr : [22,66,88]},
{
name : '齐民要术',author : '贾思勰',price : 99},
{
name : '史记',author : '司马迁',price : 99},
{
name : '资治通鉴',author : '司马光',price : 99},
{
name : '伤寒杂病论',author : '张仲景',price : 99},
{
name : '本草纲目',author : '李时珍',price : 99},
]
// let result = [];
// for (let i in arr){
// result[i] = arr[i];
// }
// console.log(result);
// let result = {};
// for(let key in obj){
// result[key] = obj[key];
// }
// console.log(result);
// 定义一个函数
// 需要一个参数 参数是拷贝的对象
function deepCopy(object){
// 先判断我们拷贝的对象的具体数据类型 是数组还是对象
// 如果是数组 我们创建空数组 如果是对象 我们创建空对象
let result;
if (Object.prototype.toString.call(object) == '[object Object]'){
result = {
};
}else if (Object.prototype.toString.call(object) == '[object Array]'){
result = [];
}else{
return object;
}
// 进行对象的拷贝
for (let key in object){
// console.log(typeof object[key]);
if (typeof object[key] == 'object'){
result[key] = deepCopy(object[key]);
}else{
result[key] = object[key];
}
// result[key] = object[key];
}
return result;
}
let newData = deepCopy(data);
console.log(data);
console.log(newData);
data.name = '1232132';
console.log(data);
console.log(newData);
data[0].page = 300;
console.log(data);
console.log(newData);
console.log(data[0].arr.push(365));
console.log(data[0].arr);
console.log(newData[0].arr);
略
官方网站
https://nodejs.org/zh-cn
https://nodejs.org/en
https://nodejs.cn
当在DOC中运行Node命令的时候 显示不是内部或者外部命令 那么就是环境变量没有安装
如果说不再Node程序目录下 不能使用
那么我们在执行Node命令的时候 需要在前面协商Node路径
但是这样写非常麻烦 我们可以把他装到变量中 window的环境变量
node = D:/Node/node
打开我的电脑 => 点击右键 => 属性 => 高级选项配置 => 环境变量 => 系统变量 => path => 新建
如果说win7不用新建 只讲使用; 把Node路径添加到后面就可以
doc命令是windwo系统命令
cd : 切换路径 ./ …/ /
mkdir : 创建文件夹
rmdir : 删除文件夹 /s 强制删除 /q 静默删除
dir : 查看当前目录
dir.> : 创建文件
del : 删除文件
clear : 清屏
############################################################################
NodeJS是非阻塞的 也就是说异步的 我们整个Node中使用的都是异步回调
几乎都在操作回调函数和模块
首先 我们Node中相较于以前的JS 多添加了服务器操作 模块操作 文件流
// 这里指的是引入一个模块 fs模块 fileSystem
// 引入模块使用require函数 内置一个参数 参数是模块名 和在html中的script中的src属性是一样的
// 只不过添加了命名空间 我们过来是是一个对象
// fs模块是核心模块 在安装好Node的时候 Node自带的模块
let fs = require('fs');
// console.log(fs);
console.log(1111111111);
// 使用模块
// 读取文件使用fs模块中的readFile方法 内置三个参数
// 第一个参数是路径 第二个参数是字符集
// 第三个参数是回调函数
// 回调内置两个参数 第一个参数是错误 第二个参数是读取出来的数据
fs.readFile('./test/1.test.txt','utf-8',(error,data)=>{
if (error){
console.log('读取失败');
}else{
console.log(data);
}
});
console.log(2222222222);
node核心API构建用的是异步事件驱动架构,其中某些类型的对象又称触发器(emitter)
会触发命名事件来调用函数,又称监听器,所有能触发的事件对象都是EventEmitter类的实例
这些对象有一个 eventEmitter.on(函数)用于一个或这个多事件绑定到命名事件中
events模块 只提供了一个对象 events.EventEmitter,核心就是监听与触发
我们使用事件驱动 使用的是一个模块 events 他也是Node的核心模块
使用emit方法进行触发事件对象
在事件中传递了参数,EvnentEmitter每个事件有一个事件名和若干个参数组成
事件名是一个字符串 表达一定的意思 见名之意,对于每一个事件 EventEmitter又支持若干个监听器
当事件触发时 注册到这个事件监听器依次被调用 事件参数作为回调参数使用
// 引入核心模块 events
const events = require('events');
// 实例化仅有的一个实例 得到一个对象
const event = new events.EventEmitter();
// 使用该对象进行绑定事件
// 绑定事件使用on方法 内置两个参数
// 第一个参数是事件的名字
// 第二个参数是回调函数 回调函数其实就是事件的行为
// 可以给回调函数传递参数 若干参数 因为这是一个自定义的函数
// event.on('I_LOVE_YOU',()=>{
// console.log('终于等到你');
// });
// 触发事件的时候 使用emit方法
// 内置参数若干 第一个参数是我们触发的事件名
// 从第二个参数开始 都是事件的参数 如果没有参数 那么emit方法内置一个参数 参数是事件的名字
// event.emit('I_LOVE_YOU');
// event.emit('I_LOVE_YOU');
// event.emit('I_LOVE_YOU');
// event.emit('I_LOVE_YOU');
// 传递事件参数
event.on('I_LOVE_YOU',(aName,bName)=>{
console.log(aName + '终于等到你' + bName);
});
event.emit('I_LOVE_YOU','Eric','Mary');
event.emit('I_LOVE_YOU','Jerry','Tom');
event.emit('I_LOVE_YOU','喜洋洋','灰太狼');
模块就是一个JS文件 将我们已有的程序进行一个不封装 当我们需要再次使用的时候 直接引入
比如jquery就是一个模块 我们可以使用其中的一些功能
在NodeJS中 我们模块比较复杂 有以下三种
核心模块就是安装完成Node的时候 自带的一些模块
不需要下载 不需要定义 我们可以直接使用
使用的时候 使用require函数进行引入模块 引入进来的是一个对象
使用的时候要模块名(引入的时候赋给的变量)对象中的属性或者是方法 或者是对象
常用的核心模块 fs events url path http querystring
核心模块比较少 只是完成一些基本功能 大多都是使用JS之前的逻辑和代码
如果说我们使用到更复杂的逻辑 那么需要依赖第三方模块
很多时候 我们需要依赖与第三方模块 去网上下载
第三方模块 模块的名字都是唯一的 不用担心重复下载 第三发模块是所有的开发者的结晶
第三方模块的使用依赖于npm命令 我们使用npm进行模块的下载
npm是包管理工具
主要用来做模块的发布 依赖 下载 安装等工作
NPM命令
init : 初始化命令 下载包之前必须初始化 生成package.json
install/i : 下载包 安装包 生成node_module文件夹 装包的 生成package_look.json 包的信息
install/[email protected] : 下载包 安装包 指定版本号
search/s : 查看包
install/i -g : 全局安装 只有工具类的模块才会使用全局安装 全局安装之后会带给我们一个命令
install/i --save || install -S : 安装运行依赖
install/i --save-dev || install -D : 安装开发依赖
remove/r : 删除包
uninstall : 卸载包
// JS源码或者时间
// let date = new Date();
// let Y = date.getFullYear();
// let M = date.getMonth() + 1;
// let D = date.getDate();
// let H = date.getHours();
// let m = date.getMinutes();
// let s = date.getSeconds();
// console.log(`${Y}年${M}月${D}日${H}时${m}分${s}秒`);
// 使用Node模块 第三方模块 time-stamp
const timer = require('time-stamp');
// console.log(timer);
// console.log(timer('YYYY年MM月DD日HH点mm分ss秒'));
console.log(timer('YYYY年MM月DD日HH点mm分ss秒=======星期zz'));
自定义模块其实就是我们自己定义的JS文件 将其封装 然后暴露
我们可以自行引入 使用
暴露 : exports || module.exports
引入 : require 在NodeJS中引入模块 一直使用require
引入自定义模块的时候注意要写路径 即使是当前目录 也不能省略./
// 第一种暴露方式
// 使用export方式进行暴露 这种方式不常用
/*
exports.username = 'admin';
exports.password = '123';
exports.fun = ()=>{
console.log('fun函数');
}
exports.obj = {
// username,password 因为username password不是变量 所以说不能这样暴露
name : 'Eric',
age : 18
};
*/
// 第二种暴露方式
// 使用modult.exports
// module.exports.username = 'admin';
// module.exports.password = '123';
// module.exports.fun = ()=>{
// console.log('fun函数');
// }
// module.exports.obj = {
// // username,password 因为username password不是变量 所以说不能这样暴露
// name : 'Eric',
// age : 18
// };
// 堆栈空间都保持一致
// console.log(exports);
// console.log(module.exports);
// console.log(module.exports === exports);
// let obj1 = {a:1};
// let obj2 = {a:1};
// console.log(obj1 == obj2);
module.exports.username = 'admin';
exports.password = '123';
exports.fun = ()=>{
console.log('fun函数');
}
exports.obj = {
// username,password 因为username password不是变量 所以说不能这样暴露
name : 'Eric',
age : 18
};
// 第三中暴露方式 最常用的方式
let name = 'Eric';
let age = 18;
let username = 'admin';
let password = '123';
let func = ()=>{
console.log('这是哈数');
}
let obj = {
name,age,username,password
}
module.exports = {
obj,func
}
// 如果说同时使用export和module.exports两种方法暴露
// 那么module.exports会覆盖exports
Node顶层对象
// __dirname : 获取当前文件夹的绝对路径
console.log(__dirname);
// __filename : 获取当前文件的绝对路径
console.log(__filename);
Buffer 结构和数组非常像 操作的方法也相似 数组中不能存储二进制的文件 而buffer专门存储二进制的数据
文件流的写入 网络请求数据处理 javascript 语言不能读取或操作数据流机制
buffer在全局作用域 不需要引入 直接使用 buffer中存储的是二进制的数据 显示的时候 是以16进制显示
buffer中每个元素从00-ff 0-255 buffer 中的每个元素 占用内存的一个字节 8比特
一个汉字占有三个字节 buffer大小一旦确定 不能修改 buffer是对底层内存的直接操作
buffer.length属性 : 查看Buffer的长度
Buffer.from(str) : 将一个字符串转换成buffer
buf.toString(); : 将缓冲区的数据 转成字符串
// let str = 'hello';
let str = 'hello 中公';
// Buffer.from将字符串转化为buffer
let buf = Buffer.from(str);
console.log(buf);
console.log(buf.length);
// 将Buffer转化会字符串
console.log(buf.toString());
// console.log(buf.toString('base64'));
// 如果说使用Buffer 将Buffer转化为base格式
// 使用toString方法 内置参数 参数是base64
let base64 = buf.toString('base64');
// 将base格式 转化会buffer
// 使用Buffer.from方法 内置两个参数 第一个参数是base64 第二个参数是’base64‘
let buff = Buffer.from(base64,'base64');
console.log(buff);
buffer和base64之间的转换
// 这是一个JSON
let json = '{"title" : "沙尘暴","type" : "百年一遇"}';
// 我们想要将json转化为base64的格式
// 将JSON准化为Buffer
// let buf = Buffer.from(json);
// let base64 = buf.toString('base64');
// console.log(base64);
// 将json转化为base64的格式 封装函数
function jsonToBase64(obj){
let buf = Buffer.from(obj);
return buf.toString('base64');
}
console.log(jsonToBase64(json));
let base = "eyJ0aXRsZSIgOiAi5rKZ5bCY5pq0IiwidHlwZSIgOiAi55m+5bm05LiA6YGHIn0=";
// 将base64格式转化为JSON封装一个函数
function base64ToJson(obj){
let string = Buffer.from(obj,'base64');
return string.toString();
}
console.log(base64ToJson(base));
NodeJS是可以对文件进行操作的 能操作所有类型的文件 对文件的操作主要包括 文件的读取 文件的写入
在文件操作中 使用的模块是我们的核心模块 fs模块 fileSystem
这个模块中有着丰富的模块操作方法 其中包括文件的读取和文件的写入
最重要的是 每一个方法其实都是两个 有一个同步方法 还有一个异步方法
同步方法方法的名字在后面加上Sync
同步的方法比异步的方法多了一个返回值 异步方法比同步方法多了一个回调函数
文件的读取异步方法使用readFile
内置两个或者三个参数 第二个参数是可选参数 字符集
如果说传递第二个参数 那么按照字符集读取 如果说不传递 那么读取的是一个Buffer
第一个参数是文件的路径
第三个参数是回调函数
回调内置两个参数 第一个参数是错误 第二个参数是读取的数据
文件的读取同步方法使用readFileSync
内置一个或者两个参数 第一个参数是文件路径 第二个参数是可选参数字符集
// 文件读取操作
// 1.引入模块
const fs = require('fs');
// 使用readFile方法
// 传递两个参数的时候
fs.readFile('../test/2.data.json',(error,data)=>{
if (error){
console.log('读取失败 错误为:');
console.log(error);
}else{
// 将buffer直接转化为对象 使用JSON.parse方法
console.log(JSON.parse(data));
// console.log(data.toString());
}
})
// 使用费三个参数的时候
fs.readFile('../test/2.data.json','utf-8',(error,data)=>{
if (error){
console.log('读取失败 错误为:');
console.log(error);
}else{
console.log(JSON.parse(data));
}
})
// 同步读取方式
// 内置一个参数的时候
let buf = fs.readFileSync('../test/1.test.txt').toString();
console.log(buf);
let result = fs.readFileSync('../test/1.test.txt','utf-8');
console.log(result + '同步');
所有的文件写入 如果说写入的文件不存在 那么会自动创建文件
文件写入 就是将我们数据写入到文件中 一下的三种方法也是一样的 所以说 都是写入
一般情况下 我们文件写入分为三步组成 同步写入最醒目 最清晰
其实写入文件就是将数据存到文件中 一共分为三步
第一 打开文件 第二 把数据放到文件中 第三 关闭文件
一共涉及到三个方法
1.openSync : 打开文件
内置三个参数
第一个参数是打开的文件路径
第二个参数是打开方式
文件的打开方式有三种
r : 只读方式打开
w : 写入方式打开 覆盖写
a : 写入方式打开 追加写
第三个参数是打开权限 这个参数一般不传
返回值是资源句柄
2.writeSync : 写入文件
内置四个参数
第一个参数是资源句柄
第二个参数是写入的数据
第三个参数是字符集 写入的字符集
第四个参数是写入的起始位置 但是 我们一般不传
3.closeSync : 关闭文件
内置一个参数 参数是资源句柄
const fs = require('fs');
// 打开文件
/*
1.openSync : 打开文件
内置三个参数
第一个参数是打开的文件路径
第二个参数是打开方式
文件的打开方式有三种
r : 只读方式打开
w : 写入方式打开 覆盖写
a : 写入方式打开 追加写
第三个参数是打开权限 这个参数一般不传
返回值是资源句柄
*/
// let fd = fs.openSync('../test/3.sync.txt', 'w');
let fd = fs.openSync('../test/3.sync.txt', 'a');
// 定义写入到文件的字符串
// let str = "五花马 千金裘 呼儿将出换美酒 与尔同销万古愁";
let str = "五花马 千金裘 呼儿将出换美酒 与尔同销万古愁\n";
// 写入文件
/*
2.writeSync : 写入文件
内置四个参数
第一个参数是资源句柄
第二个参数是写入的数据
第三个参数是字符集 写入的字符集
第四个参数是写入的起始位置 但是 我们一般不穿
*/
fs.writeSync(fd, str, 'utf-8');
// 关闭文件
/*
3.closeSync : 关闭文件
内置一个参数 参数是资源句柄
*/
fs.closeSync(fd);
异步写入方法原理和同步方法相同 只有一点不同 没有返回值 全部都靠回调
也就是说传参的方式不同 都加了一个回调函数 方法名字都去掉了Sync
一共涉及到三个方法
1.open : 打开文件
内置四个参数
第一个参数是打开的文件路径
第二个参数是打开方式
文件的打开方式有三种
r : 只读方式打开
w : 写入方式打开 覆盖写
a : 写入方式打开 追加写
第三个参数是打开权限 这个参数一般不传
第四个参数是回调函数
回调内置两个参数 第一个参数是错误 第二个参数是资源句柄
2.write : 写入文件
内置五个参数
第一个参数是资源句柄
第二个参数是写入的数据
第三个参数是字符集 写入的字符集
第四个参数是写入的起始位置 但是 我们一般不传
第五个参数是回调
回调内置一个参数 参数是错误
3.close : 关闭文件
内置两个个参数
第一个参数是资源句柄
第二个参数是回调函数
回调内置一个参数 参数是错误
const fs = require('fs');
// 打开文件
/*
1.open : 打开文件
内置四个参数
第一个参数是打开的文件路径
第二个参数是打开方式
文件的打开方式有三种
r : 只读方式打开
w : 写入方式打开 覆盖写
a : 写入方式打开 追加写
第三个参数是打开权限 这个参数一般不传
第四个参数是回调函数
回调内置两个参数 第一个参数是错误 第二个参数是资源句柄
*/
// 定义一个对象 将对象写入
let arr = [
{
article_id: 1, article_title: "诫子书", article_author: "诸葛亮" },
{
article_id: 2, article_title: "兰亭集序", article_author: "王羲之" },
{
article_id: 3, article_title: "岳阳楼记", article_author: "范仲淹" },
{
article_id: 4, article_title: "滕王阁序", article_author: "王勃" }
]
fs.open('../test/4.async.json', 'w', (error, fd) => {
if (error) {
console.log('打开文件失败');
} else {
// 写入文件
/*
2.write : 写入文件
内置五个参数
第一个参数是资源句柄
第二个参数是写入的数据
第三个参数是字符集 写入的字符集
第四个参数是写入的起始位置 但是 我们一般不传
第五个参数是回调
回调内置一个参数 参数是错误
*/
// 注意 : 对象不能直接进行写入操作 要先将其转化为字符串(JSON)
fs.write(fd, JSON.stringify(arr), 'utf-8', err => {
if (err) console.log('写入失败');
else console.log('写入成功');
})
// 关闭文件
/*
3.close : 关闭文件
内置两个个参数
第一个参数是资源句柄
第二个参数是回调函数
回调内置一个参数 参数是错误
*/
fs.close(fd, err => {
err ? console.log('关闭失败') : console.log('关闭成功');
})
}
})
简单写入操作 我们使用的最多的 因为简单
只涉及到一个方法 打开和关闭都不需要 或者说都在这一步完成了
writeFile : 写入文件操作
内置四个参数
第一个参数是打开的文件路径
第二个参数是写入的内容
第三个参数是描述 是一个对象
对象中两个属性 flag : 写入的方式 encoding : 写入的字符集
第四个参数是回调函数
回调内置一个参数 参数是错误
const fs = require('fs');
// 写入操作
/*
writeFile : 写入文件操作
内置四个参数
第一个参数是打开的文件路径
第二个参数是写入的内容
第三个参数是描述 是一个对象
对象中两个属性 flag : 写入的方式 encoding : 写入的字符集
第四个参数是回调函数
回调内置一个参数 参数是错误
*/
// 定义一个写入的字符串
let str = "将进酒 杯莫停 与君歌一曲 请君为我倾耳听";
fs.writeFile('../test/5.easy.txt',str,{
flag : 'w',encoding : 'utf-8'},error=>{
if (error) console.log('写入失败');
else console.log('写入成功');
})
let string = "床前明月光";
fs.writeFileSync('../test/6.easy.txt',string,{
flag : 'w',encoding : 'utf-8'});
流式文件操作和其他操作都不一样 需要使用事件驱动 但是使用的时候比较少
一般不使用流失操作 流式操作有一个优势 是其他写实方式不具备的
他可以进行大批量的数据写入 源源不断的进行写入操作
所以说 我们使用流式操作 一般情况下的写入方式都是追加写
涉及到的方法
1.createWriteStream : 创建一个可写的流
内置两个参数
第一个参数是写入的文件路径
第二个参数是一个描述
第一个属性是flags : 写入的方式 一般都是a
第二个属性是encoding : 字符集
返回值是一个资源 使用资源进行触发事件
触发之后 使用资源中的write方法进行写入
写入之后 使用资源中的end方法进行结束
const fs = require('fs');
// 创建一个可写的流
let ws = fs.createWriteStream('../test/7.stream.txt',{
flags : 'a',encoding : 'utf-8'});
// 使用事件驱动打开流式操作
// 使用ws中的on方法 驱动事件open
ws.on('open',()=>{
console.log('打开流');
})
// 写入操作 使用ws中的write方法
// 内置一个参数 参数是写入的内容
for (let i = 0;i <= 100000;i++){
ws.write(`第${
i}次想起你\n`);
}
// 使用ws中的on方法 驱动事件close
// 关闭可写的流
ws.on('close',()=>{
console.log('关闭流');
})
// 结束所有操作 使用end方法
ws.end();
fs events path url http querystring
我们主要讲述的是npm命令
init : 初始化
install : 下载安装
[email protected] : 安装指定版本
install -g : 全局安装
install --save : 运行依赖
install --save-dev : 开发依赖
search : 查看包
remove : 删除包
uninstall : 卸载包
暴露三种方法 尽量使用module.export 不要使用exports
因为如果同时使用两种方式 那么exports会被覆盖
引入的时候 一定要带上路径 即使是当前路径 也不能省略./
所以说引入自定义模块 都是使用./ …/开头
他是一种数据解构 存储二进制的 但是显示的时候以十六进制进行展示
我们可以将字符串或者是JSON转化成Buffer 每一个字符在Buffer中占有一个字节
但是注意 每个汉字占有三个字节
Buffer.from : 将字符串转化成Buffer
buf.toString() : 将buffer转化成字符串 可以内置参数 指定转化的类型
使用异步方法 readFile 内置两三个参数 第二个参数可选 是字符集 第一个参数是路径 最后一个是回调
同步方法 readFileSync 少了一个回调函数 多了一个返回值
涉及三个方法
openSync : 打开文件 内置两个参数 1.路径 2.方式
writeSync : 写入文件 内置三个参数 1.资源 2.数据 3.字符集
closeSync : 关闭文件 内置一个参数 资源
涉及三个方法 : 比同步的都多了一个回调函数
open write close
涉及到一个方法 writeFile 不需要打开 也不需要关闭
内置四个参数 1.路径 2.数据 3.描述 {字符集,打开方式} 4.回调
创建一个可写的流 createWriteStream
使用open事件驱动打开流
使用write方法进行写入
使用close事件驱动关闭流
使用end方法关闭
#################################################################################
使用方法readdir 读取指定文件夹中的信息 返回一个数组 数组中的元素是是文件夹中的文件夹或者文件
内置两个参数 第一个参数是文件夹路径 第二个参数是回调函数
回调内置两个参数 第一个参数是错误 第二个参数是返回的数组
const fs = require('fs')
/*
使用方法readdir 读取指定文件夹中的信息 返回一个数组 数组中的元素是是文件夹中的文件夹或者文件
内置两个参数 第一个参数是文件夹路径 第二个参数是回调函数
回调内置两个参数 第一个参数是错误 第二个参数是返回的数组
*/
fs.readdir('./test/',(error,data)=>{
if (error) console.log('读取失败');
else console.log(data);
/*
[
'1.txt', '2.html',
'3.css', '4.gif',
'4.java', '4.js',
'4.php', '4.png',
'4.py', 'aa',
'bb', 'cc'
]
*/
})
// 同步方法
let result = fs.readdirSync('./test/aa');
console.log(result);
使用stat方法读取文件或者文件夹信息 返回的是一个对象
内置两个参数 第一个参数是文件夹路径 第二个参数是回调函数
回调内置两个参数 第一个参数是错误 第二个参数是返回的对象
const fs = require('fs');
/*
使用stat方法读取文件或者文件夹信息 返回的是一个对象
内置两个参数 第一个参数是文件夹路径 第二个参数是回调函数
回调内置两个参数 第一个参数是错误 第二个参数是返回的对象
*/
// 使用异步方法读取文件夹
fs.stat('./test/aa',(error,data)=>{
console.log('文件夹',data.isFile());
if (error) console.log('读取失败');
else console.log(data);
})
// 使用同步方法读取文件
let result = fs.statSync('./test/aa/2.html');
console.log(result);
console.log('文件',result.isFile());
使用方法unlink 直接删除文件
内置两个参数 第一个参数是文件路径 第二个参数是回调函数
回调内置一个参数 参数是错误
const fs = require('fs');
/*
使用方法unlink 直接删除文件
内置两个参数 第一个参数是文件路径 第二个参数是回调函数
回调内置一个参数 参数是错误
*/
// console.log(fs);
fs.unlink('./test/2.html',err=>{
if (err) console.log('删除失败');
else console.log('删除成功');
})
fs.unlinkSync('./test/3.css');
创建文件夹 使用方法mkdir 内置两个或者三个参数
第一个参数是创建的文件夹路径
第二个参数是一个描述 描述是否递归创建 如果说我们创建的是单体(一层)文件夹 我们可以不传递这个参数
但是不能层级创建 创建多层 如果说创建多层文件夹 那么必须加上这个参数 递归创建
第三个参数是回调函数 回调内置一个参数 参数是错误
const fs = require('fs');
/*
创建文件夹 使用方法mkdir 内置两个或者三个参数
第一个参数是创建的文件夹路径
第二个参数是一个描述 描述是否递归创建 如果说我们创建的是单体(一层)文件夹 我们可以不传递这个参数
但是不能层级创建 创建多层 如果说创建多层文件夹 那么必须加上这个参数 递归创建
第三个参数是回调函数 回调内置一个参数 参数是错误
*/
fs.mkdir('./test/dd',err=>{
if (err) console.log('创建失败');
else console.log('创建成功');
});
fs.mkdir('./test/MM/NN/MM/NN',{
recursive : true},err=>{
if (err) console.log('创建失败');
else console.log('创建成功');
});
fs.mkdirSync('./test/a/b/c/d/e',{
recursive : true});
删除文件夹 使用方法rmdir 内置两个或者三个参数
第一个参数是创建的文件夹路径
第二个参数是一个描述 描述是否递归删除 如果说我们删除的是单体(一层)文件夹 我们可以不传递这个参数
但是不能层级删除 删除非空文件夹 如果说删除非空文件夹 那么必须加上这个参数 递归删除
第三个参数是回调函数 回调内置一个参数 参数是错误
const fs = require('fs');
/*
删除文件夹 使用方法rmdir 内置两个或者三个参数
第一个参数是创建的文件夹路径
第二个参数是一个描述 描述是否递归删除 如果说我们删除的是单体(一层)文件夹 我们可以不传递这个参数
但是不能层级删除 删除非空文件夹 如果说删除非空文件夹 那么必须加上这个参数 递归删除
第三个参数是回调函数 回调内置一个参数 参数是错误
*/
fs.rmdir('./test/MM',err=>{
if (err) console.log('删除失败',err);
else console.log('删除成功');
})
fs.rmdir('./test/MM',{
recursive : true},err=>{
if (err) console.log('删除失败',err);
else console.log('删除成功');
})
fs.rmdirSync('./test/a',{
recursive : true})
使用方法rename 也有同步方法
内置三个参数 第一个参数是旧的路径和文件名
第二个参数是你要移动的位置和新的文件名
第三个参数是回调函数 回调内置一个参数 参数是错误
const fs = require('fs');
/*
使用方法rename 也有同步方法
内置三个参数 第一个参数是旧的路径和文件名
第二个参数是你要移动的位置和新的文件名
第三个参数是回调函数 回调内置一个参数 参数是错误
*/
fs.rename('./test/4.py','./test/dd/666.html',err=>{
if (err) console.log('移动和更名失败');
else console.log('移动和更名成功');
})
fs.renameSync('./test/4.php','./test/dd/666.css')
path模块也是核心模块 我们很多的时候都会使用到 尤其是使用fs模块的时候 都和fs模块进行连用
basename : 返回文件的最后一部分
delimiter : 返回语句的定界符
pathname : 获取URL请求的路径
extname : 返回文件的后缀名
isAbsolute : 判断路径是否为绝对路径
join : 连接路径
resolve : 连接路径
const path = require('path');
const url = require('url');
let str1 = "http://www.ujiuye.com:80/jiaoxue/pxsz/index.html?name=Eric&age=18";
let str2 = "https://www.baidu.com:443/index.html?kw=Web";
// URL模块 将我们的URL转化成对象格式
// parse方法
// console.log(url.parse(str1));
// console.log(url.parse(str2));
// basename : 返回文件的最后一部分
// 请求的具体页面 加上请求时带来的参数
console.log(path.basename(str1));
console.log(path.basename(str2));
// delimiter : 返回语句的定界符
// 获取定界符 最鸡肋 他是一个属性
console.log(path.delimiter);
// parse : 返回一个对象 解析的url路径
console.log(path.parse(str1));
console.log(path.parse(str2));
// dirname : 获取文件夹部分
console.log(path.dirname(str1));
console.log(path.dirname(str2));
// extname : 返回文件的后缀名
console.log(path.extname(str1));
console.log(path.extname(str2));
console.log(path.extname(url.parse(str1).pathname));
// isAbsolute : 判断路径是否为绝对路径
// 判断是否为绝对路径 判断的是是否从盘符开始
console.log(path.isAbsolute(str1));
console.log(path.isAbsolute(str2));
console.log(path.isAbsolute('C:/a'));
console.log(path.isAbsolute('Z:/a'));
// join : 连接路径
console.log(path.join('/c','d','./e'));
console.log(path.join('/c/d','./e','../f','./a')); //c/d/f/a
console.log(path.join(__dirname,'/c/d','./e','../f','./a'));
// resolve : 连接路径
console.log(path.resolve('/c','d','./e'));
console.log(path.resolve(__dirname,'./c','d','./e'));
console.log(path.resolve('/c/d','./e','../f','./a')); //c/d/f/a
console.log(path.resolve(__dirname,'./c/d','./e','../f','./a')); //c/d/f/a
URL : 统一资源定位器
URL的组成
协议 : http:// https://
域名 : www.xx.xx
端口 : 80 443
路径 : / /index.html /jiaoxue/pxsz/index.html
参数 : ?n=m&a=b
锚点 : #
parse : 将URL转化成对象格式
let str1 = "http://www.ujiuye.com:80/jiaoxue/pxsz/index.html?name=Eric&age=18";
let str2 = "https://www.baidu.com:443/index.html?kw=Web";
console.log(url.parse(str1));
protocol: 'http:',
host: 'www.ujiuye.com:80',
port: '80',
hostname: 'www.ujiuye.com',
search: '?name=Eric&age=18',
query: 'name=Eric&age=18',
pathname: '/jiaoxue/pxsz/index.html',
path: '/jiaoxue/pxsz/index.html?name=Eric&age=18'
域名(英语:Domain Name),又称网域,是由一串用点分隔的名字组成的Internet上某一台计算机或计算机组的名称
用于在数据传输时对计算机的定位标识 由于IP地址具有不方便记忆并且不能显示地址组织的名称和性质等缺点
人们设计出了域名,并通过网域名称系统来将域名和IP地址相互映射使人更方便地访问互联网
而不用去记住能够被机器直接读取的IP地址数串
IP是Internet Protocol(网际互连协议)的缩写,是TCP/IP体系中的网络层协议
设计IP的目的是提高网络的可扩展性
解决互联网问题,实现大规模、异构网络的互联互通
分割顶层网络应用和底层网络技术之间的耦合关系,以利于两者的独立发展
根据端到端的设计原则,IP只为主机提供一种无连接、不可靠的、尽力而为的数据报传输服务
A类:(1.0.0.0-126.0.0.0)
第一个字节为网络号,后三个字节为主机号
该类IP地址的最前面为“0”,所以地址的网络号取值于1~126之间。一般用于大型网络
B类:(128.0.0.0-191.255.0.0)
前两个字节为网络号,后两个字节为主机号
该类IP地址的最前面为“10”,所以地址的网络号取值于128~191之间。一般用于中等规模网络
C类:(192.0.0.0-223.255.255.0)
前三个字节为网络号,最后一个字节为主机号
类IP地址的最前面为“110”,所以地址的网络号取值于192~223之间。一般用于小型网络
D类:是多播地址。该类IP地址的最前面为“1110”,所以地址的网络号取值于224~239之间。一般用于多路广播用户
E类:是保留地址。该类IP地址的最前面为“1111”,所以地址的网络号取值于240~255之间
"端口"是英文port的意译,可以认为是设备与外界通讯交流的出口
端口可分为虚拟端口和物理端口,其中虚拟端口指计算机内部或交换机路由器内的端口,不可见
例如计算机中的80端口、21端口、23端口等。物理端口又称为接口,是可见端口
计算机背板的RJ45网口,交换机路由器集线器等RJ45端口。电话使用RJ11插口也属于物理端口的范畴
http : 超文本传输协议
http是一个简单的请求-响应协议,它通常运行在TCP之上
它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应
请求和响应消息的头以ASCII码形式给出;而消息内容则具有一个类似MIME的格式。
这个简单模型是早期Web成功的有功之臣,因为它使得开发和部署是那么的直截了当
是指从客户端到服务器端的请求消息
包括:消息首行中,对资源的请求方法、资源的标识符及使用的协议
HTTP状态码(HTTP Status Code)是用以表示网页服务器超文本传输协议响应状态的3位数字代码
它由 RFC 2616 规范定义的,并得到 RFC 2518、RFC 2817、RFC 2295、RFC 2774 与 RFC 4918 等规范扩展
所有状态码的第一个数字代表了响应的五种状态之一
所示的消息短语是典型的,但是可以提供任何可读取的替代方案
1xx:指示信息—表示请求已接收,继续处理
2xx:成功—表示请求已经被成功接收、理解、接受
3xx:重定向—要完成请求必须进行更进一步的操作
4xx:客户端错误—请求有语法错误或请求无法实现
5xx:服务器端错误—服务器未能实现合法的请求
200: 客户端请求, 和服务器成功做出响应
301: 重定向, 资源被重定向到一个永久性的地址上, 资源的原始url可能无法访问
302: 重定向, 资源被重定向到一个临时性的地址上, 资源的原始url还在
400: 语义错误, 无法被服务器理解, 请求参数有误
401: 请求需要用户验证, 包含了 Authorization 证书, 但是被服务器拒绝了
403: 服务器已经理解请求, 但是拒绝执行它
404: 请求url不存在 (一般常见于请求路径拼写错误)
500: 服务器遇到了一个未曾预料的请求, 一般出现在后台代码报错
502: 网关或者代理服务器请求时, 从服务器接收到了一个无效响应
503: 服务器过载, 当前无法完成响应, 拒绝客户端连接
504: 网关或者代理服务器请求时, 等待响应超时了
MIME的全称是Multipurpose Internet Mail Extensions,即多用途互联网邮件扩展类型
这是HTTP协议中用来定义文档性质及格式的标准
服务器通过MIME告知响应内容类型,而浏览器则通过MIME类型来确定如何处理文档
application/octet-stream?未知的应用程序文件
application/json?json数据
text/plain 未知的文本文件(纯文本文件),浏览器会认为这是可以直接显示的
ext/css?css文件
text/html?HTML文件
image/gif?gif文件
##############################################################################
拥有web网页服务功能的计算机, 可以叫做web服务器, 也可以叫http服务器
借助Node.js的核心模块(安装Node就带了), http模块, 来创建web网页服务
有web网页服务功能, 可以提供浏览器要访问的资源 http模块主要用于创建http server服务器
createServer方法,创建出一个Web服务器 使用服务器中的listen方法进行监听
使用createServer中的request参数处理请求 请求最重要的是request中的url属性
这个属性可以使用url模块进行处理
const http = require('http');
// 创建服务器
http.createServer((request,response)=>{
// 本节课说明请求参数
// 请求的参数 我们只学习一个 request.url 请求的路由
// 请求的路由就是URL中的路径 url是一个属性 获取我们请求的路由
console.log(request.url);
}).listen(3000,'127.0.0.1',()=>{
console.log('服务器正在运行......');
})
使用createServer中的response参数处理响应
响应参数
响应头信息 : response.writeHead
响应内容 : response.write
响应结束 : response.end
const http = require('http');
/*
创建Web服务器使用http模块中的createServer方法进行创建
内置一个参数 参数是回调函数
回调内置两个参数
第一个参数是请求
第二个参数是响应
返回的结果是 server服务器
*/
let server = http.createServer((request,response)=>{
// 测试响应数据 响应hello Eric
// response.end('hello Eric');
// 如果说响应的是英文 那么没问题 如果说响应的是汉语 那么我们必须统一字符集
// 必须使用MIME
// 使用MIME 需要使用response中的writeHead方法 内置两个参数
// 第一个参数是http响应码 第二个参数是对象 MIME
response.writeHead(200,{
"Content-type" : "text/html;charset=utf-8"});
// 其实 我们使用响应的函数不是end 而是write函数
// 使用response对象中的write方法进行响应数据内容
response.write('今天的天气真不错
');
response.write('上课可惜了
');
response.write('应该出去玩
');
// 使用write方法响应之后 是无法完成响应的 因为不确定是否响应完成
// 这回需要使用end方法进行结束 他就是结束响应的信号枪
// response.end();
// 一个程序中只能有一个end方法 应为程序一旦执行到end 就已经结束了
// end方法可以携带一个参数 参数也能响应到页面中 但是这个参数必须是字符串
response.end('你好 埃里克
');
// response.end(123); // 报错
});
/*
服务器创建好之后 我们需要进行监听
绑定访问IP地址 端口号
IP地址需要注意 必须和你的电脑本机相匹配
使用listen方法进行监听
内置三个参数 第一个参数是端口号 注意选择端口号 只有这个参数是必选参数
第二个参数是IP地址 本机地址127.0.0.1 || localhost || 10.10.140.61
第三个参数是回调函数 没有任何意义的回调函数
*/
server.listen(3000,'127.0.0.1',()=>{
console.log('服务器正在运行......');
});
读取页面
展现静态页面
展现请求中的所有静态页面
使用url模块中的parse方法解析http请求
获取结果中的query属性
解析出get传递的参数
使用querystring模块
模块中的parse方法解析get参数
使用事件监听
request.on(data)事件进行读取数据,读取回调函数的参数
request.on(end)事件进行结束
读取Buffer.concat(数据)
mkdir : 创建文件夹
rmdir : 删除文件夹
readdir : 读取目录
stat : 读取目录或者文件信息
rename : 移动或重命名
unlink : 删除文件
dirname : 获取url中的文件夹部分
basename : 获取url中请求最后一部分 从页面开始计算
extname : 获取url中后缀名
delimiter : 略
join : 连接路径 连接相对路径
resolve : 连接路径 连接绝对路径
parse方法 : 将url解析成对象
创建Web服务器
使用createServer方法 内置一个参数 参数是回调函数 回调内置两个参数 请求 响应
使用的时候需要监听 使用listen方法 内置一个或三个参数
第一个参数必选 端口号
第二个参数IP
第三个参数是回调函数
####################################################################
静态资源中带有css的 图片
那么每一个css就是一次http请求
表单提交中的请求信息
const http = require('http');
const url = require('url');
const fs = require('fs');
const qs = require('querystring');
// 创建服务器
http.createServer((request,response)=>{
// 判断路由 解构路由
let requestUrl = url.parse(request.url).pathname;
// 处理
if (requestUrl == '/favicon.ico'){
return false;
}
// 判断路由
if (requestUrl == '/zc'){
let result = fs.readFile('./view/register.html','utf-8',(error,data)=>{
error ? console.log('爱慕骚瑞') : response.end(data);
})
}else if (requestUrl == '/register'){
// 接受get参数
let query = url.parse(request.url).query; // username=admin&password=123
// 处理query 我们可以使用querystring模块进行解决
// 使用qs模块中的parse方法 可以将queryget参数解析成一个对象 键值对的形式
let userObj = qs.parse(query);
// 将得到的数据写入到JSON中
let userArr = JSON.parse(fs.readFileSync('./data/user.json','utf-8'));
// 追加对象的ID
userObj.id = userArr[userArr.length - 1].id + 1;
// 将刚刚注册的数据追加到已有数据中
userArr.push(userObj);
fs.writeFile('./data/user.json',JSON.stringify(userArr),{
flag : 'w',encoding : 'utf-8'},error=>{
response.writeHead(200,{
'Content-type':'text/html;charset=utf-8'});
error ? console.log('爱慕骚瑞') : response.end("");
})
}else if (requestUrl == '/dl'){
let result = fs.readFile('./view/login.html','utf-8',(error,data)=>{
error ? console.log('爱慕骚瑞') : response.end(data);
})
}
}).listen(3000,'127.0.0.1',()=>{
console.log('服务器正在运行......');
})
使用事件监听
request.on(data)事件进行读取数据,读取回调函数的参数
request.on(end)事件进行结束
读取Buffer.concat(数据)
const http = require('http');
const fs = require('fs');
const path = require('path');
const url = require('url');
const qs = require('querystring');
// 封装读取页面函数
function readPage(url,response,status){
let result = fs.readFileSync(path.resolve(__dirname,url),'utf-8')
response.writeHead(status,{
'Content-type':'text/html;charset=utf-8'});
response.end(result);
}
// 创建 Web 服务器
http.createServer((request,response)=>{
// 首先我们应该确定访问者的路由
// 获取请求的页面 使用url模块去掉请求的参数
let requestUrl = url.parse(request.url).pathname;
// 判断 如果说requestUrl为根路径 那么默认是index.html
if (requestUrl == '/'){
requestUrl = '/index'
}
// 判断我们请求的路径
if (requestUrl == '/index'){
readPage('./view/index.html',response,200);
}else if (requestUrl == '/dl'){
readPage('./view/login.html',response,200);
}else if (requestUrl == '/zc'){
readPage('./view/register.html',response,200);
}else if (requestUrl == '/register'){
// 注册接口
// 接受get参数
let query = url.parse(request.url).query; // username=admin&password=123
// 处理query 我们可以使用querystring模块进行解决
// 使用qs模块中的parse方法 可以将queryget参数解析成一个对象 键值对的形式
let userObj = qs.parse(query);
// 将得到的数据写入到JSON中
let userArr = JSON.parse(fs.readFileSync('./data/user.json','utf-8'));
// 追加对象的ID
userObj.id = userArr[userArr.length - 1].id + 1;
// 将刚刚注册的数据追加到已有数据中
userArr.push(userObj);
fs.writeFile('./data/user.json',JSON.stringify(userArr),{
flag : 'w',encoding : 'utf-8'},error=>{
response.writeHead(200,{
'Content-type':'text/html;charset=utf-8'});
error ? console.log('爱慕骚瑞') : response.end("");
})
}else if (requestUrl == '/login'){
// 登录
// 接受post参数
// 定义一个数组 装取post参数
let arr = [];
// 使用事件驱动 使用on方法中的data事件进行读取post数据
// 回调函数中内置一个参数 参数就是接收到的post数据
request.on('data',(params)=>{
// console.log(params); // post参数和query是一样的username=admin&password=123
arr.push(params);
})
// 使用on方法中的end事件 结束post请求 处理post
request.on('end',()=>{
// console.log(arr);
// 我们可以使用Buffer中的concat方法直接取出数组中的buffer
let query = Buffer.concat(arr).toString(); // username=admin&password=123
// 使用querystring模块处理query参数
let userObj = qs.parse(query);
// 进行登录操作
// 第一步 读取数据
let userArr = JSON.parse(fs.readFileSync('./data/user.json','utf-8'));
// 判断用户名在JSON中是否存在
let result = userArr.find(element=>element.username == userObj.username);
response.writeHead(200,{
'Content-type':'text/html;charset=utf-8'});
if (result == undefined){
response.end("");
}else{
// 判断密码是否正确
if (result.password != userObj.password){
response.end("");
}else{
response.end("");
}
}
})
}else{
readPage('./view/404.html',response,404);
}
}).listen(3000,'127.0.0.1',()=>{
console.log('服务器正在运行......');
})
Express 是一个简洁而灵活的 Node.js Web应用框架, 提供一系列强大特性帮助你创建各种Web应用
如果不计中间件,主体框架只有一千余行代码,非常简练
Express 不对 Node.js 已有的特性进行二次抽象,我们只是在它之上扩展了Web应用所需的功能
Express内部还是使用的http模块实现服务器创建和监听, 对http模块进行了二次封装
下载express模块, 命令 npm install express 在.js文件中, 引入express模块
初始化服务器对象 设置路由接口地址 (以供浏览器访问) 监听端口号 (给服务器设置端口号)
首先实例化一个应用 application 使用这个应用创建服务器 创建路由等操作
URL上面目录部分太长了, 可以让后台规定路由的路径(/api/getNew), 然后当访问时, 让后台去转发寻找
例如: 访问 /image/rB.jpg 就相当于 访问的是上面的路径资源 (但是具体访问什么, 还是后台说了算)
不同的路径, 对应的不同路由 (路由 = 路径) 所以所有的请求, 都看后台如何处理 API接口 = 路由 = 路径 = url地址
const express = require('express');
// 实例化出一个应用
let app = new express();
// console.log(express);
// console.log(app);
// 使用express实例创建路由
// 我们需要什么类型的路由就使用什么类型的路由
// 如果说前端的请求是通过get方法请求的 那么我们使用app中的get方法
// 如果说通过post方法 那么我们使用app中的post方法
// 如果说都可以 那么使用app中的all方法或者是use方法
// 内置两个参数 第一个参数是路由 第二个参数是回调函数
// 回调内置两个参数 第一个是请求 第二个参数是响应
app.get('/index',(request,response)=>{
// 响应的时候 直接使用send方法就可以 不需要响应头信息
response.send('你好 世界');
})
app.get('/login',(request,response)=>{
// 响应的时候 直接使用send方法就可以 不需要响应头信息
response.send('登录');
})
// 监听 使用listen方法 参数和Node原生参数一样
app.listen(3000,'127.0.0.1',()=>{
console.log('服务器正在运行......');
})
const express = require('express');
// 实例化出一个应用
let app = new express();
// console.log(express);
// console.log(app);
// 使用express实例创建路由
// 我们需要什么类型的路由就使用什么类型的路由
// 如果说前端的请求是通过get方法请求的 那么我们使用app中的get方法
// 如果说通过post方法 那么我们使用app中的post方法
// 如果说都可以 那么使用app中的all方法或者是use方法
// 内置两个参数 第一个参数是路由 第二个参数是回调函数
// 回调内置两个参数 第一个是请求 第二个参数是响应
app.post('/aa',(request,response)=>{
// 响应的时候 直接使用send方法就可以 不需要响应头信息
response.send('post');
})
// 监听 使用listen方法 参数和Node原生参数一样
app.listen(3000,'127.0.0.1',()=>{
console.log('服务器正在运行......');
})
const express = require('express');
// 实例化出一个应用
let app = new express();
app.all('/index',(request,response)=>{
response.send('你好 世界');
})
app.use('/ss',(request,response)=>{
response.send('你好 中国');
})
// 监听 使用listen方法 参数和Node原生参数一样
app.listen(3000,'127.0.0.1',()=>{
console.log('服务器正在运行......');
})
? : 匹配0个或者1个字符 ?前边的
+ :匹配1个或多个+号前边的字符
:通配符 匹配0个或者多个 *所在的位置
() :将里边的字符算作一个整体
正则匹配路由
const express = require('express');
let app = new express();
// ? : 匹配零次或者一次他前边的字符
// app.get('/ab?cd',(request,response)=>{
// response.send('你好 北京');
// })
// + : 匹配一次或者多次他前边的字符
// app.get('/ab+cd',(request,response)=>{
// response.send('你好 北京');
// })
// () : 将括号中的字符当成一个整体
// app.get('/a(bc)?d',(request,response)=>{
// response.send('你好 北京');
// })
// * : 匹配零次、一次或者多次他所在的位置
// app.get('/ab*cd',(request,response)=>{
// response.send('你好 北京');
// })
// 直接写正则表达式进行匹配
app.get(/b/,(request,response)=>{
response.send('你好 北京');
})
app.listen(3000,'127.0.0.1',()=>{
console.log('服务器正在运行......');
})
携带参数的路由 参数以:分开 路径参数是命名的url字段 用于捕获在url中的位置
捕获的值放在req.params中 路径的:user_id user_id对象的键
我们以后使用路由的时候 是可以在路由的后面添加一些参数 一般情况下 和get参数相同
我们使用的时候 在浏览器端传递参数和写路由是一样的
接收参数的时候 存储在request对象中的params属性中 params属性是一个对象
这个对象中装取的就是我们传递的参数 对象中的键是参数名 对象中的值是参数的值
const express = require('express');
let app = new express();
// 直接写正则表达式进行匹配
app.get('/index/:username/:password',(request,response)=>{
// 现在要传递两个参数 需要服务器端进行接受
// 在路由中 使用冒号
// console.log(request.params);
let {
username,password} = request.params;
response.send("用户名是" + username + ";密码是" + password);
})
app.listen(3000,'127.0.0.1',()=>{
console.log('服务器正在运行......');
})
路由级别路由指的就是二级路由 我们之前测试的都是一级路由
二级路由主要的作用是什么 第一是为了分类 第二是为了优化
主要是将我们主要的大型模块进行分类 也对我们的代码进行优化 不能所有的代码一个JS文件中
例如 图书管理系统
用户模块 : www.book.com/user/login www.book.com/user/register
图书模块 : www.book.com/book/add www.book.com/book/delete www.book.com/book/find
主页
const express = require('express');
const book = require('./router/bookRouter');
const user = require('./router/userRouter');
let app = new express();
// 配置路由
// 一般使用app中的use方法
app.use('/book',book);
app.use('/user',user);
// 首页
app.get('/',(request,response)=>{
response.status(200).send('欢迎来到康氏图书管理系统');
})
// 404
// app.all('/*',(request,response)=>{
// // 使用status方法加上http的状态码
// response.status(404).send('Not Found');
// })
app.use((request,response)=>{
response.status(404).send('Not Found');
})
app.listen(3000,'127.0.0.1',()=>{
console.log('服务器正在运行......');
})
路由页
const express = require('express');
// 实例化一个路由实例
let router = new express.Router();
router.get('/',(request,response)=>{
response.send('图书首页');
})
router.get('/add',(request,response)=>{
response.send('添加图书');
})
router.get('/delete',(request,response)=>{
response.send('删除图书');
})
router.get('/edit',(request,response)=>{
response.send('修改图书');
})
router.get('/find',(request,response)=>{
response.send('查询图书');
})
// 暴露模块
module.exports = router;
Express是一个自身功能极简,完全是路由和中间件构成一个web开发框架
一个Express应用就是在调用各种中间件。中间件在Express开发中很重要
中间件函数能够访问请求对象 (req)、响应对象 (res) 以及应用程序的请求/响应循环中的下一个中间件函数
该next功能是中间件函数中的一个功能,当被调用时,它将执行当前中间件之后的中间件
下一个中间件函数通常由名为 next 的变量来表示 next使用
1.static
2.urlencode
3.json
status : 处理http响应码
send : 处理404
get post all use
#############################################################################
Express是一个自身功能极简,完全是路由和中间件构成一个web开发框架
一个Express应用就是在调用各种中间件。中间件在Express开发中很重要
中间件函数能够访问请求对象 (req)、响应对象 (res) 以及应用程序的请求/响应循环中的下一个中间件函数
该next功能是中间件函数中的一个功能,当被调用时,它将执行当前中间件之后的中间件
下一个中间件函数通常由名为 next 的变量来表示 next使用
内置中间件其实就是express自身携带的一些功能 express框架有11个模块 这11个模块都是内置中间件
static 渲染静态资源 使用static中间件就是渲染资源
在项目中 一般使用它渲染css image js less 字体 不是渲染html
const express = require('express');
const path = require('path')
let app = express();
// 使用静态资源 一般使用app中的use方法进行
// 使用express.static 内置一个参数 参数是资源的路径
app.use('/haoyang',express.static(path.resolve(__dirname,'./hy')));
app.listen(3000,'127.0.0.1',()=>{
console.log('服务器正在运行......');
})
status : 处理http响应码
send : 处理404
自定义中间件其实就是说 我们一个路由无法完成的工作 让多个路由一起完成
const express = require('express');
const fs = require('fs');
const path = require('path');
let app = express();
// 中间件
// app.get('/index',(request,response)=>{
// let arr = [];
// // 取出用户数据
// fs.readFile('./data/a.json','utf-8',(error,data)=>{
// error ? console.log(11) : arr.push(JSON.parse(data));
// })
// console.log(arr);
// })
// 使用自定义中间件解决
// 自定义中间件我们选择使用use方法 因为不确定中间是get或者是post
// 创建多个路由 必须是同名 但是不能响应 使用next调用下一个中间件 同名路由
// next是回调函数的第三个参数
// 我们可以将前面的所有的中间键数据 存储在request对象中
app.use('/index',(request,response,next)=>{
fs.readFile('./data/a.json','utf-8',(error,data)=>{
let userEric = JSON.parse(data);
error ? console.log(11) : request.userEric = userEric;
})
setTimeout(() => {
next();
}, 200);
})
// 设定第二个路由 必须同名
app.use('/index',(request,response,next)=>{
fs.readFile('./data/b.json','utf-8',(error,data)=>{
let carEric = JSON.parse(data);
error ? console.log(11) : request.carEric = carEric;
})
setTimeout(() => {
next();
}, 200);
})
// 设定第三个路由 必须同名
app.use('/index',(request,response,next)=>{
fs.readFile('./data/c.json','utf-8',(error,data)=>{
let computerEric = JSON.parse(data)
error ? console.log(11) : request.computerEric = computerEric;
})
setTimeout(() => {
next();
}, 200);
})
// 响应出index路由的数据
app.use('/index',(request,response)=>{
let arr = [request.userEric,request.carEric,request.computerEric];
// console.log(request.userEric);
response.send(arr);
})
app.listen(3000,'127.0.0.1',()=>{
console.log('服务器正在运行......');
})
第三方中间件是我们使用最多的一种方式 因为Node本身几乎没有什么功能
express主题只有一千余行代码 所以说 使用的功能很少 几乎都要依赖于第三方中间件
第三方中间件 : 基于express的模块
热更新 我们使用Node的时候 编辑代码之后 我们经常会重启服务器 否则不加载
那么如果使用热更新 就不会有这个问题 实时更新 但是只能用来测试 不要上线运行
这是一个工具 所以说我们安装的时候 使用全局安装
命令 : npm install nodemon -g
安装之后生成一个命令 生成nodemon
安装好之后 打开package.json 把主文件改成我们要运行的文件
使用模板引擎的时候 需要先下载 但是模板引擎只需要下载 不需要安装
命令 : npm install pug
使用的时候 不需要引入
const express = require('express');
const path = require('path')
let app = express();
// 使用模板引擎之前 必须进行配置模板引擎
// 配置模板引擎使用的是app中的set方法
// 配置模板引擎的类型
app.set('view engine','pug');
// 配置模板引擎的路径
app.set('views','./template/');
// 配置好模板引擎就可以使用了
app.get('/index',(request,response)=>{
// 渲染模板引擎 使用response中的render方法
// render方法内置两个参数 第一个参数是模板引擎的名字
// 第二个参数是我们当前的程序 渲染给模板的数据
let str = 'Eric';
let num = 200;
// response.render('index');
response.render('index',{
str,num});
});
app.listen(3000,'127.0.0.1',()=>{
console.log('服务器正在运行......');
})
EJS 是一套简单的模板语言,在NodeJS后端利用ejs模板引擎, 生成 HTML 页面
纯 JavaScript
快速开发
执行迅速
语法简单: 可以直接在HTML中书写JS代码
同时支持前后端环境
相比pug, 更贴近于我们现使用的HTML标签语法
安装ejs模块, npm install ejs
配置模板引擎类型 : app.set(‘view engine’, ‘ejs’)
配置模板引擎的路径 : app.set(“views”, “./template”);
如果说在模板引擎中输出标识符 <%=标识符%>
对象不能直接进行输出 会出现【object object】
那么我们使用对象必须进行迭代 也就是遍历 我们在模板引擎的<%%> 里面 完全可以使用JS代码
如果说不涉及输出 只是读取JS代码 不能家等号 因为等号是输出的意思 <%%>
再如果说 我们传递到后台中的数据是一个html标签 那么需要使用<%-标识符%>
模板引擎可以让(网站)程序实现界面与数据分离,业务代码与逻辑代码的分离,这就大大提升了开发效率
良好的设计也使得代码重用变得更加容易
我们司空见惯的模板安装卸载等概念,基本上都和模板引擎有着千丝万缕的联系
模板引擎不只是可以让你实现代码分离,也可以实现数据分离(动态数据与静态数据),还可以实现代码单元共享
甚至是多语言、动态页面与静态页面自动均衡(SDE)等等与用户界面可能没有关系的功能
const express = require('express');
const path = require('path')
let app = express();
// 配置模板引擎类型
app.set('view engine','ejs');
// 配置模板引擎的路径
app.set('views','./template/');
// 配置好模板引擎就可以使用了
app.get('/index',(request,response)=>{
let num = 999999999999999;
let str = '你好 Eric';
let obj = {
name:'Eric',age:18,sex:'男'};
let arr = [1,3,5,7,9];
let data = [
{
name:'貂蝉',age:17,sex:'女'},
{
name:'大乔',age:18,sex:'女'},
{
name:'西施',age:16,sex:'女'},
{
name:'王昭君',age:14,sex:'女'},
{
name:'杨玉环',age:13,sex:'女'},
{
name:'褒姒',age:16,sex:'女'},
];
let html = "多多益善
";
response.render('index',{
num,str,obj,arr,data,html});
});
app.listen(3000,'127.0.0.1',()=>{
console.log('服务器正在运行......');
})
模板引擎文件
ejs模板引擎
姓名
年龄
性别
<% data.forEach(element=>{ %>
<%=element.name%>
<%=element.age%>
<%=element.sex%>
<% }) %>
<%-html%>
它主要是用来处理post参数的 因为表单或者ajax经常会和后台进行交互数据
那么交互数据的时候 怎么将数据传输到后台 不能像Node原生代码一样
所以说express内置中间件就有处理 但是我们习惯性的使用body-parser第三方中间件
命令 : npm install body-parser
引入到页面中使用 使用之前需要进行配置
配置接收表单参数 : app.use(bodyParser.urlencoded({extended : true}));
配置接收JSON :app.use(bodyParser.json());
使用bodyparser的时候 我们接收post参数 参数存储在request对象中的body属性中
和路由参数存储在params属性中的效果是一样的 如果说body属性中有数据 那么他就是一个对象
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path')
let app = express();
// 配置bodyparser 使用app中的use方法
// 配置表单数据 内置一个参数 参数是一个对象
// 如果说extended的值为false 那么可以接受 字符串和数组 如果说为true 那么可以接受任意数据类型
app.use(bodyParser.urlencoded({
extended : true}));
// 配置JSON
app.use(bodyParser.json());
// 配置模板引擎类型
app.set('view engine','ejs');
// 配置模板引擎的路径
app.set('views',path.resolve(__dirname,'./template/'));
// 配置好模板引擎就可以使用了
app.post('/index',(request,response)=>{
console.log(request.body);
});
app.listen(3000,'127.0.0.1',()=>{
console.log('服务器正在运行......');
})
主要是作为文件上传模块 这个模块我很少使用 因为他不能和其他表单数据一起上传
下载安装 : npm install multer
直接引入使用
const express = require('express');
const multer = require('multer');
const paths = require('path');
const fs = require('fs');
let app = express();
// 指定文件上传的目录
let upload = multer({
dest : './upload/'});
app.post('/index',upload.single('photo'),(request,response)=>{
// 我们可以使用request对象中的file属性 这是存储文件上传的内容
// console.log(request.file);
let {
originalname,path} = request.file;
/*
{
fieldname: 'photo',
originalname: '1.jpg',
mimetype: 'image/jpeg',
destination: './upload/',
filename: '0c15b12905fb3310dd1059de76f30984',
path: 'upload\\0c15b12905fb3310dd1059de76f30984',
size: 38045
}
*/
let extname = paths.extname(originalname);
let newFileName = './upload/' + Date.now() + extname;
fs.rename(path,newFileName,err=>{
if (err) console.log('123123');
else response.send('上传成功');
})
});
app.listen(3000,'127.0.0.1',()=>{
console.log('服务器正在运行......');
})
这个中间件也是文件上传 只不过他可以和其他的表单数据同时上传 所以一般时候使用的比较多
下载命令 : npm install formidable
他就是express的脚手架
get post all use
#############################################################################
Express是一个自身功能极简,完全是路由和中间件构成一个web开发框架
一个Express应用就是在调用各种中间件。中间件在Express开发中很重要
中间件函数能够访问请求对象 (req)、响应对象 (res) 以及应用程序的请求/响应循环中的下一个中间件函数
该next功能是中间件函数中的一个功能,当被调用时,它将执行当前中间件之后的中间件
下一个中间件函数通常由名为 next 的变量来表示 next使用
内置中间件其实就是express自身携带的一些功能 express框架有11个模块 这11个模块都是内置中间件
static 渲染静态资源 使用static中间件就是渲染资源
在项目中 一般使用它渲染css image js less 字体 不是渲染html
const express = require('express');
const path = require('path')
let app = express();
// 使用静态资源 一般使用app中的use方法进行
// 使用express.static 内置一个参数 参数是资源的路径
app.use('/haoyang',express.static(path.resolve(__dirname,'./hy')));
app.listen(3000,'127.0.0.1',()=>{
console.log('服务器正在运行......');
})
status : 处理http响应码
send : 处理404
自定义中间件其实就是说 我们一个路由无法完成的工作 让多个路由一起完成
const express = require('express');
const fs = require('fs');
const path = require('path');
let app = express();
// 中间件
// app.get('/index',(request,response)=>{
// let arr = [];
// // 取出用户数据
// fs.readFile('./data/a.json','utf-8',(error,data)=>{
// error ? console.log(11) : arr.push(JSON.parse(data));
// })
// console.log(arr);
// })
// 使用自定义中间件解决
// 自定义中间件我们选择使用use方法 因为不确定中间是get或者是post
// 创建多个路由 必须是同名 但是不能响应 使用next调用下一个中间件 同名路由
// next是回调函数的第三个参数
// 我们可以将前面的所有的中间键数据 存储在request对象中
app.use('/index',(request,response,next)=>{
fs.readFile('./data/a.json','utf-8',(error,data)=>{
let userEric = JSON.parse(data);
error ? console.log(11) : request.userEric = userEric;
})
setTimeout(() => {
next();
}, 200);
})
// 设定第二个路由 必须同名
app.use('/index',(request,response,next)=>{
fs.readFile('./data/b.json','utf-8',(error,data)=>{
let carEric = JSON.parse(data);
error ? console.log(11) : request.carEric = carEric;
})
setTimeout(() => {
next();
}, 200);
})
// 设定第三个路由 必须同名
app.use('/index',(request,response,next)=>{
fs.readFile('./data/c.json','utf-8',(error,data)=>{
let computerEric = JSON.parse(data)
error ? console.log(11) : request.computerEric = computerEric;
})
setTimeout(() => {
next();
}, 200);
})
// 响应出index路由的数据
app.use('/index',(request,response)=>{
let arr = [request.userEric,request.carEric,request.computerEric];
// console.log(request.userEric);
response.send(arr);
})
app.listen(3000,'127.0.0.1',()=>{
console.log('服务器正在运行......');
})
第三方中间件是我们使用最多的一种方式 因为Node本身几乎没有什么功能
express主题只有一千余行代码 所以说 使用的功能很少 几乎都要依赖于第三方中间件
第三方中间件 : 基于express的模块
热更新 我们使用Node的时候 编辑代码之后 我们经常会重启服务器 否则不加载
那么如果使用热更新 就不会有这个问题 实时更新 但是只能用来测试 不要上线运行
这是一个工具 所以说我们安装的时候 使用全局安装
命令 : npm install nodemon -g
安装之后生成一个命令 生成nodemon
安装好之后 打开package.json 把主文件改成我们要运行的文件
使用模板引擎的时候 需要先下载 但是模板引擎只需要下载 不需要安装
命令 : npm install pug
使用的时候 不需要引入
const express = require('express');
const path = require('path')
let app = express();
// 使用模板引擎之前 必须进行配置模板引擎
// 配置模板引擎使用的是app中的set方法
// 配置模板引擎的类型
app.set('view engine','pug');
// 配置模板引擎的路径
app.set('views','./template/');
// 配置好模板引擎就可以使用了
app.get('/index',(request,response)=>{
// 渲染模板引擎 使用response中的render方法
// render方法内置两个参数 第一个参数是模板引擎的名字
// 第二个参数是我们当前的程序 渲染给模板的数据
let str = 'Eric';
let num = 200;
// response.render('index');
response.render('index',{
str,num});
});
app.listen(3000,'127.0.0.1',()=>{
console.log('服务器正在运行......');
})
EJS 是一套简单的模板语言,在NodeJS后端利用ejs模板引擎, 生成 HTML 页面
纯 JavaScript
快速开发
执行迅速
语法简单: 可以直接在HTML中书写JS代码
同时支持前后端环境
相比pug, 更贴近于我们现使用的HTML标签语法
安装ejs模块, npm install ejs
配置模板引擎类型 : app.set(‘view engine’, ‘ejs’)
配置模板引擎的路径 : app.set(“views”, “./template”);
如果说在模板引擎中输出标识符 <%=标识符%>
对象不能直接进行输出 会出现【object object】
那么我们使用对象必须进行迭代 也就是遍历 我们在模板引擎的<%%> 里面 完全可以使用JS代码
如果说不涉及输出 只是读取JS代码 不能家等号 因为等号是输出的意思 <%%>
再如果说 我们传递到后台中的数据是一个html标签 那么需要使用<%-标识符%>
模板引擎可以让(网站)程序实现界面与数据分离,业务代码与逻辑代码的分离,这就大大提升了开发效率
良好的设计也使得代码重用变得更加容易
我们司空见惯的模板安装卸载等概念,基本上都和模板引擎有着千丝万缕的联系
模板引擎不只是可以让你实现代码分离,也可以实现数据分离(动态数据与静态数据),还可以实现代码单元共享
甚至是多语言、动态页面与静态页面自动均衡(SDE)等等与用户界面可能没有关系的功能
const express = require('express');
const path = require('path')
let app = express();
// 配置模板引擎类型
app.set('view engine','ejs');
// 配置模板引擎的路径
app.set('views','./template/');
// 配置好模板引擎就可以使用了
app.get('/index',(request,response)=>{
let num = 999999999999999;
let str = '你好 Eric';
let obj = {
name:'Eric',age:18,sex:'男'};
let arr = [1,3,5,7,9];
let data = [
{
name:'貂蝉',age:17,sex:'女'},
{
name:'大乔',age:18,sex:'女'},
{
name:'西施',age:16,sex:'女'},
{
name:'王昭君',age:14,sex:'女'},
{
name:'杨玉环',age:13,sex:'女'},
{
name:'褒姒',age:16,sex:'女'},
];
let html = "多多益善
";
response.render('index',{
num,str,obj,arr,data,html});
});
app.listen(3000,'127.0.0.1',()=>{
console.log('服务器正在运行......');
})
模板引擎文件
ejs模板引擎
姓名
年龄
性别
<% data.forEach(element=>{ %>
<%=element.name%>
<%=element.age%>
<%=element.sex%>
<% }) %>
<%-html%>
它主要是用来处理post参数的 因为表单或者ajax经常会和后台进行交互数据
那么交互数据的时候 怎么将数据传输到后台 不能像Node原生代码一样
所以说express内置中间件就有处理 但是我们习惯性的使用body-parser第三方中间件
命令 : npm install body-parser
引入到页面中使用 使用之前需要进行配置
配置接收表单参数 : app.use(bodyParser.urlencoded({extended : true}));
配置接收JSON :app.use(bodyParser.json());
使用bodyparser的时候 我们接收post参数 参数存储在request对象中的body属性中
和路由参数存储在params属性中的效果是一样的 如果说body属性中有数据 那么他就是一个对象
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path')
let app = express();
// 配置bodyparser 使用app中的use方法
// 配置表单数据 内置一个参数 参数是一个对象
// 如果说extended的值为false 那么可以接受 字符串和数组 如果说为true 那么可以接受任意数据类型
app.use(bodyParser.urlencoded({
extended : true}));
// 配置JSON
app.use(bodyParser.json());
// 配置模板引擎类型
app.set('view engine','ejs');
// 配置模板引擎的路径
app.set('views',path.resolve(__dirname,'./template/'));
// 配置好模板引擎就可以使用了
app.post('/index',(request,response)=>{
console.log(request.body);
});
app.listen(3000,'127.0.0.1',()=>{
console.log('服务器正在运行......');
})
主要是作为文件上传模块 这个模块我很少使用 因为他不能和其他表单数据一起上传
下载安装 : npm install multer
直接引入使用
const express = require('express');
const multer = require('multer');
const paths = require('path');
const fs = require('fs');
let app = express();
// 指定文件上传的目录
let upload = multer({
dest : './upload/'});
app.post('/index',upload.single('photo'),(request,response)=>{
// 我们可以使用request对象中的file属性 这是存储文件上传的内容
// console.log(request.file);
let {
originalname,path} = request.file;
/*
{
fieldname: 'photo',
originalname: '1.jpg',
mimetype: 'image/jpeg',
destination: './upload/',
filename: '0c15b12905fb3310dd1059de76f30984',
path: 'upload\\0c15b12905fb3310dd1059de76f30984',
size: 38045
}
*/
let extname = paths.extname(originalname);
let newFileName = './upload/' + Date.now() + extname;
fs.rename(path,newFileName,err=>{
if (err) console.log('123123');
else response.send('上传成功');
})
});
app.listen(3000,'127.0.0.1',()=>{
console.log('服务器正在运行......');
})
这个中间件也是文件上传 只不过他可以和其他的表单数据同时上传 所以一般时候使用的比较多
下载命令 : npm install formidable
const express = require('express');
let formidable = require('formidable');
const path = require('path');
const fs = require('fs');
let app = express();
// 上传文件
app.post('/index',(request,response)=>{
// 实例化一个上传实例
let form = new formidable.IncomingForm();
// 指定上传的路径
form.uploadDir = path.resolve(__dirname,'./upload/');
// 开始上传 使用form对象中的parse方法进行上传
// 内置两个个参数 第一个参数是请求对象request 第二个参数是回调函数
// 回调内置三个参数 第一个参数是错误 第二个参数是上传的其他字段 第三个参数是上传的文件信息
form.parse(request,(error,fields,files)=>{
if (error){
console.log('爱慕骚瑞');
}else{
// 获取我们上传的对象
let file = files.photo;
// 取出我们上传的对象的临时路径和临时文件名
let oldname = file.path;
// 创建新的文件名 避免命名冲突 使用时间戳命名
let fileName = Date.now() + path.extname(file.name);
// 指定新的上传路径 然后改名
let newName = path.resolve(__dirname,'./upload/',fileName);
// 更名操作
fs.rename(oldname,newName,err=>{
err ? console.log('111') : response.send('上传成功')
})
}
})
});
app.listen(3000,'127.0.0.1',()=>{
console.log('服务器正在运行......');
})
crypto模块是加密模块 密码在数据库中或者存储的任何地方都不能是直接的密码 必须进行加密
下载模块命令 : npm install crypto
下载之后 直接使用就可以
const crypto = require('crypto');
// 加密
let str = '123';
// 使用crypto中的create方法 创建加密对象
// 内置一个参数 参数是加密的格式
let mdpass = crypto.createHash('md5');
// 使用加密对象中的update方法进行加密字符串
mdpass.update(str);
// 最后获取结果 使用digest方法获取结果
// 一般情况下 密码加密传递的参数是hex
// console.log(mdpass.digest('base64'));
console.log(mdpass.digest('hex'));
验证码使用模块svg-captcha
下载命令 : npm install svg-captcha
const svgCaptcha = require('svg-captcha');
const express = require('express');
const app = express();
app.get('/',(request,response)=>{
// 使用svgCaptcha对象中的create方法创建验证码
// 验证码其实可以添加一些属性 增强验证码
// 可以传递一个参数 参数是对象 对象中有一些属性
// let svg = svgCaptcha.create();
let svg = svgCaptcha.create({
size : 6, // 码的数量
color : 'red',
background : '#CCC',
noise : 10
});
// 创建出来的验证码对象中一共有两个属性
// 一个是data 验证码图片
// 一个是text 验证码途中中的码
response.send(svg.data);
console.log(svg.text);
})
app.listen(3000);
网站的小图标 首先下载模块
下载命令 : npm install serve-favicon
使用的时候 注意 千万不要写错
app.use(favicon(path.join(__dirname,'youjiuye.ico')))
他就是express的脚手架 直接生成很多文件或者文件夹 生成一个框架
下载命令 : npm install express-generator -g
注意 需要全局安装 安装之后在doc命令中生成express命令 直接使用 生成脚手架
全局下载存储路径 : C:\Users\Administrator\AppData\Roaming\npm
get post all use
#############################################################################
Express是一个自身功能极简,完全是路由和中间件构成一个web开发框架
一个Express应用就是在调用各种中间件。中间件在Express开发中很重要
中间件函数能够访问请求对象 (req)、响应对象 (res) 以及应用程序的请求/响应循环中的下一个中间件函数
该next功能是中间件函数中的一个功能,当被调用时,它将执行当前中间件之后的中间件
下一个中间件函数通常由名为 next 的变量来表示 next使用
内置中间件其实就是express自身携带的一些功能 express框架有11个模块 这11个模块都是内置中间件
static 渲染静态资源 使用static中间件就是渲染资源
在项目中 一般使用它渲染css image js less 字体 不是渲染html
const express = require('express');
const path = require('path')
let app = express();
// 使用静态资源 一般使用app中的use方法进行
// 使用express.static 内置一个参数 参数是资源的路径
app.use('/haoyang',express.static(path.resolve(__dirname,'./hy')));
app.listen(3000,'127.0.0.1',()=>{
console.log('服务器正在运行......');
})
status : 处理http响应码
send : 处理404
自定义中间件其实就是说 我们一个路由无法完成的工作 让多个路由一起完成
const express = require('express');
const fs = require('fs');
const path = require('path');
let app = express();
// 中间件
// app.get('/index',(request,response)=>{
// let arr = [];
// // 取出用户数据
// fs.readFile('./data/a.json','utf-8',(error,data)=>{
// error ? console.log(11) : arr.push(JSON.parse(data));
// })
// console.log(arr);
// })
// 使用自定义中间件解决
// 自定义中间件我们选择使用use方法 因为不确定中间是get或者是post
// 创建多个路由 必须是同名 但是不能响应 使用next调用下一个中间件 同名路由
// next是回调函数的第三个参数
// 我们可以将前面的所有的中间键数据 存储在request对象中
app.use('/index',(request,response,next)=>{
fs.readFile('./data/a.json','utf-8',(error,data)=>{
let userEric = JSON.parse(data);
error ? console.log(11) : request.userEric = userEric;
})
setTimeout(() => {
next();
}, 200);
})
// 设定第二个路由 必须同名
app.use('/index',(request,response,next)=>{
fs.readFile('./data/b.json','utf-8',(error,data)=>{
let carEric = JSON.parse(data);
error ? console.log(11) : request.carEric = carEric;
})
setTimeout(() => {
next();
}, 200);
})
// 设定第三个路由 必须同名
app.use('/index',(request,response,next)=>{
fs.readFile('./data/c.json','utf-8',(error,data)=>{
let computerEric = JSON.parse(data)
error ? console.log(11) : request.computerEric = computerEric;
})
setTimeout(() => {
next();
}, 200);
})
// 响应出index路由的数据
app.use('/index',(request,response)=>{
let arr = [request.userEric,request.carEric,request.computerEric];
// console.log(request.userEric);
response.send(arr);
})
app.listen(3000,'127.0.0.1',()=>{
console.log('服务器正在运行......');
})
第三方中间件是我们使用最多的一种方式 因为Node本身几乎没有什么功能
express主题只有一千余行代码 所以说 使用的功能很少 几乎都要依赖于第三方中间件
第三方中间件 : 基于express的模块
热更新 我们使用Node的时候 编辑代码之后 我们经常会重启服务器 否则不加载
那么如果使用热更新 就不会有这个问题 实时更新 但是只能用来测试 不要上线运行
这是一个工具 所以说我们安装的时候 使用全局安装
命令 : npm install nodemon -g
安装之后生成一个命令 生成nodemon
安装好之后 打开package.json 把主文件改成我们要运行的文件
使用模板引擎的时候 需要先下载 但是模板引擎只需要下载 不需要安装
命令 : npm install pug
使用的时候 不需要引入
const express = require('express');
const path = require('path')
let app = express();
// 使用模板引擎之前 必须进行配置模板引擎
// 配置模板引擎使用的是app中的set方法
// 配置模板引擎的类型
app.set('view engine','pug');
// 配置模板引擎的路径
app.set('views','./template/');
// 配置好模板引擎就可以使用了
app.get('/index',(request,response)=>{
// 渲染模板引擎 使用response中的render方法
// render方法内置两个参数 第一个参数是模板引擎的名字
// 第二个参数是我们当前的程序 渲染给模板的数据
let str = 'Eric';
let num = 200;
// response.render('index');
response.render('index',{
str,num});
});
app.listen(3000,'127.0.0.1',()=>{
console.log('服务器正在运行......');
})
EJS 是一套简单的模板语言,在NodeJS后端利用ejs模板引擎, 生成 HTML 页面
纯 JavaScript
快速开发
执行迅速
语法简单: 可以直接在HTML中书写JS代码
同时支持前后端环境
相比pug, 更贴近于我们现使用的HTML标签语法
安装ejs模块, npm install ejs
配置模板引擎类型 : app.set(‘view engine’, ‘ejs’)
配置模板引擎的路径 : app.set(“views”, “./template”);
如果说在模板引擎中输出标识符 <%=标识符%>
对象不能直接进行输出 会出现【object object】
那么我们使用对象必须进行迭代 也就是遍历 我们在模板引擎的<%%> 里面 完全可以使用JS代码
如果说不涉及输出 只是读取JS代码 不能家等号 因为等号是输出的意思 <%%>
再如果说 我们传递到后台中的数据是一个html标签 那么需要使用<%-标识符%>
模板引擎可以让(网站)程序实现界面与数据分离,业务代码与逻辑代码的分离,这就大大提升了开发效率
良好的设计也使得代码重用变得更加容易
我们司空见惯的模板安装卸载等概念,基本上都和模板引擎有着千丝万缕的联系
模板引擎不只是可以让你实现代码分离,也可以实现数据分离(动态数据与静态数据),还可以实现代码单元共享
甚至是多语言、动态页面与静态页面自动均衡(SDE)等等与用户界面可能没有关系的功能
const express = require('express');
const path = require('path')
let app = express();
// 配置模板引擎类型
app.set('view engine','ejs');
// 配置模板引擎的路径
app.set('views','./template/');
// 配置好模板引擎就可以使用了
app.get('/index',(request,response)=>{
let num = 999999999999999;
let str = '你好 Eric';
let obj = {
name:'Eric',age:18,sex:'男'};
let arr = [1,3,5,7,9];
let data = [
{
name:'貂蝉',age:17,sex:'女'},
{
name:'大乔',age:18,sex:'女'},
{
name:'西施',age:16,sex:'女'},
{
name:'王昭君',age:14,sex:'女'},
{
name:'杨玉环',age:13,sex:'女'},
{
name:'褒姒',age:16,sex:'女'},
];
let html = "多多益善
";
response.render('index',{
num,str,obj,arr,data,html});
});
app.listen(3000,'127.0.0.1',()=>{
console.log('服务器正在运行......');
})
模板引擎文件
ejs模板引擎
姓名
年龄
性别
<% data.forEach(element=>{ %>
<%=element.name%>
<%=element.age%>
<%=element.sex%>
<% }) %>
<%-html%>
它主要是用来处理post参数的 因为表单或者ajax经常会和后台进行交互数据
那么交互数据的时候 怎么将数据传输到后台 不能像Node原生代码一样
所以说express内置中间件就有处理 但是我们习惯性的使用body-parser第三方中间件
命令 : npm install body-parser
引入到页面中使用 使用之前需要进行配置
配置接收表单参数 : app.use(bodyParser.urlencoded({extended : true}));
配置接收JSON :app.use(bodyParser.json());
使用bodyparser的时候 我们接收post参数 参数存储在request对象中的body属性中
和路由参数存储在params属性中的效果是一样的 如果说body属性中有数据 那么他就是一个对象
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path')
let app = express();
// 配置bodyparser 使用app中的use方法
// 配置表单数据 内置一个参数 参数是一个对象
// 如果说extended的值为false 那么可以接受 字符串和数组 如果说为true 那么可以接受任意数据类型
app.use(bodyParser.urlencoded({
extended : true}));
// 配置JSON
app.use(bodyParser.json());
// 配置模板引擎类型
app.set('view engine','ejs');
// 配置模板引擎的路径
app.set('views',path.resolve(__dirname,'./template/'));
// 配置好模板引擎就可以使用了
app.post('/index',(request,response)=>{
console.log(request.body);
});
app.listen(3000,'127.0.0.1',()=>{
console.log('服务器正在运行......');
})
主要是作为文件上传模块 这个模块我很少使用 因为他不能和其他表单数据一起上传
下载安装 : npm install multer
直接引入使用
const express = require('express');
const multer = require('multer');
const paths = require('path');
const fs = require('fs');
let app = express();
// 指定文件上传的目录
let upload = multer({
dest : './upload/'});
app.post('/index',upload.single('photo'),(request,response)=>{
// 我们可以使用request对象中的file属性 这是存储文件上传的内容
// console.log(request.file);
let {
originalname,path} = request.file;
/*
{
fieldname: 'photo',
originalname: '1.jpg',
mimetype: 'image/jpeg',
destination: './upload/',
filename: '0c15b12905fb3310dd1059de76f30984',
path: 'upload\\0c15b12905fb3310dd1059de76f30984',
size: 38045
}
*/
let extname = paths.extname(originalname);
let newFileName = './upload/' + Date.now() + extname;
fs.rename(path,newFileName,err=>{
if (err) console.log('123123');
else response.send('上传成功');
})
});
app.listen(3000,'127.0.0.1',()=>{
console.log('服务器正在运行......');
})
这个中间件也是文件上传 只不过他可以和其他的表单数据同时上传 所以一般时候使用的比较多
下载命令 : npm install formidable
const express = require('express');
let formidable = require('formidable');
const path = require('path');
const fs = require('fs');
let app = express();
// 上传文件
app.post('/index',(request,response)=>{
// 实例化一个上传实例
let form = new formidable.IncomingForm();
// 指定上传的路径
form.uploadDir = path.resolve(__dirname,'./upload/');
// 开始上传 使用form对象中的parse方法进行上传
// 内置两个个参数 第一个参数是请求对象request 第二个参数是回调函数
// 回调内置三个参数 第一个参数是错误 第二个参数是上传的其他字段 第三个参数是上传的文件信息
form.parse(request,(error,fields,files)=>{
if (error){
console.log('爱慕骚瑞');
}else{
// 获取我们上传的对象
let file = files.photo;
// 取出我们上传的对象的临时路径和临时文件名
let oldname = file.path;
// 创建新的文件名 避免命名冲突 使用时间戳命名
let fileName = Date.now() + path.extname(file.name);
// 指定新的上传路径 然后改名
let newName = path.resolve(__dirname,'./upload/',fileName);
// 更名操作
fs.rename(oldname,newName,err=>{
err ? console.log('111') : response.send('上传成功')
})
}
})
});
app.listen(3000,'127.0.0.1',()=>{
console.log('服务器正在运行......');
})
crypto模块是加密模块 密码在数据库中或者存储的任何地方都不能是直接的密码 必须进行加密
下载模块命令 : npm install crypto
下载之后 直接使用就可以
const crypto = require('crypto');
// 加密
let str = '123';
// 使用crypto中的create方法 创建加密对象
// 内置一个参数 参数是加密的格式
let mdpass = crypto.createHash('md5');
// 使用加密对象中的update方法进行加密字符串
mdpass.update(str);
// 最后获取结果 使用digest方法获取结果
// 一般情况下 密码加密传递的参数是hex
// console.log(mdpass.digest('base64'));
console.log(mdpass.digest('hex'));
验证码使用模块svg-captcha
下载命令 : npm install svg-captcha
const svgCaptcha = require('svg-captcha');
const express = require('express');
const app = express();
app.get('/',(request,response)=>{
// 使用svgCaptcha对象中的create方法创建验证码
// 验证码其实可以添加一些属性 增强验证码
// 可以传递一个参数 参数是对象 对象中有一些属性
// let svg = svgCaptcha.create();
let svg = svgCaptcha.create({
size : 6, // 码的数量
color : 'red',
background : '#CCC',
noise : 10
});
// 创建出来的验证码对象中一共有两个属性
// 一个是data 验证码图片
// 一个是text 验证码途中中的码
response.send(svg.data);
console.log(svg.text);
})
app.listen(3000);
网站的小图标 首先下载模块
下载命令 : npm install serve-favicon
使用的时候 注意 千万不要写错
app.use(favicon(path.join(__dirname,'youjiuye.ico')))
他就是express的脚手架 直接生成很多文件或者文件夹 生成一个框架
下载命令 : npm install express-generator -g
注意 需要全局安装 安装之后在doc命令中生成express命令 直接使用 生成脚手架
全局下载存储路径 : C:\Users\Administrator\AppData\Roaming\npm
他是存储早浏览器端的会话控制 它能够存储一些数据 存储的数据有限
能够在当前站点下进行共享数据 使用中间件cookie-parser
下载命令 : npm install cookieparser
使用的时候配置 cookieParser()
主要的作用 :
1.30天免登陆
2.记录用户偏好
const cookieParser = require('cookie-parser');
const express = require('express');
const app = express();
// 配置cookie 使用app中的use
app.use(cookieParser());
app.get('/',(request,response)=>{
// 我们在具体使用cookie的时候 需要怎么定义
// cookie存储在response对象中的cookie函数中
// 使用cookie方法 内置两个或者三个参数
// 第一个参数是cookie的名字 第二个参数是cookie的值
// 第三个参数是生命周期 cookie的过期时间
// 如果没有第三个参数 那么默认是浏览器关闭的时候
response.cookie('uname','Eric');
response.cookie('username','EricGreen',{
maxAge : 60 * 1000});
response.send('COOKIE')
})
app.listen(3000);
这是存储在服务器端的会话控制 它能够记录用户的登录状态
相对来说比较安全 因为存储在服务器端 客户无法操作
http协议是一种无状态协议 只负责连接 但是他不能告诉你谁在连接
必须说那个session记录我们用户的登录状态
使用session的时候 必须依赖于cookie
session的使用 下载模块express-session
下载命令 : npm install express-session
配置session
secret:“自定义名字” : 对session使用相关的cookie签名
resave:true : 强制session保存
saveUninitialized:false : 是否保存未初始化的会话任务
cookie:{maxAge:1000603} : 过期时间
session和cookie的区别
cookie存储在浏览器端 session保存在服务器端
cookie不安全 session相对来说比较安全
cookie单体的存储最多不能超过4KB session没有限制 但是一定不要太大 因为给服务器压力太大
cookie在一个站点中最多能有40个 但是session只能有一个
session依赖与cookie生成的session_id
1.开发的时候需要一个开发环境,要是我们修改一下代码保存之后浏览器就自动展现最新的代码那就好了
2.本地写代码的时候,要是调后端的接口不跨域就好了(代理服务)
3.为了跟上时代,要是能用上什么ES678N等等新东西就好了(翻译服务)
4.项目要上线了,要是能一键压缩代码啊图片什么的就好了(压缩打包服务)
相对于其他模块打包工具(Grant/Gulp)优势
node是nodejs运行的环境, npm是安装node一起安装的包管理器的工具, 可以方便的管理我们需要的所有第三方依赖包
webpack通常使用npm包管理工具进行安装。现在webpack对外公布的稳定版本是webpack4
全局安装webpack : 命令 npm install webpack -g
命令 | 安装环境 | 备注 |
---|---|---|
npm view webpack versions --json | 不安装, 查看 | 查看现在所有webpack模块的版本号 |
npm install webpack -g | -g 全局安装 |
在全局安装webpack 在电脑就可以使用webpack命令了(工具类模块要全局) |
webpack -v | 不安装, 查看全局webpack版本号(注意, webpack4.x版本, 还要安装webpack-cli工具才可以运行此命令) | 可能出现的问题: 1. webpack不是内部或外部命令(证明你全局安装失败/计算机的环境变量node的配置失效) |
webpack的命令, 大多都会执行webpack-cli里的Api方法, 来实现具体的功能效果, 所以webpack4.x版本需要在全局安装此模块, 而webpack3.x没有抽离出来那些API方法, 所以webpack3.x则不需要安装此模块
命令: npm i webpack-cli -g
注意webpack4 配合 webpack-cli3.x版本
Webpack为开发者提供了程序打包的配置信息入口,让开发者可以更好的控制, 管理程序的打包过程与最后程序的输出结果。默认的webpack配置文件是webpack.config.js, 运行webpack打包命令, 会自动查看运行命令时, 所在目录下的webpack.config.js文件
注意: webpack4.x版本可以省略这个文件, webpack3.x版本是必须声明此文件的
官网链接: https://www.webpackjs.com/concepts/
webpack的概念名 | 解释 |
---|---|
入口起点 | 基础目录, 指定了"./src"目录, 那么下面所有的配置中使用的相对路径, 都是以src为起点 |
入口 | 入口起点指示 webpack 应该使用哪个模块来作为构建其内部依赖图的开始 进入起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的 |
出口 | output告诉 webpack 在哪输出它所创建的结果及如何命名文件,默认值为 ./dist |
加载器 | loader 让 webpack 能去处理非 JavaScript 文件(webpack 自身只理解 JavaScript)loader 可以将所有类型的文件转换为webpack 能够处理的有效模块 然后你就可以利用 webpack 的打包能力,对它们进行处理。 |
插件 | loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。 插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。 插件接口功能极其强大,可以用来处理各种各样的任务。 |
模式 | 通过选择 development 或 production 之中的一个,来设置 mode 参数,你可以启用相应模式下的 webpack 内置的优化 |
官网链接: https://www.webpackjs.com/configuration/
键名 | 概念 | 解释 |
---|---|---|
context | 入口起点 | 基础目录,绝对路径,用于从配置中解析入口起点(entry point) |
entry | 入口 (必须) | 配置打包入口文件的名字 |
output | 出口 (必须) | 打包后输出到哪里, 和输出的文件名 |
module | 加载器配置 | 在rules对应数组中, 定义对象规则 |
plugins | 插件配置 | 配置插件功能 |
mode | 模式 | 选择线上/线下环境模式 |
devtool | 开发工具 | 如何生成 source map, 记录代码所在文件的行数 (调试方式) |
webpack命令会自动查找当前命令所在目录下的 webpack.config.js 配置文件, 根据配置文件进行代码的打包
module.exports = {
context: __dirname + "/src", // 拼接src的绝对路径, context设置入口的文件前缀, 代表入口要从这个文件夹开始寻找 (必须是绝对路径) __dirname: 指的当前文件所在文件夹的路径路径
entry: "./main.js",
output: {
path: __dirname + '/dist', // 给我输出到同级下的dist目录中(如果没有会自动创建)
filename: 'bundle.js' // 输出js文件的名字
}
};
module.exports = {
context: path.resolve(__dirname, "src"),
entry: ["./main.js", "./center.js"], // 设置多入口文件路径
output: {
path: __dirname + '/dist',
filename: 'bundle.js'
}
};
module.exports = {
context: path.resolve(__dirname, "src"),
entry: {
"first": "./main.js",
"second": "./center.js"
}, // 如果是多入口单出口用数组结构, 如果是多入口, 多出口用对象结构, 而且key值是打包后的文件名
output: {
path: __dirname + '/dist',
filename: '[name].js' // [name]是webpack内置的字符串变量, 对应entry里每个key
}
}
需要下载2个加载器模块 (目的是为了让webpack认识css文件)
在webpack.config.js中, 加载器配置
module: {
// 对加载器进行配置
rules: [ // 加载器的使用规则
{
// 独立的规则对象
test: /\.css$/, // 以.css结尾的文件类型
use: [ 'style-loader', 'css-loader' ] // 使用哪些加载器进行转换
// 注意: 2个加载器的顺序, 默认是从右往左进行使用
}
]
}
在入口文件引入css模块
import "./style/home.css" // 注意无需用变量接收
会把css代码以字符串的形式, 打包进js文件当中
在dist下新建index.html, 只要引入打包后的bundle.js, 来查看css代码被打包进js的效果即可
需要下载1个插件模块
HtmlWebpackPlugin
简化了HTML文件的创建,你可以让插件为你生成一个HTML文件,使用默认模板, 或使用你自己指定的模板webpack.config.js插件配置
const HtmlWebpackPlugin = require("html-webpack-plugin");
plugins: [ // 配置各种插件
new HtmlWebpackPlugin({
// 插件配置对象
title: "webpack ldx使用",
filename: "index.html", // 产出文件名(在dist目录查看)
template: __dirname + "/index.html", // 以此文件来作为基准(注意绝对路径, 因为此文件不在src下 ,用path.resolve(__dirname,"./index.html"))
inject: true, // 代表打包后的资源都引入到html的什么位置
favicon: "./assets/favicon.ico", // 插入打包后的favicon图标
// base: "./", // html网页中所有相对路径的前缀 (一般不给/给./, 虚拟路径)
// 控制html文件是否要压缩(true压缩, false不压缩)
minify: {
//对html文件进行压缩,
collapseBooleanAttributes: true, //是否简写boolean格式的属性如:disabled="disabled"简写为disabled
collapseWhitespace: true, //是否去除空格,默认false
minifyCSS: true, //是否压缩html里的css(使用clean-css进行的压缩) 默认值false
minifyJS: true, //是否压缩html里的js(使用uglify-js进行的压缩)
removeAttributeQuotes: true, //是否移除属性的引号 默认false
removeComments: true, //是否移除注释 默认false
removeCommentsFromCDATA: true, //从脚本和样式删除的注释, 默认false
useShortDoctype: true //使用短的文档类型,将文档转化成html5,默认false
}
}) // 数组元素是插件new对象
]
src/index.html 静态网页模板
执行webpack打包命令, 观察在dist生成的目录中, 是否新增了xxx.html文件, 并且会自动引入所有需要的外部资源
Cannot find module “webpack/lib/node/NodeTeplatePlugins”
在安装html-webpack-plugin插件的工程中, 单独的在本地安装一下跟全局webpack对应的版本
选项key | 值类型 | 默认值 | 解释 |
---|---|---|---|
title | String | Webpack App | 在生成html网页中标签里的内容 (如果指定template选项, 则此选项失效 |
filename | String | index.html | 生成的html网页文件的名字 (也可以设置目录+名字) |
template | String | 以哪个现有的html文件作为基础模板, 在此模板的基础上, 生成html网页文件 | |
inject | Boolean/String | true | 值的范围(true || ‘head’ || ‘body’ || false) true/‘body’ -> script等引入代码, 放到body标签内部末尾 ‘head’/false -> script等引入代码, 放到head标签内部末尾 |
favicon | String | 将制定favicon.ico图标的路径, 插入到html网页中去 | |
base | String | 制定html中所有相对路径, 都以它的值为出发起点, 例如: base的值为/bar/, 那么你HTML网页里的img, src=“my.img”, 那实际上去找的路径其实是 /bar/my.img | |
minify | Boolean | 看mode的值 | 是否压缩html代码, 如果mode为’production’, 那么minify的值为true, 否则为false |
需要引入1个插件模块
*.css
,移动到独立分离的CSS文件。因此,你的样式将不再内嵌到JS中,而是会放到一个单独的CSS文件中。如果你的样式文件较大,这会做更快加载,因为CSS会跟JS 并行加载。webpack.config.js加载器修改:
const ExtractTextPlugin = require("extract-text-webpack-plugin");
rules: [ // 加载器的使用规则
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
// 从一个已存在的 loader 中,创建一个提取(extract) loader。
fallback: "style-loader", // 应用于当CSS没有被提取(正常使用use:css-loader进行提取, 如果失败, 则使用fallback来提取)
use: "css-loader" // loader被用于将资源转换成一个CSS单独文件
})
}
]
插件配置:
其他选项默认即可
new ExtractTextPlugin("style.css"), // 输出的文件名
在dist打包生成的目录中, 就会分离出单独的.css文件
Chunk.entrypoints: Use Chunks.groupsIterable and filter by instanceof Entrypoint instead
“extract-text-webpack-plugin”: “^3.0.2” 此插件3.x版本对应webpack3.x, 所以我们需要更高的extract版本, 所以下载extract-text-webpack-plugin@next (@next下载下一个内测最新版)
需要安装less 和 less-loader 来解析less代码, 和加载less文件
npm install less@3
npm install less-loader@7
在webpack.config.js中 配置加载器, 解析.less文件
{
test: /\.less$/,
use: ['style-loader', 'css-loader', "less-loader"]
}
但是这样发现css代码没有分离出来, 所以还需要使用extract-text-webpack-plugin的配置, 分离出css代码
{
test: /\.less$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: ['css-loader', "less-loader"]
})
}
观察打包后style.css中多了less文件里的样式代码
是一个转换 CSS 代码的工具和插件 (postcss转换css代码, 为了兼容不同的浏览器)
类似于babel.js把浏览器不兼容的js转换成兼容的js代码 (babel转换js代码, 为了兼容不同浏览器)
注意它本身是一个工具, 和less/sass等预处理器不同, 它不能处理css代码
而是靠各种插件来支持css在不同浏览器和环境下正确运行的
先下载postcss-loader 和postcss到当前工程中
npm install postcss@7
npm install postcss-loader@3
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: [{
loader: 'css-loader',
options: {
importLoaders: 1 }
}, "postcss-loader"]
})
// importLoaders 用于配置「css-loader 作用于 @import 的资源之前」有多少个 loader。
},
在css和less文件中, 准备一些代码
自动补全前缀,
1.先下载此插件模块: npm i autoprefixer@9
2.postcss.config.js 配置如下:
module.exports = {
plugins: {
// postcss在翻译css代码的时候, 需要使用哪些插件功能
// 1. 写使用插件模块的名字, postcss会自己去require引入
// 2. 必须配置浏览器列表才可以 自动添加前缀
'autoprefixer': {
// 浏览器支持列表放到了package.json中browserslist中进行编写
}
}
}
package.json的browserslist下设置
"browserslist": [
"defaults",
"not ie < 11",
"last 2 versions",
"iOS 7",
"last 3 iOS versions"
]
// defaults相当于 "> 5%", 国内浏览器使用率大于5%的
// not ie < 11 不兼容IE11以下的浏览器 (支持ie11)
// 支持最后2个版本
// iOS苹果手机操作系统, 支持ios7
// 支持最后3个IOS的版本 ios13, 12, 11
浏览 && 画图, 解释rem 如何适配的
此插件是自动把(css/less…文件里 px转换成适配的rem单位), 无需再手动计算了
'postcss-pxtorem': {
rootValue: 16, // 这个值就是你看设计稿上基准字体是多少, 就写多少, 1rem=16px
unitPrecision: 6, // 小数点几位
propList: ['*'], // 指定需要转换rem的属性 例如: 'font-size' 'line-height' *代表所有属性
mediaQuery: false, // 媒体查询的px是否转换
minPixelValue: 0, // 小于指定数值不转换
// 默认px识别大写小, 所以不写成px不转换
}
注意: 只对css/less文件内代码有效, 因为webpack.config.js中, 加载器使用了postcss-loader
注意: 如果html中使用px转rem, 可以安装插件, 来自动把px转换成rem使用
注意: html的font-size不会自动随着网页的变化而变化
想要压缩打包后的css文件, 可以使用 optimize-css-assets-webpack-plugin, 先下载这个插件
在webpack.config.js中配置
const OptimizeCss = require('optimize-css-assets-webpack-plugin');
plugins: [ // 新增
new OptimizeCss()
]
url-loader
功能类似于 file-loader
,但是在文件大小(单位 byte)低于指定的限制时,可以返回一个 DataURL(base64字符串)注意: webpack认为, 图片也是一个模块, 所以才需要loader来解析图片, 所以图片需要import/require引入)
/*{
test: /\.(png|jpg|jpeg|gif|svg)$/,
use: [
{
loader: 'url-loader',
options: { // 参数
limit: 8192 // 8kb内的文件都转换成DataURL, 如果超出的都转换成base64字符串
}
}
]
},
*/
// 上面options可以简写成下面?传参数的格式
{
test: /\.(png|jpg|jpeg|gif|svg)$/, // 处理这些结尾的文件
use: [
{
// options传参的方式被我改成了?传参
// ? 代表给加载器传入配置参数
// limit字段: 对打包的图片限制字节大小为8KB以内, 超出此限制,会被file-loader产生一个文件
// [name]: 代表直接使用打包目标文件的名字,
// [ext]: 代表直接使用打包目标文件的扩展名
// name字段: 代表给打包后的文件定义名字(可以使用[]这种规则)
// 8KB
loader: 'url-loader?limit=8192&name=assetsDir/[name].[ext]'
}
]
}
// 如果处理字体图标需要引入这个
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: 'fonts/[name].[hash:7].[ext]'
}
}
总结: 小于limit限制的assets下的图片资源, 都会被打包进js文件中, 先被url-loader转换成了base64字符串 (这种加密方式的字符串, 可以被img标签的src所接受, 但是一定要在base64加密的字符串前, 声明一个表示 data:image/png;base64, 后面跟base64字符串)
注意: 非打包的资源, 不再被当做模块使用, 所以我们只能使用路径引入的方式, 不要使用import/require
注意: 引用的相对路径, 要以打包后dist目录中index.html为起点, 来查找static (而且static也会被复制到dist中)
new CopyWebpackPlugin([{
from : __dirname + "/src/static",//从哪个文件夹开始复制
to : __dirname + "/dist/static"//复制到那个文件夹
}])
静态资源存放位置 | 使用在哪里 | 资源大小 | 产出到哪里 | 备注 |
---|---|---|---|---|
assets (会被webpack处理) | img标签 | 超过8KB | dist/生成独立文件 | import 图片 |
assets (会被webpack处理) | img标签 | 小于8KB | 打包进了js中, base64字符串 | import 图片 |
assets (会被webpack处理) | css背景 | 超过8KB | url’图片路径’ | import 图片 |
assets (会被webpack处理) | css背景 | 小于8KB | url('base64字符串") | import 图片 |
assets (会被webpack处理) | i标签 | 判断10KB/8KB | 使用了字体和字体样式(超过限制生成独立文件, 小于限制大小, 打包进js中) | import css样式文件 |
static (不会被wp处理) | img标签 | 超过8KB | copy了src同级下static到dist目录中 | 写static的路径 |
static (不会被wp处理) | img标签 | 小于8KB | copy了src同级下static到dist目录中 | 写static的路径 |
static (不会被wp处理) | css背景 | 超过8KB | copy了src同级下static到dist目录中 | 写static路径 |
static (不会被wp处理) | css背景 | 小于8KB | copy了src同级下static到dist目录中 | 写static路径 |
static (不会被wp处理) | i标签 | 不用管 | copy了src同级下static到dist目录中 | 需要在模板index.html中引入css文件 |
总结:
在webpack打包的项目中, 使用jQuery功能
下载jquery模块, npm i jquery
在webpack中配置resolve, 模块解析方式
resolve: {
// 配置如何解析模块的引入
alias: {
// 别名
// import xxx from 'jquery' 实际上 运行时是 import xxx from 'jquery/dist/jquery.min.js'
jquery: 'jquery/dist/jquery.min.js',
}
}
在main.js中引入jquery, import $ from ‘jquery’, 创建标签插入到网页中, 在前端使用了npm下载的模块
// 1. npm init初始化的文件夹, 其实跟node_module里的第三方包模块文件夹 是一个意思
// 2. require/import引入此文件夹时, 实际上引入的是此文件夹的包入口模块(package.json下main属性指定的)
// 默认路径索引
// 模块内: 看package.json 里 main 指定的
// 普通文件夹: index.html
找插件/加载器, 下载插件/加载器模块, webpack.config.js中进行配置, 编码/准备资源, 打包, 把打包后的资源部署到服务器上
部署: 配置环境和需要的各种软件参数等, 上传代码资源包
https://www.webpackjs.com/plugins/
https://www.webpackjs.com/loaders/
webpack的作用是什么,谈谈你对它的理解?
答案: 现在的前端网页功能丰富,特别是SPA(single page web application 单页应用)技术流行后,JavaScript的复杂度增加和需要一大堆依赖包,还需要解决SCSS,Less……新增样式的扩展写法的编译工作。所以现代化的前端已经完全依赖于WebPack的辅助了。
现在最流行的三个前端框架,可以说和webpack已经紧密相连,框架官方都推出了和自身框架依赖的webpack构建工具。
· React.js+WebPack
· Vue.js+WebPack
· AngluarJS+WebPack
webpack的工作原理?
答案: WebPack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Sass,TypeScript等),并将其转换和打包为合适的格式供浏览器使用。在3.0出现后,Webpack还肩负起了优化项目的责任。
数据库是按照数据结构来组织、存储和管理数据的仓库。
数据库(database)是用来组织、存储和管理数据的仓库。
当今世界是一个充满着数据的互联网世界,充斥着大量的数据。数据的来源有很多,比如出行记录、消费记录、浏览的网页、发送的消息等等。除了文本类型的数据,图像、音乐、声音都是数据。
为了方便管理互联网世界中的数据,就有了数据库管理系统的概念(简称:数据库)。开发者可以对数据库中的数据进行新增、查询、更新、删除等操作。
常见数据库及种类
其中,MySQL、Oracle、SQL Server 属于传统型数据库(又叫做:关系型数据库 或 SQL 数据库),这三者的设计理念相同,用法比较类似。
数据的组织结构:指的就是数据以什么样的结构进行存储。
整理前
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w9kTdNI6-1616809019372)(C:\Users\Administrator\Desktop\MySQL录课课件\image\4-1整理前.png)]
整理后
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vSG9GJ5W-1616809019375)(C:\Users\Administrator\Desktop\MySQL录课课件\image\4-2整理后.png)]
传统型数据库的数据组织结构,与 Excel 中数据的组织结构比较类似。
因此,我们可以对比着 Excel 来了解和学习传统型数据库的数据组织结构。
每个 Excel 中,数据的组织结构分别为工作簿、工作表、数据行、列这 4 大部分组成
图示
学生表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lG7CRSZs-1616809019377)(C:\Users\Administrator\Desktop\MySQL录课课件\image\4-3excel学生表.png)]
老师表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zj2EFWMV-1616809019380)(C:\Users\Administrator\Desktop\MySQL录课课件\image\4-4excel老师表.png)]
excel说明
整个 Excel 叫做工作簿
student和teacher 是工作表
student工作表中有 3 行数据
每行数据由 7 列信息组成
每列信息都有对应的数据类型
数据库、表、行、字段的关系
在实际项目开发中,一般情况下,每个项目都对应独立的数据库。
不同的数据,要存储到数据库的不同表中,例如:学生信息存储到 student 表中,老师信息存储到 teacher 表中。
每个表中具体存储哪些信息,由字段来决定
例如:
我们可以为 student 表设计 id、username、age、sex、province、education、t_no 这 7 个字段;
我们可以为 teacher 表设计 id、username、sex、age 这 4 个字段;
表中的行,代表每一条具体的数据。
有些表之间会以某个字段产生关系,比如:学生表中的老师编号代表老师是谁
安装版本 : mysql-5.5.49-win32
安装过程 : 略
他是数据库图形化工具 因为我们是前端开发 所以说 我们没有必要使用DOC命令操作数据库
图形化可以直接使用工具进行创建数据库 创建数据表
安装版本 : @11.1.13.0
安装工具 : navicat111_mysql_cs_x64
安装过程 : 略
连接操作 主要是在主工具栏中选择连接 => 选择MySQL
连接的参数一共有五个
1. 连接的名称 项目的名称
2. 主机名 如果是本地 那么我们可以使用localhost或者是127.0.0.1
3. 端口号 MySQL的端口号默认是3306 当然如果安装的时候定义了 那么以安装的时候为准
4. 用户名 我们MySQL中安装好的时候 默认有一个用户 root 超级管理员
5. 密码 安装数据库的时候 设定的密码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hUBfWrBY-1616809019382)(C:\Users\Administrator\Desktop\MySQL录课课件\image\connect.png)]
连接成功之后 连接的项目名称是灰色的 如果使用 那么双击 变成彩色 可以操作该连接
创建数据库 我们使用 右击连接的项目名称 选择新建数据库
新建数据库涉及三个参数
1.数据库的名字 名字的命名是数字字母下划线 不要使用$
2.数据库的字符集 默认是utf8 如果设定 也要设定成utf8
3.排序规则 utf8_general_ci
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aerXtDMy-1616809019384)(C:\Users\Administrator\Desktop\MySQL录课课件\image\create_database.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qP8d5nK9-1616809019385)(C:\Users\Administrator\Desktop\MySQL录课课件\image\1.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-26AeIUpi-1616809019386)(C:\Users\Administrator\Desktop\MySQL录课课件\image\2.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UYITypE9-1616809019387)(C:\Users\Administrator\Desktop\MySQL录课课件\image\3.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hqU7sl9y-1616809019388)(C:\Users\Administrator\Desktop\MySQL录课课件\image\4.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kbkxF3Op-1616809019389)(C:\Users\Administrator\Desktop\MySQL录课课件\image\5.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HlShpihx-1616809019391)(C:\Users\Administrator\Desktop\MySQL录课课件\image\6.png)]
数据库的解构是库表解构 也就是说 我们最开始安装的是MySQL服务 而不是一个数据库 在我们MySQL服务中
我们可以创建很多的数据库 一般情况下 一个项目对应的是一个数据库
那么 我们每一个库中还有很多中数据 所以说 我们在每一个数据库中 都会设定很多的数据表
每一张表中存储一类数据 那么我们创建好数据库之后 我们就需要创建数据表
我们可以为 student 表设计 id、username、age、sex、province、education、t_no 这 7 个字段;
我们可以为 teacher 表设计 id、username、sex、age 这 4 个字段;
创建一个学生表
1.在导航窗格中单击右键 选择新建表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iqpRIe99-1616809019392)(C:\Users\Administrator\Desktop\MySQL录课课件\image\create_table.png)]
2.点击对象工具栏中的保存 设定表名 保存成功就是创建表完毕
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Oiyfjf2a-1616809019393)(C:\Users\Administrator\Desktop\MySQL录课课件\image\table.png)]
数据类型
tinyint : 小整形 一个字节 也就是说从数字-128—127
int : 整形 专门写整数 四个字节 -2147483648—2147483647
char : 定长字符串 默认存储255个字节 可以自定义 但是不能超过255
varchar : 变长字符串 默认存储255个字节 可以自定义 但是不能超过255
text : 文本类型 可存储16KB的数据内容
char和varchar的区别
如果说设定好了字符串长度 存储数据的时候 数据的长度可能没有设定的长度长
如果是varchar 他会将我们数据类型的长度进行解析 将我们空白字节去掉 占用的空间就是我们数据长度
但是如果使用char的话 那么我们占用的空间就是设定的字节长度
也就是说 varchar要比char节约内存
但是char有一个优点 char的速度要比varchar快一些
字段属性
主键 : primary key 数据以该字段为主 同时避免出现重复数据
非空 : not null 不允许字段出现null
自增长 : auto_increment 自增长 保证数据不重复
管理数据 其实就是我们使用图形化界面对数据表进行添加 修改 删除数据操作
打开我们的数据表 在我们数据表的下方 有一个小加号 点击这个加好 证明开始添加数据
点击之后 就可以在我们表中直接手动键入数据
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zPVNCBPP-1616809019394)(C:\Users\Administrator\Desktop\MySQL录课课件\image\insert.png)]
在键入数据之后 点击一下对号 保存数据
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eSi5l0PG-1616809019395)(C:\Users\Administrator\Desktop\MySQL录课课件\image\insert_table.png)]
修改数据 直接在数据上点击 获取鼠标焦点 然后修改
但是注意 修改是不可逆的 也就是说 修改之后无法还原 慎重修改
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CPCTqwaF-1616809019396)(C:\Users\Administrator\Desktop\MySQL录课课件\image\update.png)]
删除操作 让要删除的数据获取鼠标光标 然后点击减号 直接删除
但是注意 删除是不可逆的 也就是说 删除之后无法还原 慎重删除
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RPf8HuCv-1616809019398)(C:\Users\Administrator\Desktop\MySQL录课课件\image\delete.png)]
删除之后 得到数据
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gBIZbL7X-1616809019400)(C:\Users\Administrator\Desktop\MySQL录课课件\image\delete_table.png)]
备份数据 其实就是作为数据保留 为什么要备份数据 避免误删数据 避免黑客攻击 避免数据丢失或者被盗
备份数据分为一下几步
1.在导航窗格中 选择我们要备份的数据库 单击右键
2.选择转储SQL文件 选择结构和数据
3.保存到响应的地址中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oJcuLPBX-1616809019401)(C:\Users\Administrator\Desktop\MySQL录课课件\image\copy.png)]
还原数据 就是将已备份的数据 重新放到数据库中
1.首先 创建一个空的数据库 准备装取我们还原的数据
2.选择这个数据库 单击右键进行运行数据库 选择运行SQL文件
3.在文件的选项中 上传我们要恢复的数据库
4.点击开始 只要碰到successfully 就恢复成功
SQL(英文全称:Structured Query Language)是结构化查询语言,专门用来访问和处理数据库的编程语言
能够让我们以编程的形式,操作数据库里面的数据。
1.SQL 是一门数据库编程语言
2.使用 SQL 语言编写出来的代码,叫做 SQL 语句
3.SQL 语言只能在关系型数据库中使用(例如 MySQL、Oracle、SQL Server)
4.SQL语言在window中不区分大小写,官方建议关键字使用大写,自己定义的变量用小写
1.从数据库中查询数据
2.向数据库中插入新的数据
3.更新数据库中的数据
4.从数据库删除数据
5.可以创建新数据库
6.可在数据库中创建新表
特殊说明:开发中,重点进行数据的CRUD操作(增、删、改、查)
操作语句主要是增、删、改 只要执行操作语句成功 那么就会返回影响行数
只要执行操作语句成功 那么数据库中的数据就会产生变化 所以说叫做影响行数
插入语句说那个关键字 insert into
语句 : insert into 表名(字段1,字段2……) values(值1,值2)
数据库插入语句 不需要按照表中的字段顺序进行填写字段 但是值和字段必须一一对应
也不要求将所有字段进行数据插入
insert into student(username,age,province,sex,education,t_no) values('庞广达',32,'湖北',1,'本科',10001);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O07GVv5t-1616809019402)(C:\Users\Administrator\Desktop\MySQL录课课件\image\insert_status.png)]
插入执行结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tegsiQm5-1616809019403)(C:\Users\Administrator\Desktop\MySQL录课课件\image\insert_result.png)]
注意 我们在使用插入语句的时候 其实是可以省略value前面的字段名称的 但是需要加上一个要求
我们如果说省略了字段名 那么我们对表中的数据进行添加的时候 必须添加所有字段 并且按照顺序进行添加
语句 : insert into 表名 values(值1,值2)
insert into student values(null,'蝴蝶',23,0,'湖南','硕士',10001);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BqtTwn0t-1616809019404)(C:\Users\Administrator\Desktop\MySQL录课课件\image\insert_result2.png)]
修改就是直接修改掉数据库中已存在的数据 使用关键字 update
语句 : update 表名 set 字段1= 值1,字段2 = 值2…… where 条件
where后面跟上的是 修改的条件 也就是修改的某一条数据
一般情况下我们以ID(主键)作为添加 因为不会重复 是唯一的
update student set username = '毛毛虫',age = 16 where id = 20210304
执行返回状态
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EemafAhn-1616809019405)(C:\Users\Administrator\Desktop\MySQL录课课件\image\update_status.png)]
执行结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7HBQjYO8-1616809019407)(C:\Users\Administrator\Desktop\MySQL录课课件\image\update_result.png)]
删除语句 使用的相对来说比较少
语句 : delete from 表名 where 条件
delete from student where id = 20210304
执行返回状态
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B9E2Z9Pi-1616809019408)(C:\Users\Administrator\Desktop\MySQL录课课件\image\delete_status.png)]
执行结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nkEK2HIU-1616809019409)(C:\Users\Administrator\Desktop\MySQL录课课件\image\delete_result.png)]
在数据库操作中 最重要的就是查询语句 在一个项目中 查询语句能占有整体SQL语句中的60%以上
所以说 我们最多的就是使用查询语句
语句 : select * from 表名 || select 字段1,字段2…… from 表名
-- 使用select最简单的查询语句
select * from student
查找的结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gc9u7dAQ-1616809019411)(C:\Users\Administrator\Desktop\MySQL录课课件\image\select1.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wqXvIiml-1616809019413)(C:\Users\Administrator\Desktop\MySQL录课课件\image\select2.png)]
以上查询是将整个数据表中所有的内容全部查询完毕,但是这样效果并不是特别的好
因为很多时候 我们查询语句的时候 都是查询部分数据 或者是查询出一条数据
我们多用条件查询 条件和修改删除语句一样 都是使用where进行连接条件
where子句中有很多中查询语句 我们主要是运算符查询 但是我们要查询的内容比较多
语句 : select * from 表名 where 条件
算数运算符一般情况下就是 + - * / %
-- where子句查询
-- 算数运算符查询
select * from student where age = 15 + 3;
select * from student where age = 20 - 3;
select * from student where age = 3 * 7;
select * from student where age = 40 / 2;
select * from student where sex % 2 = 0;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mlbOfQrO-1616809019414)(C:\Users\Administrator\Desktop\MySQL录课课件\image\select3.png)]
比较运算符一般是 > >= < <= != <> =
-- 比较运算符
select * from student where age > 20;
select * from student where age >= 20;
select * from student where age < 20;
select * from student where age <= 20;
select * from student where age != 20;
select * from student where age <> 20;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5HkL9gA4-1616809019416)(C:\Users\Administrator\Desktop\MySQL录课课件\image\select4.png)]
逻辑运算符就是 逻辑与and 逻辑或or
-- 逻辑运算符查询
select * from student where age > 20 and sex = 0;
select * from student where age < 20 or sex = 0;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VHVA5d3p-1616809019417)(C:\Users\Administrator\Desktop\MySQL录课课件\image\select5.png)]
聚合查询其实就是一种运算 他能运算出一个字段中所有数据的总量 平均值 最大值 最小值 总和等
其实就是在计算一个字段中的数据量 这个计算一般情况下 我们都会选择整形的字段
总量(count) 最大值(max) 最小值(min) 平均值(avg) 总和(sum)
语句 : select 聚合运算类型(字段名) from 表名
-- 总量
select count(id) from student;
select count(username) from student;
select count(*) from student;
-- 最大值
select max(age) from student;
-- 最小值
select min(age) from student;
-- 平均值
select avg(age) from student;
-- 总和
select sum(age) from student;
分组查询其实就是将数据进行分组
分组查询关键字是group by 但是注意 分组查询中不能出现where语句
语句 : select 字段 from 表名 group by 分组的字段
-- 分组查询
select sex,count(id) from student group by sex;
select t_no,count(id) from student group by t_no;
安班次分组的结果是
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iZ331bMO-1616809019419)(C:\Users\Administrator\Desktop\MySQL录课课件\image\select6.png)]
其实排序查询应用最为广泛 因为我们很多的数据几乎都是逆序排序查找出来的
朋友圈中的数据 QQ邮箱的邮件 新闻网站 这种查询几乎都是逆序查找出来的
注意 排序查询和分组查询一样 都是不能使用where的查询方式 排序的字段一般情况下都是整型
排序查询的关键字 order by
排序方式有两种 asc 顺序(默认) desc 逆序
语句 : select * from 表名 order by 排序的字段 desc || asc
-- 排序查询
select * from student order by age desc
select * from student order by age asc
查询的结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qSnPtSon-1616809019419)(C:\Users\Administrator\Desktop\MySQL录课课件\image\select7.png)]
受限查询一般用于分叶管理 我们使用的时候 很多时候都会和排序 或者是分组嵌套使用
受限查询使用关键字 : limit
后面跟上两个参数 第一个参数x 受限制的起始位置 第二个参数是y 数据的步长 查找出来的数据量
注意 : 受限查询也不实用where
语句 : select * from 表名 limit x,y
-- 受限查询
select * from student limit 0,3;
select * from student limit 3,3;
select * from student order by age desc limit 0,3
查询的结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0bWmWvKg-1616809019423)(C:\Users\Administrator\Desktop\MySQL录课课件\image\select8.png)]
区间查询其实就是查询出一段区间中的数据 这个查询其实使用比较运算符也能完成
使用大于等于和小于等于 只不过区间查询更简单一些
区间查询关键字 : between
语句 : select * from 表名 where 字段 between x and y
-- 区间查询
select * from student where age >= 20 and age <= 26;
select * from student where age between 20 and 26;
查询的结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2j7x9esO-1616809019424)(C:\Users\Administrator\Desktop\MySQL录课课件\image\select9.png)]
什么是多表查询 就是两个或者两个以上的多个表进行一起查询
我们想要完成多表查询 那么必须保证多个表之间有所关联
怎么关联两张表 一个表中的主键和另一个表中的非主键字段保持一致
多表查询关键字 : join on left join right join
使用join连接两个表
使用as给表取一个别名
语句 : select a.字段,b.字段 from a表名 join b表名 on 条件
-- 多表查询
-- 这种查询方式和之前不同 因为多表查询 所以说必须添加条件
select t.username,s.username,s.age,s.sex from teacher as t join student as s
-- 在join多表查询中 添加条件不是使用where 使用的on join和on进行连用
select t.username,s.username,s.age,s.sex from teacher as t join student as s on t.id = s.t_no
查询的结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K34NxIqF-1616809019426)(C:\Users\Administrator\Desktop\MySQL录课课件\image\select10.png)]
左,右联合查询
左联合查询就是在查询的时候以左表为主 提高查询速度 如果说左表中存在数据二右表中没有与之匹配的数据
那么 右表使用null进行补齐 左表中所有的数据全部显示出来
右联合同理
什么是左表 卸载join左边的就是左表
什么是右表 卸载join右边的就是右表
语句 : select a.字段,b.字段 from a表名 left || reght join b表名 on 条件
-- 左联合查询
select t.username,s.username,s.age,s.sex from teacher as t left join student as s on t.id = s.t_no
-- 右联合查询
select t.username,s.username,s.age,s.sex from teacher as t right join student as s on t.id = s.t_no
-- 确定左右表
select t.username,s.username,s.age,s.sex from student as s right join teacher as t on t.id = s.t_no
查询的结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vd9mLnTP-1616809019427)(C:\Users\Administrator\Desktop\MySQL录课课件\image\select11.png)]
只有开发者要通过Node(Express)开发相应功能并且结合MySQL以后,普通用户才能操作数据库
也就是说只能通过Node来操作MySQL,进而是开发者提供了哪些功能那么用户就能使用此功能
普通用户是没有权限直接访问数据库的。
例如:我们上网常常碰见到登录注册
注册:把注册表单中的数据传递到服务器,然后Express把数据添加到数据库中
登录:把登录表单中的数据传递到服务器,然后Express通过条件,判断数据库中寻找有无此用户且密码是否匹配
1、安装第三方mysql模块 npm i mysql
2、连接到Mysql数据库
3、执行SQL语句操作数据库
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kq7jsv5C-1616809019429)(C:\Users\Administrator\Desktop\MySQL录课课件\image\node_mysql.png)]
操作数据库 使用MySQL模块 涉及到的连接方法
createConnection : 创建连接
该方法是连接数据库使用 内置一个参数 参数是对象描述 对象中有五个属性
1.host 主机 : 127.0.0.1 || localhost
2.port 端口 : 3306
3.user 用户名 : root
4.password 密码 : 123
5.database 连接的数据库名 : web33
方法的返回值是一个连接资源
// 引入模块
const mysql = require('mysql');
// 连接数据库
/*
createConnection : 创建连接
该方法是连接数据库使用 内置一个参数 参数是对象描述 对象中有五个属性
1.host 主机 : 127.0.0.1 || localhost
2.port 端口 : 3306
3.user 用户名 : root
4.password 密码 : 123
5.database 连接的数据库名 : web33
方法的返回值是一个连接资源
*/
let db = mysql.createConnection({
host : '127.0.0.1',
port : 3306,
user : 'root',
password : '123',
database : 'web33'
})
console.log(db);
操作数据库基于我们已经连接完成的数据库 操作数据库我们主要有对数据库的添加 修改 删除 查询等操作
query : 操作数据库的方法
该方法内置两个或者是三个参数
第一个参数是SQL语句 在SQL语句中 我们可以使用?进行占位
第二个参数是数据的填充 如果说只有一个数据 那么可以直接写 如果多个 那么第二个参数是数组 这个参数可选
第三个参数是回调函数
回调内置一个或者两个参数
第一个参数是错误
第二个参数是返回的数据 如果说是操作语句 那么没有这个参数 如果说是查询语句 第二个参数是查询出的数据
const mysql = require('mysql');
let db = mysql.createConnection({
host: '127.0.0.1',
port: 3306,
user: 'root',
password: '123',
database: 'web33'
})
// 操作数据库 使用的是连接资源中的query方法
/*
query : 操作数据库的方法
该方法内置两个或者是三个参数
第一个参数是SQL语句 在SQL语句中 我们可以使用?进行占位
第二个参数是数据的填充 如果说只有一个数据 那么可以直接写 如果多个 那么第二个参数是数组 这个参数可选
第三个参数是回调函数
回调内置一个或者两个参数
第一个参数是错误
第二个参数是返回的数据 如果说是操作语句 那么没有这个参数 如果说是查询语句 第二个参数是查询出的数据
*/
// 两个参数的情况下
// db.query("insert into student values(null,'小陈',23,0,'黑龙江','本科',10002)",error=>{
// if (error){
// console.log('添加失败');
// }else{
// console.log('添加成功');
// }
// })
// 三个参数的情况下 使用占位符
let username = '小欣';
let age = 19;
let sex = 0;
let address = '内蒙古';
let school = '专科';
let tno = 10001;
// 使用三个参数 占位的形式操作数据库
// 占位的填充按照顺序
db.query("insert into student values(null,?,?,?,?,?,?)",[username,age,sex,address,school,tno],error=>{
if (error){
console.log(error.sqlMessage);
console.log('添加失败');
}else{
console.log('添加成功');
}
})
const mysql = require('mysql');
let db = mysql.createConnection({
host: '127.0.0.1',
port: 3306,
user: 'root',
password: '123',
database: 'web33'
})
// 模拟前端接受的数据
let username = '小明';
let age = 12;
let sex = 1;
let address = '山东';
let school = '小学';
let tno = 10002;
let id = 20210312;
// 修改操作
db.query("UPDATE student SET username=?,age=?,sex=?,province=?,education=?,t_no=? where id=?",[username,age,sex,address,school,tno,id],error=>{
if (error){
console.log(error.sqlMessage);
console.log('修改失败');
}else{
console.log('修改成功');
}
})
const mysql = require('mysql');
let db = mysql.createConnection({
host: '127.0.0.1',
port: 3306,
user: 'root',
password: '123',
database: 'web33'
})
// 接受一个删除的id
let del = 20210312;
// 删除操作
db.query("DELETE FROM student WHERE id = ?",del,error=>{
if (error){
console.log(error.sqlMessage);
console.log('删除失败');
}else{
console.log('删除成功');
}
})
查询操作和之前的那两拨有点不同 因为他要返回数据结果 所以说在回调中设定了第二个参数
参数就是查询返回的数据结果 但是如果说查询失败 那么返回一个空数组
const mysql = require('mysql');
let db = mysql.createConnection({
host: '127.0.0.1',
port: 3306,
user: 'root',
password: '123',
database: 'web33'
})
// 写什么样的查询语句 返回的数据就是什么样的
// db.query("SELECT * FROM teacher",(error,data)=>{
// if (error){
// console.log(error.sqlMessage);
// }else{
// console.log(data);
// }
// })
// db.query("SELECT * FROM teacher WHERE sex = 1",(error,data)=>{
// if (error){
// console.log(error.sqlMessage);
// }else{
// console.log(data);
// }
// })
db.query("SELECT * FROM teacher WHERE sex = ?",1,(error,data)=>{
if (error){
console.log(error.sqlMessage);
}else{
console.log(data);
}
})
我们发现 我们每次操作数据库的时候 使用的方式几乎都是相同的 都是先进行连接 然后再进行操作
每一次操作数据库的方法也都是固定的 使用query方法
所以说 我们每次这样使用 造成代码冗余
我们可以封装一个操作数据库的模块 封装好之后 再使用数据库的时候 直接调用这个模块
db.js
const mysql = require('mysql');
const fs = require('fs');
const path = require('path');
// 读取JSON 使用配置文件
let dbConfig = JSON.parse(fs.readFileSync(path.resolve(__dirname,'./dbConfig.json'),'utf-8'));
// 连接数据库操作
let link = mysql.createConnection(dbConfig);
// 检测连接是否成功
link.connect(err=>{
if (err) console.log('连接失败');
})
// 操作数据库
// 使用promise操作数据库 这样 能够保证取出操作完数据之后 再给与响应
// 我们定义一个query方法 方法中需要传递参数 我们定义两个参数
// 第一个参数是SQL语句
// 第二个参数是占位的数组
let query = (sql,params=[])=>{
return new Promise((resolve,reject)=>{
link.query(sql,params,(error,result)=>{
if (error){
reject([error,null]);
}else{
resolve([null,result]);
}
})
})
}
module.exports = query;
db.config.json
{
"host" : "127.0.0.1",
"port" : 3306,
"user" : "root",
"password" : "123",
"database" : "web33"
}
使用
const db = require('./db');
// 定义一个SQL语句
let sql = "select * from teacher";
// 定义一个async函数 直接获取promise状态
async function test(){
let [error,data] = await db(sql);
if (error){
console.log('执行失败');
}else{
console.log(data);
}
}
test();