day01 - JS高级语法(一)
01 - 全局变量_局部变量
- 定义:
- 定义在function外部的变量:全局变量
- 定义在function内部的变量:局部变量
- 定义在function内部,但没有var的变量也是,适合公用的变量
- 使用场景
- 全局:很少,一直常驻内存中不易销毁,容易出现命名冲突,适合公用的变量
- 局部:函数执行完毕
02 - 作用域链
var point = 30;
function play() {
//就近原则:从内部开始寻找,如果找不到再去外层作用域查找
var point2 = 20;
alert(point);
}
play();//30
- 作用域
- 函数作用域:函数内部使用范围
- 全局作用域:整个网页范围
03 - 提前声明
function fn(a) {
console.log(a);//function(){} 优先级高
var a = 2;
function a() { }
console.log(a);//2
}
fn(1);
// 优先显示function a() { }
04 - 经典面试题
for(var i=1;i<=3;i++){
//定时器异步执行,等外部代码全部执行完毕再执行自己
setTimeout(function(){
alert(i);
},0)
}
for(let i=1;i<=3;i++){
//let 在每次循环的时候,创建了一个变量副本, 并且保留下来
setTimeout(function(){
alert(i);
},0)
}
// 输出,4 4 4 1 2 3
05 - let
- 特点:
- 块级作用域,就是大括号
- 暂时性死区【不能在初始化之前访问它】
- 当全局变量使用的时候,它不属于window对象了
//let:新增了块级作用域,就是大括号
function play(){
let a=10;
if(1==1){
let a=20;
{
let a = 30;
console.log(a);
}
console.log(a);
}
console.log(a);
}
function run(){
//不能在初始化之前去访问let修饰的变量:暂时性死区
console.log(x); //Cannot access 'x' before initialization
let x=66;
}
var global_name = 'frank'
let global_age = 19
//用let定义的全局变量,不属于window,用var定义的全局变量是属于window的
console.log(window.global_name);
console.log(window.global_age); // 拿不到
run(); // 报错
play(); // 30,20,10
06 - const
- const【常量】
- 命名:要求全部大写,多个单词之间用下划线隔开
- 特点:
- 定义的时候就得初始化它的值,且后面不能修改
- 当作局部变量的时候,效果和let一样
- 如果用const定义一个对象,那么时可以修改对象内部属性的
//常量:定义的时候就得初始化它的值,且后面不能再修改
const IP_ADDRESS = '172.168.2.1';
// IP_ADDRESS="127.0.0.1"; 错误的
//如果要将const当成局部变量去使用,则特点和let一样的
if(1==1){
const ONE=1;
}
console.log(ONE); //报错:ONE is not defined
07 - 定时器面试题(定时器是异步执行的他比同步执行的慢)
console.log(3)
setTimeout(function(){
console.log(1)
},0)
console.log(4)
setTimeout(function(){
console.log(2);
},0)
console.log(5)
//3,4,5,1,2
08 - const问题
//如果用const定义一个对象,那么是可以修改对象内部属性的
const OBJ={name:'jackie',age:30};
OBJ={};
OBJ.name='rose';
09 - 匿名函数
10 - IIFE
-
IIFE(立即执行函数表达式)
-
语法:
(function([形参列表]){ //函数体(立即执行的JS代码) [return] })([实参列表])
-
作用:
-
早期模块化解决方案,避免了命名冲突
-
防止外部代码来访问我内部的一些变量,提高了安全性
注意:连续写多个IIFE会报错,请使用分号隔开
-
11 - IIFE
12 - 闭包
- 定义:
- 函数嵌套函数,内部函数可以访问外部函数的局部变量,闭包环境是内部函数和外部函数沟通的桥梁。
- 好处:
- 内部变量既有了全局变量的常驻内存不销毁的特点,并且不会和外部名称重名
- 坏处:
- 用多了之后,增加了内存消耗,容易造成内存泄漏
- 原理:
- 局部变量一直被内部函数所使用,所以不会被垃圾回收器收走
var fn = (function(){
var num=10;
return function(){
num++;
console.log(num);
}
})()
//闭包:函数嵌套函数,内部函数可以访问外部函数的局部变量,闭包环境是内部函数和外部函数沟通的桥梁
//在外部执行fn,不论执行多少次,num变量一直存在,从未还原过
//好处:num既有了全局变量的常驻内存不销毁的特点,并且num又不会和外部名称重名
//坏处:用多了之后,增加了内存消耗,容易造成内存泄漏
//num为什么不会被num垃圾回收器收走:
//这个变量一直被内部函数(fn)所使用,所以num不会被垃圾回收器收走
fn();//11
fn();//12
fn();//13
//如何让GC回收掉num呢?
//将fn设置为null,没有地方用num了,自然就被GC收走了
fn=null;
//没有任何地方使用的变量,会被垃圾回收器收走(释放内存空间)
var s="jackie";
13 - 闭包应用
//点击LI,弹出LI的下标
let lis = document.querySelectorAll('li')
for(var i=0;i<lis.length;i++){
(function(s){
lis[s].onclick=function(){
alert(s);
}
})(i)
}
14 - 闭包面试题
var name = "The Window";
let object = {
name: "My Object",
getNameFunc: function () {
console.log(this);
var that = this;//that就是object
return function () {
console.log(this);//此时this已经变化为window了,所以不要用this
return that.name;//object.name
};
//独立的创建一个函数,不论写在哪,都属于window
function play(){
console.log("play 里面的this是谁呢?",this);//window
}
var run =function(){
console.log("run 里面的this是谁呢?",this);//window
}
run();
play();
}
};
object.getNameFunc()();
15 - this
var obj = {
start:function(){
console.log(this);
}
}
obj.start();//obj
var fn = obj.start;
fn();//windows
-
this指向问题
-
this指向window
-
function 函数名(){} var 函数名=function(){}
-
-
this指向当前这个对象
-
var obj = { 函数名:function(){} }
-
-
this指向当前这个btn按钮
-
btn.onclick=function(){}
-
-
16 - arguments
- arguments
- 含义:当前函数的实参列表(只能在函数里面使用)
- arguments.callee
- 含义:当前函数(只能在函数里面使用)
17 - call_apply
- 相同点:都是调用函数,并且动态修改函数里面的this值
- 不同点:
- 函数名:call(新this值,参数1,参数2,参数N)
- 函数名:apply(新this值,[参数1,参数2,参数N])
- 场景:
//slice截取的时候,找的是 this
let args = [7,8,9].slice();//还是7,8,9 从原数组截取的
let args = [7,8,9].slice.call([1,2,3]);//1,2,3,因为修改了this指向,所以从后面这个数组截取的
18 - 函数柯里化
- 含义:柯里化是一种使用多个参数的一个函数,转换成一系列使用一个参数的函数的技术
- 意义:通过拆分参数的方式达到减少代码冗余,增加可读性的目的
function sum(){
//调用slice方法,但是把slice里面的this改成arguments,然后产生一个新数组
let args = [].slice.call(arguments);//1,3
function exec(){
args.push(...arguments);// args = [1,2,3,4]
return exec;
}
exec.calc=function(){
return args.reduce(function(total,current){
return total+current;
})
}
return exec;
}
function sum(){
//调用slice方法,但是把slice里面的this改成arguments,然后产生一个新数组
let args = [].slice.call(arguments);//1,3
function exec(){
args.push(...arguments);// args = [1,2,3,4]
return exec;
}
exec.calc=function(){
return args.reduce(function(total,current){
return total+current;
})
}
return exec;
}
// console.log(sum(1,3)(2,4)(100)(1));
var exec1 = sum(1,3);
var exec2 = exec1(2,4)
var exec3 = exec2(100)
var exec4 = exec3(1);
console.log(exec4.calc());
19 - 函数柯里化简化
function a(){
var arr=[];
var play = function(num){
arr.push(num);
console.log(arr);
return play;
}
return play;
}
var play = a()
var play2 = play(1);
var play3 = play2(2);
var play4 = play2(3);
day02 - JS高级语法(二)
01 - 柯里化
function sum(){
//1.第一层函数只用来收集参数的,参数10给的是外层函数
//将伪数组转换为普通数组
//调用普通数组的slice方法,然后截取的是arguments的值,最终返回一个新数组
var args = [].slice.call(arguments);//args=[10,1]
return function(){
//2.参数20给的是内层函数,并且将10和20合并起来
args.push(...arguments);//args=[10,1,20,30,40,50]
var total=0;
for(var i=0;i<args.length;i++){
total+=args[i]
}
return total;
}
}
console.log(sum(10,1)(20,30,40,50));
02 - 柯里化
//普通函数写法
function validate(reg,str){
return reg.test(str);
}
console.log(validate(/^\d{1,3}$/,"v88"));
//柯里化函数写法(一个函数只接收一个参数)
function currying(reg){
return function(str){
return reg.test(str);
}
}
var validPhone = currying(/^\d{11}$/)
var validEmail = currying(/^d2d2d@d$/)
validPhone("123123123")
validPhone("23533")
validEmail("12323")
03 - eval
var str = "1*2+3";
document.write(eval(str));
// 太过强大,不建议使用
04 - 递归
-
执行上下文【代码的执行坏境】
- 全局执行上下文
- 函数执行上下文
- eval执行上下文,[很少使用,了解即可]
-
执行上下文调用栈【代码的执行流程】【先进后出】
- 全局上下文入栈
- 函数1上下文入栈
- 函数2上下文入栈
- 函数N上下文入栈
- 函数N上下文出栈
- 函数2上下文出栈
- 函数1上下文出栈
- 全局上下文出栈
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7iKqDl4y-1634128183206)(images/20210907111232.png)]
var i=1;
function play(){
i++;
if(i==1000){
return;
}
console.log(i,Math.random())
play();
}
play();
-
递归
- 含义:函数内部调用自身的过程
- 特点:
1. 函数调用函数,执行传递的动作; 2. 确定最后一次的终止条件,执行回归的动作
05 - 递归求阶乘
function ji(n) {
if(n == 1) return 1; //确定边界,回归
var j = ji(n - 1); //传递
return n * j;
}
ji(10)
-
刨析图
06 - 递归_快速排序
let arr=[4,7,3,20,11,2];
function quickSort(list){
if(list.length==0){
return list;
}
//1.找基准值,就是第一个值
let basic = list[0];
let left_arr=[];
let right_arr=[];
if(list.length==1){
return basic;
}
//2.遍历数组,从基准值的下一个开始
for(let i=1;i<list.length;i++){
if(basic<list[i]){
//3.放右边
right_arr.push(list[i])
}
if(basic>list[i]){
//4.放左边
left_arr.push(list[i])
}
}
return [].concat(quickSort(left_arr),basic,quickSort(right_arr));
}
console.log(quickSort(arr));
-
递归面试题二【快速排序】
-
分析思路:
-
在数据集之中,选择一个元素作为”基准”
-
所有小于”基准”的元素,都移到”基准”的左边;所有大于”基准”的元素,都移到”基准”的右边。
-
对”基准”左边和右边的两个子集,不断重复第一步和第二步,直到所有子集只剩下一个元素为止。
案例模拟:
4,7,3,20,11,2
基准:[3,2],4,[7,20,11]
[3,2]
基准:2,[3]
[3]
[7,20,11]
基准:7,[20,11]
[20,11]
基准:11,20
代码实现:
-
07 - 变量类型
var a = 123;
var b = { name: 'jack' }
function show(x, y) {
x = 456;
y = { name: 'rose' } //更改为新对象的内存地址
y.name = 'rose' //修改原对象内部属性
}
show(a, b)
console.log(a, b)
就是存储的值,还是地址的问题
08 - 浅拷贝
- 对象浅拷贝
- 概念:复制出来的对象做修改时对原对象造成了影响
- 写法:
- 引用赋值:var 对象B=对象A;
- 扩展运算符:var 对象B={…对象A}
- Assign :Object.assign(目标对象,源对象);
- 案例;
//浅拷贝:复制出来的对象做修改时对原对象造成了影响
var a ={
name:'jack',age:30,work:['写代码','开滴滴','跑摩的']
}
var b = a; //复制了引用的地址而已【浅拷贝】
b.name='rose';
console.log(a.name);//rose
// 虽然c是新创建的一个对象,c不等于a,
//但是:展开符只能展开一层,但是a里面还有一个对象,就是work数组
var c = {...a};
c.name='peter'
//c和a是不同的两个对象,但是c和a他们俩共用同一个work
c.work[0]='项目经理'
console.log(a.work);//['项目经理','开滴滴','跑摩的']
var x ={};
var y={code:'XA003',list:[1,2,3]}
//将y的全部内容复制到x里【浅拷贝】
var result = Object.assign(x,y);
console.log(x);
x.list.push(4,5,6);
console.log(y);
09 - 深拷贝
-
对象深拷贝
-
概念:复制出来的对象和源对象无任何关系,不会再互相影响,在堆内存中独立存储
-
写法:
-
方式一:
JSON.stringify(源对象)、JSON.parse(源对象JSON字符串)
缺点:undefined的值、函数无法拷贝 -
方式二:
递归深拷贝function deepCopy(target,source){ for(let i in source){ if(typeof source[i]=='object'){ let p=null; if(source[i].length){ //数组 p=[]; }else{ //对象 p={}; } target[i] = deepCopy(p,source[i]) }else{ target[i]=source[i]; } } return target; }
-
-
day03 - JS高级语法(三)
01 - 二分查找法
-
前提:有序数组
-
思路:
-
计算出中间值,然后和要查找的数字比较是在中间值的左侧还是右侧
-
缩短查找范围,再重新计算中间值,继续判断
-
直到要查找的数字就是中间值为止
代码:
//1.循环数组,逐个比较 //2.数组.indexOf(300) //3.二分查找法(折半查找法):前提是有序数组 //寻找数字5的下标是多少? var arr =[1,2,3,4,5,6,7,8,9,10,11,12,13,14]; function binarySearch(arr,num){ //1.找出中间值 var start = 0; var stop = arr.length-1; var middle = parseInt((stop+start)/2) //2.比较中间值和要查找的数字 while(arr[middle]!=num && stop>start){ if(arr[middle]<num){ //说明我要找的值在右侧 start = middle+1; }else{ //说明我要找的值在左侧 stop = middle-1; } //3.每次循环要重新计算中间值 middle = parseInt((stop+start)/2) } return arr[middle]!=num?-1:middle; } console.log(binarySearch(arr,5));
-
02 - 防抖
- 函数防抖(debounce)
- 思路:
- 在第一次触发事件时,不立即执行函数,而是给出一个期限值比如200ms
- 如果在200ms内没有再次触发滚动事件,那么就执行函数
- 如果在200ms内再次触发滚动事件,那么当前的计时取消,重新开始计时
- 效果:
- 如果短时间内大量触发同一事件,只会执行一次函数
- 实现:
- 借助定时器完成
代码:
//滚动时,调用的其实是debounce里面返回的匿名函数
window.onscroll=debounce();
function debounce(){
var timer=null;
//每次滚动的时候,先清除上一次还未执行的定时器,然后再启动新的定时器
//高频次的滚动,不断的清除定时器,减少了函数的执行次数
return function(){
clearTimeout(timer);
timer = setTimeout(function(){
var top = document.documentElement.scrollTop || document.body.scrollTop;
console.log(top);
},100)
}
}
03 - 节流
- 函数节流(throttle)
- 问题:
- 希望即使用户不断拖动滚动条,也能在某个时间间隔之后给出反馈
- 思路:
- 连续触发事件但是在 n 秒中只执行一次函数。节流会稀释函数的执行频率,类似控制阀门一样定期开放的函数,也就是让函数执行一次后,在某个时间段内暂时失效,过了这段时间后再重新激活
代码:
//滚动时,调用的其实是throttle里面返回的匿名函数
window.onscroll=throttle(500);
function throttle(delay){
var start=0;//初始化第一次执行的时间点
return function(){
//每次滚动时调用内部函数
var now = Date.now();
//用当前时间 - 上次执行的时间
var sub = now - start;
//计算看是否超过了500毫秒
if(sub>delay){
//如果超过了500毫秒,去执行一次代码
var top = document.documentElement.scrollTop || document.body.scrollTop;
console.log(top);
//执行完业务代码之后,将当前时间作为下一次执行的开始时间
start=now;
}
}
}
04 - 严格模式
- 注意事项:
- “use strict” 指令只允许出现在脚本或函数的开头。
- 严格模式下不能使用未声明的变量,不论是局部还是全局。
- 在函数内,禁止this关键字指向全局对象
- 在函数内,不能使用arguments.callee
优点: - 消除代码运行的一些不安全之处,保证代码运行的安全;
- 提高编译器效率,增加运行速度;
- 为未来新版本的Javascript做好铺垫。
代码:
//如果写在全局,则全局都是严格模式,如果写在函数内,则函数内代码是严格模式
//一定要写在第一行
"use strict";
//x="123"; 必须使用变量修饰符,否则报错
function play(){
console.log(this);//this不再指向全局对象了(window)
}
play();
var obj = {
sub(){
//不能使用arguments.callee
console.log(arguments.callee);
}
}
obj.sub();
05 - 模板字符串
-
ES6【ES2015】
- ECMAScript是一种由Ecma国际(前身为欧洲计算机制造商协会,英文名称是European Computer Manufacturers Association)通过ECMA-262标准化的脚本程序设计语言。
- JavaScript是对这个标准的实现和扩展
-
解决2个问题:
-
变量和字符串连接的问题,不需要再用加号拼接
-
多行字符串不需要再用多组+拼接
案例:
var name="科比" //模板字符串 //解决2个问题: //1.变量和字符串连接的问题,不需要再用加号拼接 //2.多行字符串不需要再用多组+拼接 var str = `${name}是NBA最好的得分手之一,突破、投篮、罚球、三分球他都驾轻就熟 几乎没有进攻盲区,单场比赛81分的个人纪录 就有力地证明了这一点。除了疯狂的得分外, ${name}的组织能力也很出众,经常担任球队进攻的第一发起人。 另外${name}还是联盟中最好的防守人之一,贴身防守非常具有压迫性。` document.write(str);
-
06 - 扩展运算符
-
【…】
-
函数中应用:
-
形参arr的前面写三个点,就代表这个数组的长度是不一定的,你传递的实参数组个数多少都无所谓
function changeParam(…arr){ console.log(arr); } changeParam(“a”,”b”,”c”); changeParam(123,456); changeParam([“a”]);
-
如果还想传别的参数,要记得把可变参数放最后
function changeParam(num,...arr) { console.log(arr+"<br>"); } changeParam(2,["a", "b", "c"]); changeParam(3,[123, 456]); changeParam(4,["a"]);
-
-
对象中应用:
-
展开对象的最外层结构
let z = { a: 3, b: 4 }; let n = { ...z };
-
-
数组中应用:
//数组转序列 console.log(...[1, 2, 3]); //1 2 3 console.log(1, ...[2, 3, 4], 5); //1 2 3 4 5 //伪数组转化为数组 var oDiv = document.getElementsByTagName("div"); console.log([...oDiv]); //[div, div, div] //数组复制 var arr2=[…arr1] //求最大值 Math.max(...array)
07 - 默认参数
//旧的默认参数写法
function sum(x,y){
x = x || 0;
y=y || 0;
return x*y;
}
console.log(sum());
//ES6的默认参数写法
//如果传递了参数,则使用传过来的参数;如果没传参数就用默认值 0
function multiple(x=0,y=0){
return x*y;
}
console.log(multiple());
console.log(multiple(1,3));
//如果传递了参数,则使用传过来的参数;如果没传参数就用默认值
function show(name = "some people", sex = "man", age = "18") {
//你的骚操作
}
show();
show("jackie","woman",33)
08 - 解构赋值
//字符串:"how are you doing?" 将每个单词放在一个单独的变量里
var str = "how are you doing?" ;
[one,two,three,four] = str.split(" ")
console.log(one);
console.log(two);
console.log(three);
console.log(four);
//可以在对应位置留空来跳过被解构数组中的某些元素
[,,u,] = str.split(" ")
console.log(u);
//结合“扩展运算符”来捕获数组中的所有尾随元素
[x,...y] = str.split(" ")
console.log(x);//how
//y这个变量是否是Array类型的实例
console.log(y);//Array
//typeof只能检测出来:string,number,boolean,function,undefined,object
//instanceof
console.log(y instanceof Date);//false
console.log(new Date() instanceof Date);//true
//面试题:不利用第三个变量交换a和b的值
var a = 1;
var b = 3;
a=a+b;//a=4,b=3
b=a-b;//a=4,b=1
a=a-b;//a=3,b=1
console.log(a,b);
var a = "Cat";
var b = "Dog";
[b,a]=[a,b]
console.log(a,b);
//对象的结构
var animal={
type:'猫科动物',
color:'黄色',
age:3,
name:'小黄'
}
let {type,color} = animal;
console.log(type);
console.log(color);
//对象当参数的结构
function hunt({name}){
console.log(name);
}
hunt(animal);
09 - Set
-
含义:一种无序、值必须唯一的数据集合
-
创建Set集合:可接收数组或类似数组的对象、字符串作为参数
var myset=new Set([可选参数]);//1.创建Set集合 const myset = new Set(); //2.给集合添加值 myset.add(true) myset.add("Frank") myset.add("Rose") myset.add(123) myset.add({}) //3.判断某个值是否存在 console.log(myset.has("Frank")) //4.删除 myset.delete("Frank") console.log(myset.has("Frank")) //5.keys()和values()返回所有的数据集合 console.log(myset.keys()); console.log(myset.values()); //6.清空 // myset.clear(); // console.log(myset) //遍历 myset.forEach(item=>{ console.log(item); }) //新增加的for-of遍历语法:这里的i不是下标,而是值 for(let i of myset){ console.log(i); } for(let i of [55,66,77,88,99]){ console.log(i); } //面试题:数组去重 var list = ["jack","rose","Frank","Peter","Tim","rose","Frank"]; var myset2 = new Set(list) //Array.from()可以将伪数组,Set 转换为普通数组 console.log(Array.from(myset2)) //iterable 可以被遍历的对象
10 - Map
-
含义:采用key-value的键值对形式存储数据,key不能重复
-
创建Map集合:
-
var maps=new Map();
//1.创建map实例 var m = new Map(); //2.给map添加数据 m.set("name","jackie"); m.set(100,10000); m.set(false,true); //3.根据key获取value console.log(m.get(100)); //4.删除 m.delete(false) //5.根据key来判断value是否存在 console.log(m.has(false)); //6.返回map里所有的key console.log(m.keys()); //7.返回map里所有的value console.log(m.values()); //8.返回map里所有key-value数据对 console.log(m.entries()); //9.遍历 m.forEach(function(value,key){ console.log(key,value); }) //10.遍历 for(let i of m){ console.log(i) } //清空 m.clear();
-
扩展:数组、Set、Map 对比
Array | 有序集合 | 下标唯一,值可重复,下标是有序的 |
---|---|---|
Set | 无序集合 | 没有下标,值是唯一 |
Map | 无序集合 | 键值对存储键唯一,值可重复 |
day04 - 面向对象(一)
面向对象编程(OOP)
-
意义:使用对象时,只关注对象提供的功能,不关注其内部细节
-
创建:
//字面量方式创建对象: var obj = { 'sex':'女', //1.添加属性 描述 //2.添加方法 行为 buy:function () { console.log('买搓衣板'); } }
//实例创建对象:(代码冗余) var obj = new Object(); //1.添加属性 obj.name = '小华'; //2.添加方法 obj.skill = function () { console.log('敲代码'); }
//工厂模式创建对象:(缺点:结构不明确) function createObject(name) { var obj = new Object(); //1.创建对象 obj.name = name; //2.添加属性 obj.skill = function () { //3.添加方法 console.log('敲代码'); } return obj; //4.返回创建好的对像 }
//构造函数创建对象:(缺点:浪费内存) //函数名首字母大写(类名),使用this添加属性和方法,调用的时候前面必须加new function Student(name,sex,age) { this.name = name; this.study = function () { console.log("good"); } //在构造函数中不用this,用普通的函数创建和变量创建是私有的,在外部无法直接访问 //私有成员【需要更高的安全性】 var lan=""; function intro(){} } var stu = new Student(属性序列); stu.属性 stu.函数();
原型(prototype)
-
含义:每个函数都有一个 prototype(原型)属性,这个属性是一个对象,它的用途是包含可以由特定类型的所有实例共享的属性和方法。
-
语法:
//创建一个Student构造函数 function Student() {} //1.通过原型添加属性和方法(共享) Student.prototype.name = "小红"; Student.prototype.study = function () { console.log("good good study,day day up!!!!"); }
构造函数名.prototype 和 实例对象.proto 是完全相等的
function Student(){}; var oldW = new Student(); oldW.__proto__ === Student.prototype // true
原型属性与函数的使用
Phone.prototype={ call(){}, message(){}, internet(){} } //获取原型上面的属性和方法时,不需要加 __proto__,直接写名字即可 var p = new Phone('iPhone12','gysz12b110@xx','white') p.call(); //当我们想修改原型上的内容时,才需要通过__proto__ p.__proto__.call=function(){}
Object.create()
//创建一个没有原型的纯粹的对象
var obj = Object.create(null);
//创建一个带有原型【{run:function(){}}】的对象
var obj2 = Object.create({run:function(){}})
-
构造函数方式和原型方式对比
prototype原型的好处:
- 给构造函数上挂载一个原型prototype,目的是为了减少某些函数去重复创建
- 原型上的内容创建一次就行,以后不论创建出来多少个对象,大家都共用
- 但是构造函数里面的内容,每new一次,就要创建一次,造成性能浪费。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3n7o8I6V-1634128183214)(images/20210909162829-16311862689372.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VPE0PYgh-1634128183215)(images/20210909162842-16311862822904.png)]
补充:
01 - 工厂模式
//工厂函数(在函数内部创建对象,且return出来给外部使用)
//好处:外部无需关注工厂函数里面具体是怎么实现的【隐藏细节】
function factory(name,age){
var obj = new Object();
obj.myName=name;
obj.myAge=age;
return obj;
}
console.log(factory("frank",20))
console.log(factory("rose",10))
02 - 构造函数
//构造函数->类
function Animal(type,name,age,sex){
this.myType=type;
this.myName=name;
this.myAge=age;
this.mySex=sex;
var mySize="2米";
//私有的方法,虽然写在Animal里面,但是它是属于window的,本质上和Animal没有关系
function introduce(){
console.log("我身长"+mySize);
}
this.eat=function(food){
console.log(this.myName+"吃"+food)
}
this.sleep=function(){
introduce();
console.log("睡觉")
}
}
//创建一个Animal类的实例
var tiger = new Animal("爬行","壁虎",1,"母")
//在构造函数中只要是用this点出来的属性和方法都是 public的【公开】
tiger.eat("蚊子");
// tiger.sleep=function(){
// console.log("我不睡觉")
// }
//在外部又创建了一个公开的属性,也叫mySize
tiger.mySize="3米";
console.log(tiger.mySize)
tiger.sleep();
//如何设计一些私有的private的属性和方法呢?
//在构造函数中不要用this,就用普通的函数创建和变量创建就是私有的了
//手机
//品牌:brand
//型号:type
//屏幕:4.7
//打电话、发短信、拍照、上网、GPS
03 - Prototype
//1.每个对象都有__proto__属性
function Person(name){
this.name=name;
}
var p = new Person("frank");
var arr = new Array();
console.log(arr.__proto__);
console.log(p)
console.log(p.__proto__);
04 - Prototype
function Student(name){
this.name=name;
}
//prototype原型的好处:
//给构造函数上挂载一个原型prototype,目的是为了减少某些函数去重复创建
//原型上的内容创建一次就行
//以后不论创建出来多少个对象,大家都共用
//prototype的作用:函数与属性共享的
Student.prototype.study=function(){
console.log("学习")
//这个方法是共享函数(所有Student的实例共享)
}
//构造函数名.prototype
Student.prototype.school="蜗牛学院"
var smallW = new Student("小王");
var oldW = new Student("老王")
oldW.study();
//实例对象.__proto__
oldW.__proto__.school='家里蹲'
console.log(smallW)
console.log(oldW)
//构造函数名.prototype 和 实例对象.__proto__ 是完全相等的
console.log(oldW.__proto__ === Student.prototype);
05 - Prototype
function Student(name) {
this.name = name;
}
//给Student设置一个原型,就是{},{}的原型是Object
Student.prototype = {
study:function(){},
love:function(){}
}
console.log(new Student("X子文"))
//对象默认的原型是 Object,Object是根
console.log({name:'rose'});
//创建一个没有原型的纯粹的对象
var obj = Object.create(null);
console.log(obj);
//创建一个带有原型【{run:function(){}}】的对象
var obj2 = Object.create({run:function(){}})
console.log(obj2);
05 - Prototype
//手机
//品牌:brand
//型号:type
//屏幕:4.7
function Phone(type,iCloud,color){
this.type=type;
this.iCloud=iCloud;
this.color=color;
}
Phone.prototype={
call(){
console.log(this.type)
},
message(){},
internet(){}
}
//打电话、发短信、拍照、上网、GPS
var p = new Phone('iPhone12','gysz12b110@xx','white')
//获取原型上面的属性和方法时,不需要加 __proto__,直接写名字即可
p.call();
//当我们想修改原型上的内容时,才需要通过__proto__
p.__proto__.call=function(){
console.log("重置。。。。")
}
day05 - 面向对象(二)
01 - 作业鉴赏
//程序员,老师,学生
function Coder(name,age){
this.name=name;
this.age=age;
}
Coder.prototype.code=function(){
console.log("coding.....")
}
function Teacher(name,age,subject){
this.name=name;
this.age=age;
this.subject=subject;
this.tech=function(){
console.log("授课")
}
}
Teacher.prototype = new Coder();
function Student(name,age,major,sex,tel){
this.name=name;
this.age=age;
this.major=major;
this.sex=sex;
this.tel=tel;
this.study=function(){
console.log("学习")
}
}
Student.prototype=new Coder();
var s = new Student("小王",20,'Web','男',1110);
console.log(s);
s.study();
s.code();
02 - prototype优先
-
原型模式的执行流程: (就近原则)
- 先查找构造函数实例里的属性或方法,如果有,立刻返回;
- 如果构造函数实例里没有,则去它的原型对象里找,如果有就返回
function Fn() { this.a = '这是私有属性a'; this.b = function() { alert('这是私有方法b'); } } Fn.prototype.a = '这是公共属性a'; Fn.prototype.b = function() { alert('这是公共方法b'); } var f = new Fn(); alert(f.a); f.b();
03 - hasOwnProperty
-
如何判断属性是在构造函数的实例里,还是在原型里
- box.hasOwnProperty(‘name’)
- 实例里有返回 true,否则返回 false
<ul> <li>111</li> <li>22</li> <li>33</li> <li>44</li> </ul> <script> var obj = { name:'jack', age:30 } var arr = ["A","B","C",'D'] for(var i in obj){ console.log(i,obj[i],obj.hasOwnProperty(i)) } for(let i in arr){ console.log(i,arr[i]) } var liList = document.getElementsByTagName("li"); console.log(liList) for(let i in liList){ console.log(i,liList.hasOwnProperty(i)); } function Person(name,age){ this.name=name; this.age=age; } Person.prototype.sex="女" var p = new Person("rose",20); console.log(p.hasOwnProperty("name"))//true表示name是Person构造函数里定义的 console.log(p.hasOwnProperty("age"))//true表示age是Person构造函数里定义的 console.log(p.hasOwnProperty("sex"))//false表示sex是原型上面定义的 </script>
04 - constructor
-
在默认情况下,所有的原型对象都会自动获得一个 constructor(构造函数)属性,这个属性(是一个指针)指向 prototype 属性所在的函数(Person)
Person.prototype.constructor === Person //true function Person(){} // Person.prototype.name="jack" Person.prototype = { constructor:Person, name:"Nicholas", age:29, job:"Software Engineer", sayName:function(){} } var p1 = new Person(); var p2 = new Person(); console.log(p1) console.log(p1.constructor);//对象.constructor 【对象所在的类(构造函数)】 console.log(Person.prototype.constructor);//构造函数名.prototype.constructor 【构造函数】
附加;prototype与constructor的关系、
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hBGfJiIW-1634128183216)(images/20210910110205-16314192388592.png)]
05 - 原型链继承
-
含义:原型与原型之间进行继承,形成了一个链条,称为原型链
-
语法:子元素.prototype = 父元素实例
Student.prototype = new Person(); SmallStudent.prototype = new Student(); //类:人、学生、小学生、一年级学生、一年级一班学生 function Person(){ this.name="人" } function Student(school){ this.school=school } function SmallStudent(name,age,school){ this.age = age; } //原型与原型之间进行继承,形成了一个链条,称为原型链 Student.prototype = new Person(); SmallStudent.prototype = new Student("学生--"); //给原型上添加constructor,形成双向链条 Student.prototype.constructor = Student; SmallStudent.prototype.constructor = SmallStudent; var s = new SmallStudent("小明",2,"蜗牛") console.log(s); console.log(s.role); console.log(s.name);
06 - 对象冒充继承
-
对象冒充的原理:调用父类函数时改变this指向,定义子类的属性和方法
-
在子类型中:父类型.call(this,参数1,参数2…);
-
可以解决传参的问题,但是不能继承父类型原型中的属性和方法
function Person(name){ this.name=name; } function Student(name,school){ Person.call(this,name) this.school=school } function SmallStudent(name,age,school){ //创建子类之前,先调用父类,将父类里的this改成SmallStudent,给SmallStudent添加学校 Student.call(this,name,school); this.age = age; } //好处:1.提取了多个类的共性做成一个父类,把共性的属性和方法都放父类里面 //2.子类里面只设计属于自己的属性 function HighStudent(name,height,school){ Student.call(this,name,school); this.height=height; } var s = new SmallStudent("小明",3,"蜗牛") var h = new HighStudent("小红",180,'蜗牛') console.log(s.school,s.name,s.age) console.log(h.school,h.name,h.height)
07 - 混合继承
-
混合继承 对象冒充+原型链继承
-
对象冒充:继承父类型构造函数中的属性和方法
-
原型链继承:继承父类型原型对象中的属性和方法
//混合继承: //对象冒充:构造函数的动态参数 //原型链:把父类的方法放进原型中 function Person(name,age){ this.name=name; this.age=age; this.eat=function(){ console.log("Person.吃饭") } } function Student(name,age,_class){ Person.call(this,name,age); this._class=_class; } Student.prototype = new Person; Student.prototype.constructor = Student; function SmallStudent(name,age,_class){ Student.call(this,name,age,_class) this.play=function(){ console.log("玩耍") } } SmallStudent.prototype = new Student; SmallStudent.prototype.constructor = SmallStudent; var stu = new Student("班长大人",40,"3班"); console.log(stu); stu.eat(); var stu2 = new SmallStudent("小明",10,"3班"); stu2.eat(); stu2.play(); console.log(stu2)
08 - ES6中的类
-
动物类案例:
class Animal{ //通过new运算符在创建对象时才会调用的构造函数 constructor(name,age){ this.name=name; this.age=age; } hunter(){ console.log(捕猎); } //get和set访问器中,访问属性时要加下划线,防止出现无限递归问题 get name(){ return this._name; } set name(value){ this._name=value; } } new Animal("x",2).name;//当获取name值的时候,实际上是调用了 get name() new Animal("x",2).name="j"//当给name赋值的时候,实际上调用了set name()
-
案例:
//语法糖 class Phone{ constructor(brand,price,contact){ this.brand=brand; this.price=price; this.contact=contact; } //属性访问器:set、get set contact(newContact){ console.log("set contact",newContact) if(newContact.length>0){ this._contact = newContact; } } get contact(){ this._contact = this._contact.map(item=>{ //substr(从哪里开始截取,截取几个) substring(从哪开始截取,截取的结束位置-1) return "**" + item.tel.substring(2) }) return this._contact; } } var p = new Phone("MI",2999,[{name:"小明",tel:"12312"},{name:"小红",tel:"678"}]); p.contact=[]; console.log(p.contact); p.contact=[{name:"小王",tel:"998282"},{name:"小绿",tel:"736338"}]; console.log(p.contact);
10 - ES6中的继承
//手机(父类)
class Phone{
constructor(brand,price,contact){
this.brand=brand;
this.price=price;
this.contact=contact;
}
}
//智能手机(子类)
class SmartPhone extends Phone{
constructor(brand,price,contact,OS){
//调用父类构造函数
super(brand,price,contact)
this.OS = OS;
}
}
var p = new SmartPhone("MI",2999,[{name:"小明",tel:"12312"},{name:"小红",tel:"678"}],'Android');
console.log(p);
day06 - HTTP协议
HTTP协议
- 什么是协议?
- 用于计算机网络中数据传输的一个规则和标准
- TCP/IP 协议 4 层结构
- 链路层->网络层->传输层->应用层
- 当你输入URL地址之后发生了什么?※
- 地址跳转
- 查找缓存
- 域名解析(DNS)解析成IP地址
- 建立TCP链接【包含三次握手】
- 发起请求(request)
- 接收响应(response)
- 四次挥手,断开连接
- 加载DOM树
- 加载CSS树
- DOM结构和CSS合并渲染
- TCP协议
- 特点:提供一种面向连接的、可靠的字节流服务
- TCP协议三次握手※
![image-20210913122020100](images/20210913122020.png)
- TCP协议四次挥手※
![image-20210913122031201](images/20210913122031.png)
- HTTP协议(超文本传输协议)
- 特点:※
- 简单快速、可以传输任意类型
- 无连接
- 无状态
- 特点:※
- 请求过程:※
- 建立连接
- 发送请求(request)
- 接收响应结果(response)
- 关闭连接
- HTTP协议常用的几种请求方式※
- GET:明文传输,不安全,传输的数据量小【加载资源文件,查询数据】
- POST:以文件形式传输,传输的数据不限制【新增功能】
- HEAD:头部信息【配置信息】
- PUT:【修改功能】
- DELETE:【删除功能】
- OPTIONS:查看服务器支持的请求类型是什么
- HTTP请求头格式 [HEAD]※
- 请求行:【传输类型 URL地址 HTTP版本号】
- 请求头:【key:value】
- 空行
- 请求正文:【参数】
- HTTP响应头格式 [HEAD]※
- 响应行:【HTTP版本号 状态码】
- 响应头:【key:value】
- 空行
- 响应正文:【结果】
状态码与缓存
-
HTTP状态码【服务器给客户端的】※
1xx:请求中
2xx:成功范畴
3xx:重定向
4xx:客户端出错
5xx:服务端报错100:请求
200:OK
301:永久重定向
302:临时重定向
304:当前资源来自于缓存
400:客户端语法错误
401 :客户端没权限
403:服务的拒绝客户端的请求
404:资源没找到
500:服务的代码报错
503:服务端繁忙 -
关于HTTP的缓存※
-
流程:
- 读取本地缓存->200
- 强制缓存【Expires,Cache-Control】->200
- 协商缓存【ETag,Last-Modified】-> 304
- 读取最新数据->200
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a75Bc2Ie-1634128183217)(images/Snipaste_2021-09-14_09-42-33.png)]
-
-
HTTP的端口号
http协议默认端口号:80端口
https协议默认端口号:443端口
http端口号范围:1-65536之间,1-1024被系统占用
推荐端口号:8080,8081,8082,8083
HTTPS
-
传输方式:
数据内容首先以对称加密方式(公钥)加密,传输过程再由非对称加密方式(私钥)加密的双重加密方式 -
对称加密:
发送方和接收方都用的同一个密钥 加密解密都是同一个密钥 从始至终只需要保存一个密钥就行
-
非对称加密:
发送方和接收方使用一对密钥,即公钥和私钥。一般私钥是保密不能被泄露的,公钥可以对外传播。
了解WebSocket
- 基于TCP连接的一个长连接协议
通常:- 聊天室
- 股票
- 网络游戏
- 系统通知
补充:
01 - Super
-
super关键字
-
super关键词指向当前对象的原型对象
-
super()指代父类的构造方法
-
super.函数名()可以调用父类函数
案例:
class Student extends Person{ constructor(name,age,major){ //调用父类的构造函数,并传参,且一定要写在第一行 super(name,age); this.major=major; } //子类重写(覆盖)了父类函数:父类函数内容太广泛太笼统,不具体,并不能完成体现子类的特点 work(){ //用super.xxx 可以获取父类的成员 super.work(); console.log("学生的工作就是学习") } }
-
02 - Static
-
static关键字
被static修饰的方法与属性:
-
不需要new对象,它在class创建的时候就一并产生
-
在创建class的时候它也就随之创建一次
-
用类名调用,而不是对象调用
class Person{ constructor(name,age){ this.name=name; this.age=age; } } class Student extends Person{ constructor(name,age,major){ super(name,age); this.major=major; } static role='kid' static getMyName(){ console.log('getMyName : ' + this.name);//返回当前类名:Student } } var s = new Student("jack",30,'Web'); //1.被static修饰的成员,通过类名调用的,不是对象调用 Student.getMyName(); //2.被static修饰的成员归属于类的,不归属于对象 //3.被static修饰的成员是该类创建时也一起创建的 //平时用过的静态成员(大多是工具函数) console.log(Date.now()); console.log(Math.PI);
-
03 - 私有属性
-
ES6中定义私有成员
- 私有成员:在名称前面加 # ,访问范围仅限于class大括号以内
-
私有属性
-
必须先在constructor之前定义一下;普通属性不用预先定义
class Animal{ a; #b; constructor(a,b){ this.a=a; this.#b=b; } run(){ console.log("this.#b : " + this.#b) } #stop(){ console.log("stop") } }
-
04 - TryCatch
-
异常处理/异常捕获
//语法错误 // war s = "123"; //逻辑错误:调试找到错误 //难以预料:try(试试)-catch(捕获) try { //1.将有可能出错的代码放在try里面 console.log('试一试') alertn(123); } catch (error) { //2.如果真的出错了,就会进入catch里面 console.log('出错啦 :',error) } //3.用了try-catch之后,即使报错也不会影响后续的代码执行 console.log("执行完毕")
//主动抛出异常(主动报错) function play(){ throw "当前函数不可用" }
day07 - 异步编程(一)
同步与异步※
- 同步:阻塞式的,一定要等前一段代码结果出来之后才能继续执行后面的
- 异步:非阻塞式,什么时候执行完了,会通过事件触发来告诉我们(事件是其中一种方式)
- FileReader、setTimeout、setInterval
【阿贾克斯】
-
含义:指一种创建交互式网页应用的网页开发技术
-
作用:可以在不重新加载整个网页的情况下,对网页的某部分进行更新
代码:/** * url 访问地址 * method 请求方式:GET、POST * data 传递给后端的数据(POST才用) * callback 回调函数(将异步的结果放进去) */ function ajax(url,method,data,callback){ //1.创建XMLHttpRequest对象:xhr let xhr = new XMLHttpRequest(); //2.打开通道 xhr.open(method,url); if(method.toUpperCase()=='POST'){ xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded'); } //3.发送请求 xhr.send(data); //4.通过事件来获取响应结果 xhr.onreadystatechange=function(){ //5.readyState=4说明整个请求已经完成了 if(this.readyState==4){ //6.获取HTTP状态码,如果是200则成功 if(this.status==200){ let res = JSON.parse(this.response) if(res.errcode){ alert(res.errmsg) return; } //将结果传给回调函数 callback(res); }else{ //7.如果状态码不是200,我们就打印状态文本,例如:404 文本就是 Not Found console.log(this.statusText) } } } }
Content-Type:
- 纯文本 text/plain
- JSON application/json
- form表单 application/x-www-form-urlencoded
- 文件上传 multiple-form
回调地狱
-
多层回调函数嵌套,使得程序变得难以维护,可读性差
setTimeout(function() { console.log('Yay!'); setTimeout(function() { console.log('Yahoo!'); setTimeout(function(){ //回调地狱 },1000) }, 1000) }, 1000);
Promise(承诺)
-
ES6推出了Promise语法结构来解决回调地狱问题。
-
这也就表达了将来会执行的操作,代表异步操作。
-
三种状态:
pending(进行中)、fulfilled(已成功)和rejected(已失败)
-
状态改变的过程:
pending->fulfilled
pending->rejected
一旦变成了成功或失败状态,状态就不会再变化。 -
创建Promise实例:
const promise = new Promise((resolve, reject) => { // do something here ... if (success) { resolve(value); // fulfilled } else { reject(error); // rejected } }); //通过then方法,分别指定resolved状态和rejected状态的回调函数 promise.then(function(value) { // success }, function(error) { // failure });
-
用Promise改造Ajax:
/** * url 访问地址 * method 请求方式:GET、POST * data 传递给后端的数据(POST才用) */ function ajax(url,method,data){ return new Promise((resolve,reject)=>{ //1.创建XMLHttpRequest对象:xhr let xhr = new XMLHttpRequest(); //2.打开通道 xhr.open(method,url); if(method.toUpperCase()=='POST'){ xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded'); } //3.发送请求 xhr.send(data); //4.通过事件来获取响应结果 xhr.onreadystatechange=function(){ //5.readyState=4说明整个请求已经完成了 if(this.readyState==4){ //6.获取HTTP状态码,如果是200则成功 if(this.status==200){ let res = JSON.parse(this.response) if(res.errcode){ alert(res.errmsg) return; } //将结果传给回调函数 resolve(res); }else{ //7.如果状态码不是200,我们就打印状态文本,例如:404 文本就是 Not Found reject(this.statusText) } } } }) } let url = 'http://yapi.smart-xwork.cn/mock/91220/user/list' ajax(url,"GET",null).then(result=>{ if(result instanceof Array){ //遍历结果 result.forEach(item=>{ //创建LI标签 let li = document.createElement("li"); li.innerHTML = item.name +":"+ item.sex; //将LI追加到UL中 document.getElementById("parent").appendChild(li); }) } },error=>{ console.log("失败:" +error); })
Promise中的异常捕获:
-
方式一:
ajax(‘GET’,’api/seller.do’,’username=jackie’).then((data)=>{ console.log(data);//成功获得结果 },(err)=>{ console.log(err);//获取错误信息 }
-
方式二:
ajax(‘GET’,’api/seller.do’,’username=jackie’).then((data)=>{ console.log(data);//成功获得结果 }).catch(err=>{ console.log(err);//获取错误信息 })
补充:
01 - 作业
02 - 原生Ajax
/**
* url 访问地址
* method 请求方式:GET、POST
* data 传递给后端的数据(POST才用)
* callback 回调函数(将异步的结果放进去)
*/
function ajax(url,method,data,callback){
//1.创建XMLHttpRequest对象:xhr
let xhr = new XMLHttpRequest();
//2.打开通道
xhr.open(method,url);
if(method.toUpperCase()=='POST'){
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
}
//3.发送请求
xhr.send(data);
//4.通过事件来获取响应结果
xhr.onreadystatechange=function(){
//5.readyState=4说明整个请求已经完成了
if(this.readyState==4){
//6.获取HTTP状态码,如果是200则成功
if(this.status==200){
let res = JSON.parse(this.response)
if(res.errcode){
alert(res.errmsg)
return;
}
//将结果传给回调函数
callback(res);
}else{
//7.如果状态码不是200,我们就打印状态文本,例如:404 文本就是 Not Found
console.log(this.statusText)
}
}
}
}
// let url = 'http://yapi.smart-xwork.cn/mock/91220/user/list'
// ajax(url,"GET",null,function(result){
// if(result instanceof Array){
// //遍历结果
// result.forEach(item=>{
// //创建LI标签
// let li = document.createElement("li");
// li.innerHTML = item.name +":"+ item.sex;
// //将LI追加到UL中
// document.getElementById("parent").appendChild(li);
// })
// }
// });
let url = 'http://yapi.smart-xwork.cn/mock/91220/user/add';
//Content-Type:
//纯文本 text/plain
//JSON application/json
//form表单 application/x-www-form-urlencoded
//文件上传 multiple-form
let params = new URLSearchParams();
params.append("name","Jerry")
params.append("age","2")
params.append("sex","男")
params.append("phone","110120119114")
ajax(url,"POST",params,function(res){
console.log(res);
})
ajax('./data.json',"GET",null,function(res){
console.log(res);
})
03 - Promise
var promise = new Promise(function(resolve,reject){
//ajax请求
console.log('1.发起ajax请求');
var random = Math.random();
console.log("2.产生随机数:" +random)
if(random>0.5){
//走成功那条路
resolve("success")
}else{
//走失败那条路
reject("fail")
}
})
promise.then(function(res){
//成功的处理
console.log(res);
},function(err){
console.log(err);
//失败的处理
})
04 - Promise
//要求等待1秒后弹出”Yay”,然后再等1秒弹出”Yahoo”,再等1秒弹出“God”
new Promise((resolve,reject)=>{
setTimeout(function(){
console.log("Yay")
resolve("Yahoo")
},1000)
}).then(function(res){
setTimeout(function(){
console.log(res)
},1000)
}).then(function(){
setTimeout(function(){
console.log("God")
},1000)
})
05 - Promise
//要求等待1秒后弹出”Yay”,然后再等1秒弹出”Yahoo”,再等1秒弹出“God”
function p(str){
return new Promise(resolve=>{
setTimeout(function(){
resolve(str)
},1000)
})
}
var promise = p("Yay")
var promise2= promise.then(function(res){
console.log(res);
return p("Yahoo") //再次调用p方法,实际上是重新new了一个Promise对象,赋值给promise2
})
var promise3= promise2.then(function(res){
console.log(res);
return p("God") //再次调用p方法,实际上是重新new了一个Promise对象,赋值给promise3
})
promise3.then(res=>{
console.log(res);
})
06 - Promise_Ajax
/**
* url 访问地址
* method 请求方式:GET、POST
* data 传递给后端的数据(POST才用)
*/
function ajax(url,method,data){
return new Promise((resolve,reject)=>{
//1.创建XMLHttpRequest对象:xhr
let xhr = new XMLHttpRequest();
//2.打开通道
xhr.open(method,url);
if(method.toUpperCase()=='POST'){
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
}
//3.发送请求
xhr.send(data);
//4.通过事件来获取响应结果
xhr.onreadystatechange=function(){
//5.readyState=4说明整个请求已经完成了
if(this.readyState==4){
//6.获取HTTP状态码,如果是200则成功
if(this.status==200){
let res = JSON.parse(this.response)
if(res.errcode){
alert(res.errmsg)
return;
}
//将结果传给回调函数
resolve(res);
}else{
//7.如果状态码不是200,我们就打印状态文本,例如:404 文本就是 Not Found
reject(this.statusText)
}
}
}
})
}
let url = 'http://yapi.smart-xwork.cn/mock/91220/user/list'
ajax(url,"GET",null).then(result=>{
if(result instanceof Array){
//遍历结果
result.forEach(item=>{
//创建LI标签
let li = document.createElement("li");
li.innerHTML = item.name +":"+ item.sex;
//将LI追加到UL中
document.getElementById("parent").appendChild(li);
})
}
},error=>{
console.log("失败:" +error);
})
07 - 回调函数
//传统做法:回调函数来得到异步结果
function a(num,str,bol,callback){
callback();
}
a(12,"hello",true,function(){
console.log("66666666")
});
//Promise的出现就是为了干掉回调函数这种传统写法
function a(num,str,bol){
return new Promise(resolve=>{
resolve("66666666")
})
}
a().then(res=>{
console.log(res)
})
08 - 错误捕获
var promise = new Promise(function(resolve,reject){
//ajax请求
console.log('1.发起ajax请求');
var random = Math.random();
console.log("2.产生随机数:" +random)
if(random>0.5){
//走成功那条路
resolve("success")
}else{
//走失败那条路
reject("fail")
}
})
// promise.then(成功的回调,失败的回调)
// promise.then(function(res){
// //成功的处理
// console.log(res);
// },function(err){
// console.log(err);
// //失败的处理
// })
//promise.then(成功回调).catch(失败的回调)
promise.then(res=>{
console.log(res)
}).catch(err=>{
console.log(err);
})
day08 - 异步编程(二)
Promise.all
-
Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
var p1 = new Promise(function (resolve,reject) {}); var p2 = new Promise(function (resolve,reject) {}); var p3 = new Promise(function (resolve,reject) {}); var p = Promise.all([p1,p2,p3]);
-
p的状态,由p1,p2,p3决定,全部实例对象为resolve,p的状态才会resolve,所有对象的返回值组成一个数组,传递给p的回调函数。任何一个对象为rejected,p的状态就变成rejected
Promise.allSettled
-
同时启动多个Promise实例,返回多个结果[允许有的成功,有的失败]
Promise.allSettled([ Promise.resolve('a'),//创建了一个成功状态的Promise对象 Promise.reject('b'),//创建一个失败状态的Promise对象 ]).then(arr => console.log(arr))
Promise.race[比赛]
//只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。
//那个率先改变的 Promise 实例的返回值,就传递给p的回调函数
var p =Promise.race([p2(),p1(),p3()]);
p.then(res=>{
console.log(res)
})
async与await[能让我们的异步代码看起来像是同步代码]
-
async:作为一个关键字放到函数前面,用于表示函数是一个异步函数,因为async就是异步的意思, 异步函数也就意味着该函数的执行不会阻塞后面代码的执行。
async function timeout() { return 'hello world'; } console.log(timeout()); //返回一个Promise对象 console.log('虽然在后面,但是我先执行'); timeout().then(result => { console.log(result); // 用这个对象.then才能获取结果 hello world })
-
await:等待的意思,它后面跟着一个返回promise 对象的表达式,注意await 关键字只能放到async 函数里面。
async function show(){ return 123; } async function play(){ var res =await show(); console.log(res); //123 } play();
补充:
01 - Promise.all
function p1(){
return new Promise(resolve=>{
resolve("success")
})
}
function p2(){
return new Promise((resolve,reject)=>{
reject("fail")
})
}
function p3(){
return new Promise(resolve=>{
resolve("success")
})
}
function start(){
//如果三者都返回成功,最终结果就是成功;如果三者有一个返回失败,最终结果就是失败
Promise.all([p1(),p2(),p3()]).then(res=>{
console.log(res);
}).catch(err=>{
console.log(err)
})
}
start();
02 - Promise.allSettled
//Promise的三种状态
//还没出结果的时候:pending
//成功结果:fulfilled
//失败结果:rejected
//pending->fulfilled 或 pending->rejected (只能是从没结果到成功,或失败)
//fulfilled->rejected (不会出现从成功变成失败,或从失败变成成功)
//同时启动多个Promise实例,返回多个结果[允许有的成功,有的失败]
function start(){
Promise.allSettled([
Promise.resolve("111"), //创建了一个成功状态的Promise实例
Promise.reject("222"),//创建了一个失败状态的Promise实例
Promise.resolve("333"), //创建了一个成功状态的Promise实例
]).then(res=>{
console.log(res);
})
}
start();
03 - Promise.race
function p1(){
return new Promise(resolve=>{
resolve("success1")
})
}
function p2(){
return new Promise((resolve,reject)=>{
reject("fail")
})
}
function p3(){
return new Promise(resolve=>{
resolve("success2")
})
}
function start(){
//race:比赛
//只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。
//那个率先改变的 Promise 实例的返回值,就传递给p的回调函数
var p =Promise.race([p2(),p1(),p3()]);
p.then(res=>{
//最终结果就是执行最快的那个Promise,如果没有时间上的差异,就是按先后顺序
console.log(res)
})
}
start();
04 - async
//给函数前面加async关键字,函数的返回值就会变成一个Promise对象
//如果我们手动在函数里面return了一个值,则这个值就是Promise对象里面的value,通过then可以取出
async function show(){
return 123;
}
// 相当于写了下面的代码
// function show(){
// return new Promise(resolve=>{
// resolve(123)
// })
// }
//也相当于下面的代码
// function show() {
// return Promise.resolve(123);
// }
show().then(res=>{
console.log(res);//123
})
05 - async_await
//async - await:能让我们的异步代码看起来像是同步代码
async function show(){
return 123;
}
//await关键字只能在被async修饰的function里面使用
async function play(){
//用await来代替 .then方法,res就是then()里面正确返回的结果
try {
var res =await show();
console.log(res);
} catch (error) {
console.log("万一报错了,执行这里,原因:"+error)
}
}
play();
06 - async_ajax
function ajax(url, method, data) {
return new Promise((resolve,reject)=>{
let xhr = new XMLHttpRequest();
xhr.open(method, url);
throw "错错错,是我的错"
if (method.toUpperCase() == 'POST') {
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
}
xhr.send(data);
xhr.onreadystatechange = function () {
if (this.readyState == 4) {
if (this.status == 200) {
let res = JSON.parse(this.response)
if (res.errcode) {
alert(res.errmsg)
return;
}
resolve(res);
} else {
reject(this.statusText)
}
}
}
})
}
//使用async+await 让代码看起来像同步代码,代替了过去的回调函数,以及 then
async function play(){
try {
let url = 'http://yapi.smart-xwork.cn/mock/91220/user/list'
let res =await ajax(url,'GET',null)
console.log(res);
} catch (error) {
console.log(error)
}
}
play();
console.log("黑色")
console.error("红色")
console.info("蓝色")
console.warn("黄色")
day09 - WebPack常用配置
webpack.config.js
const path = require('path');
const htmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const Terser =require('terser-webpack-plugin');
const Clean = require('clean-webpack-plugin');
const WebpackBar = require('webpackbar');
module.exports={
//环境:development(开发环境)、production(线上环境)
mode:'development',
// 入口
entry:{
index:'./src/index.js',
print:'./src/print.js',
app:'./src/app.js'
},
// 出口
output:{
filename:'[name].bundle.js',
path:path.resolve(__dirname,'dist')
},
//插件配置
plugins:[
new htmlWebpackPlugin({
filename:'index.html', //生成的html的文件名,默认就是index.html
title:'Webpack4.x学习', //网页的标题
favicon:'./favicon.ico',//网页小图标
inject:'body',//将生成的js插入在body里面,也可以写head
chunks:['index'],//只引入print那个js文件
minify:{ //压缩规则
'removeComments':true, //删除所有注释
'collapseWhitespace':false,//删除所有的空格、换行
}
}),
new htmlWebpackPlugin({
filename:'login.html', //生成的html的文件名,默认就是index.html
title:'登录', //网页的标题
template:'./src/login_template.html', //设置一个模板文件
favicon:'./favicon.ico',//网页小图标
inject:'body',//将生成的js插入在body里面,也可以写head
chunks:['app'],//只引入app那个js文件
minify:{ //压缩规则
'removeComments':true, //删除所有注释
'collapseWhitespace':false,//删除所有的空格、换行
}
}),
new MiniCssExtractPlugin({filename:"[name].css"}),//分离CSS
new OptimizeCssAssetsPlugin(),//压缩CSS文件
new Terser(), //在开发环境下压缩JS文件
new Clean.CleanWebpackPlugin(), //清理dist文件夹
new WebpackBar() //打包进度条
],
module:{
rules:[
{
test:/\.css$/,
// use:['style-loader','css-loader']
use: [{loader:MiniCssExtractPlugin.loader},'css-loader']
},
{
test:/\.(jpg|png|gif|webp)$/,
use:[{
loader:'url-loader',
options:{
limit:8192,
outputPath:'images/'
}
}]
},
{
test:/\.(ttf|woff)$/,
use:['file-loader']
}
]
}
}
补充:
01 - webpack
配置相关
-
使用参考:
-
安装
node.js
-
安装淘宝镜像
npm install -g cnpm -- registry=https://registry.npm.taobao.org
-
创建“工作区”文件夹
-
cmd切换到该目录
-
npm init -y
该文件夹生成package.json
文件webpack.config.js
手动新建转换配置文件 -
安装
cnpm install --save-dev webpack@4.32.2
和cnpm install --save-dev webpack-cli@3.3.11
(我感觉叫webpack
支持) -
这是文件夹大致分布。(dist输出文件夹,src输入文件夹,父盒子 工作配置相关)
-
index.html
修改(略) -
package.json
设置{ "name": "day09", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "webpack-dev-server --open --config webpack.dev.js", "build": "webpack --config webpack.prod.js" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "clean-webpack-plugin": "^4.0.0", "css-loader": "^5.0.2", "html-webpack-plugin": "^4.5.2", "mini-css-extract-plugin": "^0.8.2", "optimize-css-assets-webpack-plugin": "^6.0.1", "style-loader": "^2.0.0", "terser-webpack-plugin": "^1.4.5", "url-loader": "^4.1.1", "webpack": "^4.32.2", "webpack-bundle-analyzer": "^4.4.2", "webpack-cli": "^3.3.11", "webpack-dev-server": "^3.11.2", "webpack-merge": "^5.8.0", "webpackbar": "^5.0.0-3" } }
、
webpack.config.js
设置const path = require('path'); // 引入jquery // const $ = require('jquery'); // 新建html const htmlWebpackPlugin = require('html-webpack-plugin'); // 把css抽出为单独的文件 const MiniCssExtractPlugin = require("mini-css-extract-plugin"); // 压缩css const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'); // 压缩js const Terser = require('terser-webpack-plugin'); // 每次构建前清理 /dist 文件夹 const Clean = require('clean-webpack-plugin'); // 打包进度条 const WebpackBar = require('webpackbar'); // 分析并可视化构建后的打包文件 const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; // 导出webpack配置内容 module.exports = { mode: 'development', // 入口 // entry: './src/index.js', entry: { index: './src/index.js', // print:'./src/print.js', app: './src/app.js' }, // 出口 output: { // filename: 'bundle.js', filename: '[name].bundle.js', // __dirname:代表当前文件的绝对路径 path: path.resolve(__dirname, 'dist') }, plugins: [ new htmlWebpackPlugin({ filename: 'index.html', //生成的html的文件名,默认就是index.html title: '张胖胖', //网页的标题 favicon: './favicon.ico',//网页小图标 inject: 'body',//将生成的js插入在body里面,也可以写head chunks: ['index'],//只引入print那个js文件 minify: { //压缩规则 'removeComments': false, //删除所有注释 'collapseWhitespace': false,//删除所有的空格、换行 } }), new htmlWebpackPlugin({ filename: 'login.html', //生成的html的文件名,默认就是index.html title: '登录', //网页的标题 template: './src/index.html', //设置一个模板文件 favicon: './favicon.ico',//网页小图标 inject: 'body',//将生成的js插入在body里面,也可以写head chunks: ['app', 'index'],//只引入app那个js文件 minify: { //压缩规则 'removeComments': false, //删除所有注释 'collapseWhitespace': false,//删除所有的空格、换行 } }), new MiniCssExtractPlugin({ filename: "[name].css" }), new Clean.CleanWebpackPlugin(), new WebpackBar({ color: 'pink' }), // new OptimizeCssAssetsPlugin(), // new Terser(), // new BundleAnalyzerPlugin, ], module: { rules: [ { test: /\.css$/, // use: ['style-loader', 'css-loader'] use: [{ loader: MiniCssExtractPlugin.loader }, 'css-loader'] }, { test: /\.(jpg|png|gif|webp)$/, use: [{ loader: 'url-loader', options: { limit: 8192, outputPath: 'images/' } }] } ] }, // 以服务器打开实时更新 devServer: { port: 8000, host: '127.0.0.1', contentBase: './dist', overlay: { errors: true }, } }
。(
index.js
随便输入个东西,此时基本就可以用npm run build
运行看看了,具体见readme) -
安装
loader
(插件)--save-dev
忘了怎么说了:-
处理css:
cnpm install --save-dev style-loader@2.0.0
和cnpm install --save-dev css-loader@5.0.2
规则:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0UnHyoHu-1634128183218)(images/image-20210917200556054.png)]
-
url:
cnpm install --save-dev url-loader
小于8kb的图片用什么64转换显示。规则:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9DSeeczG-1634128183219)(images/image-20210917200626238.png)]
-
打包进度条:
cnpm install --save-dev webpackbar
new一个!
new WebpackBar({ color: 'pink' }),
-
分析并可视化构建后的打包文件
cnpm install --save-dev webpack-bundle-analyzer
new一个!
new BundleAnalyzerPlugin,
-
以服务器打开:
cnpm install --save-dev webpack-dev-server@3.11.2
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xCqVzLWR-1634128183220)(images/image-20210917200927304.png)]
-
拆分webpack.config.js插件:
cnpm install --save-dev webpack-merge
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PSyctkMS-1634128183220)(images/image-20210917201043750.png)]
-
时间:
cnpm install time-stamp
-
全部重新安装:
cnpm install
-
-
-
安装
plugin
(工具):-
创建html:
cnpm install --save-dev html-webpack-plugin@4.5.2
规则:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MPm3HSEb-1634128183221)(images/image-20210917201335530.png)]
-
把css抽离成单独的css文件:
cnpm install mini-css-extract-plugin@0.8.2 -D
new一个!
-
CSS代码压缩:
cnpm install --save-dev optimize-css-assets-webpack-plugin
new一个!
-
在开发环境下,压缩JS代码:
cnpm install --save-dev terser-webpack-plugin@1.4.5
new一个!
-
每次构建前清理 /dist 文件夹,是比较推荐的做法:
cnpm install --save-dev clean-webpack-plugin
new一个!
new 名.CleanWebpackPlugin(),
-
02 - cmd
相关
cmd
常用命令node xx.js
运行JS文件cls
清屏dir
列出当前目录下的所有文件cd 目录名
进入指定的文件夹md 目录名
创建一个文件夹rd 目录名
删除该文件夹(只能删空文件夹)盘符号:
切换到指定盘符
day10 - GIT命令
集中式的版本控制系统:SVN,CVS
-
版本库是集中存放在中央服务器的,而干活的时候,用的是自己的电脑,所以要先从中央服务器取得最新的版本,
然后开始干活,干完活了,再把自己的活推送给中央服务器。
中央服务器就好比是一个图书馆.你要改一本书.必须先从图书馆借出来,然后回到家自己改,改完了,再放回图书馆。
集中式版本控制系统最大的毛病就是必须联网才能工作,如果在局域网内还好,带宽够大,速度够快,可如果在互联网上,遇到网速慢的话,可能提交一个10M的文件就需要5分钟
分布式版本控制系统:Git
-
分布式版本控制系统根本没有“中央服务器”,每个人的电脑上都是一个完整的版本库,这样,你工作的时候,
就不需要联网了,因为版本库就在你自己的电脑上。既然每个人电脑上都有一个完整的版本库,那多个人如何协作呢?
比方说你在自己电脑上改了文件A,你的同事也在他的电脑上改了文件A,这时,你们俩之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。
分布式 VS 集中式
-
集中式版本控制系统相比,分布式版本控制系统的安全性要高很多,因为每个人电脑里都有完整的版本库,
某一个人的电脑坏掉了不要紧,随便从其他人那里复制一个就可以了。
而集中式版本控制系统的中央服务器要是出了问题,所有人都没法干活了。
由于CVS自身设计的问题,会造成提交文件不完整,版本库莫名其妙损坏的情况。
同样是开源而且免费的SVN修正了CVS的一些稳定性问题,是目前用得最多的集中式版本库控制系统。Git的优势不单是不必联网这么简单,后面我们还会看到Git极其强大的分支管理,把SVN等远远抛在了后面。
安装Git
-
Linux:
终端中直接输入 sudo apt-get install git -
Mac:
运行Xcode,菜单“Xcode”->“Preferences”,
在弹出窗口中找到“Downloads”,选择“Command Line Tools”,点“Install”就可以完成安装了 -
Windows:
下载地址:https://git-for-windows.github.io
安装后,在开始菜单里找到“Git”->“Git Bash”,蹦出一个类似命令行窗口的东西,就说明Git安装成功!
最后:
在命令行输入名字和邮箱进行登记:
$ git config —global user.name “Your Name”$ git config —global user.email “email@example.com”
创建版本库(repository)
- 可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,
以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。- 创建一个空的文件夹(frankGit),确保目录名和父级目录名没有中文和符号
- 命令行:git init 把这个目录变成Git可以管理的仓库
tips:
所有的版本控制系统,其实只能跟踪文本文件的改动,也就是在文本中修改和添加内容都可以被检测到。
图片、视频、Word这种二进制文件,无法跟踪变化,只能检测文件大小发生了变化。
将文件添加到版本库
- 创建一个readme.md文件
内容:do you love me? - 放在frankGit或frankGit的子目录
- 命令行: git add readme.md //把文件添加到仓库暂存区,可以添加多次之后再进行commit
- 命令行: git commit -m “本次提交了XXXX,么么哒” //把文件提交到仓库
查看版本库状态和修改记录
- 修改readme.md文件 内容:do you hate me?
- 命令行:git status //仓库当前的状态
- 查看修改具体内容:git diff
- 再次提交到版本库暂存区:git add readme.md
- 再查看状态:git status
- 再次提交到版本库:git commit -m “呵呵,这次我不爱你了。”
- 再查看状态:git status
[每次修改,如果不add到暂存区,那就不会加入到commit中。]
版本回退
- 命令行:git log //查看提交的历史记录
- 命令行:git log —pretty=oneline //美化输出结果,可以看到一串十六进制的版本号
- 命令行:git reset —hard HEAD^ //回退到上一个版本
- 命令行:git reset —hard 版本号前几位 //也可以通过版本号跳转到指定版本
- 命令行:git reflog //查看命令的历史记录
撤销修改
- 命令行:git checkout — readme.md //撤销本地文件的修改
- 命令行:git reset HEAD readme.md //撤销暂存区文件的修改
- 命令行:git reset —hard HEAD^ //撤销版本库里文件的修改:回退到上一个版本
删除文件
- 命令行:rm readme.md //删除本地文件
- 命令行:git status //提示本地和版本库不一致
- 命令行:git rm readme.md //删除缓冲区文件
- 命令行:git commit -m “本次删除了readme文件,以此为据” //删除版本库里文件
- 命令行:git checkout — readme.md //如果是误删,可以撤销删除操作(本地)
配置远程仓库
- GitHub:用于提供Git仓库托管服务 ,只要注册一个GitHub账号,就可以免费获取Git远程仓库
- 步骤:
- 打开github,注册一个账号。
- 创建ssh key,在C:\用户\用户名.ssh目录,如果没有id_rsa文件和id_rsa.pub文件,就需要创建。
2-1. ssh-keygen -t rsa -C “154580967@qq.com”
2-2. 一路默认 - 登录GitHub,打开“Account settings”,“SSH Keys”页面:
3-1. 点“Add SSH Key”,填上任意Title,在Key文本框里粘贴id_rsa.pub文件的内容:
3-2. 点“Add Key”,就应该看到已经添加的Key:
[在GitHub上免费托管的Git仓库,任何人都可以看到。所以,不要把敏感信息放进去。]
从本地同步到远程库
- 已经在本地创建了一个Git仓库后,又想在GitHub创建一个Git仓库,并且让这两个仓库进行远程同步
- 步骤:
- 登录GitHub,在右上角找到“Create a new repo”按钮,创建一个新的仓库
- 在Repository name填入frankGit,其他默认,点击“Create repository”,就成功地创建了一个新的Git仓库
- 命令行:git remote add origin git@github.com:自己的帐户名/frankGit.git //origin是自定义远程库的名字
[git 解决fatal: Not a git repository:git init] - 命令行:git push -u origin master //将当前分支推送到远程,并将本地master和远程master关联起来
[推送前确保本地仓库是有东西的,commit过的] - 命令行:git push origin master //把本地master分支的最新修改推送至GitHub
从远程库克隆到本地
- 登录GitHub,创建一个新的仓库,名字叫gitskills
- 勾选Initialize this repository with a README,自动创建README.md文件。
- 命令行:git clone git@github.com:自己的帐户名/gitskills.git
[可让多人从同一个仓库下载到自己的本地进行开发]
分支管理
- 分支就是科幻电影里面的平行宇宙,如果两个平行宇宙互不干扰,那对现在的你也没啥影响。
默认分支:master - 使用:
- 命令行:git branch dev //创建分支dev
- 命令行:git checkout dev //切换到dev分支
- 命令行:git checkout -b dev //创建分支dev,并切换到dev分支
- 命令行:git branch //查看当前分支,前面带*号的就是当前分支
- 命令行:git add readme.md
git commit -m “本次是从dev分支提交的” - 命令行:git checkout master //切换回master
- 命令行:vi readme.md //再查看文件,会发现文件中刚添加的内容不存在了。
- 命令行:git merge dev //合并指定分支到当前分支
- 命令行:git branch -d dev //删除dev分支
- 命令行:git branch //再查看,dev分支就没有了
- 如果两个分支同时修改一个文件,提交。再合并,就会出现冲突。
- 只需要将冲突的文件打开,删除标记,重新add和commit
- 命令行:git branch -d dev
- 命令行:git log --graph //查看分支合并图
强行删除分支(feature)
- 命令行:git checkout -b feature //创建新分支并切换
- 命令行:vi work.js //创建work.js文件
- 命令行:git add work.js //添加到缓存区
- 命令行:git commit -m “添加一个新的分支咯” //添加到版本库一个新分支
- 命令行:git checkout dev //切换回dev分支
- 命令行:git branch -d feature //还未合并就要马上删除feature分支
- 命令行:git branch -D feature //必须强行删除才可以
多人协作
- 命令行:git remote //查看远程库的信息
- 命令行:git remote -v //显示更详细的远程库信息
- 命令行:git push origin master //把master分支上的所有本地提交推送到远程库
- 命令行:git push origin dev //推送dev分支到远程库
强制推送:git push -u origin dev -f - 当自己和伙伴提交有冲突时,使用git pull把最新的提交从远程库下载下来,在本地合并,再推送。
命令行:git branch --set-upstream dev origin/dev //指定本地dev分支与远程origin/dev分支的链接
命令行:git pull --rebase origin master - 命令行:git add readme.md
- 命令行:git commit -m “合并后再提交”
工作流程
- [repository-点击settings-选择collaborators-搜索框中搜索合作者账号然后add,然后给合作者发确认邮件]
[必须在SSH中添加合作者的pub.key]- 克隆到本地环境
- 修改文件先在本地进行提交
- 用git push origin branch-name推送自己的修改;
- 如果推送失败,则因为远程分支比你的本地更新,需要先用git pull origin branch-name 试图合并;
- 如果合并有冲突,则解决冲突,并在本地提交;
- 没有冲突或者解决掉冲突后,再用git push origin branch-name推送就能成功!
- 如果git pull提示“no tracking information”,则说明本地分支和远程分支的链接关系没有创建,
用命令git branch --set-upstream branch-name origin/branch-name。 - git 出现 fatal: refusing to merge unrelated histories 错误,其实这个问题是因为 两个 根本不相干的 git 库
git pull origin master --allow-unrelated-histories 把两段不相干的 分支进行强行合并
[master:主分支要时刻与远程同步]
[dev:开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步]
[bug:本地修复bug记录,不需要推送到远程]
[feature:取决于你是否和你的小伙伴合作在上面开发]
常用的Linux命令
- 命令:ls【列出当前目录里面所有的文件和文件夹】
- 命令:touch login.html【创建一个login.html空文件】
- 命令:cat login.html【在命令行中预览login.html文件的内容】
- 命令:clear 【清屏】
- 命令:mkdir 文件夹名 【创建一个文件夹】
- 命令:pwd【输出当前的路径】
- 命令:vim login.html【vim是系统内置的一款编辑器】
- 命令:vim login.html【用vim打开login.html文件】
- 按下:i【切换到编辑模式】
- 随意的修改文件内容
- 按下:ESC【关闭编辑模式】
- 按下:冒号 + wq【保存并退出vim】
补充:
01 - GIT常用的命令
-
git config --global user.name "自己的名字"
git config --global user.email "自己的邮箱"
git config user.name 查看当前用户名
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZhyFW7vX-1634128183222)(images/git命令.jpg)]
day11 - Node模块(一)
Node的特点 ♥
- 本身采用JS语言编写,基于Chrome的V8引擎进行解析的。
- 无需安装运行服务器软件,就可以直接创建一个HTTP服务。
- 单线程
- 非阻塞IO
- 事件驱动
Node的模块化 ♥
-
特点:基于CMD标准-同步的模块化标准
语法://文件A:导入 const 模块名 = require('路径'); 模块名.成员1; 模块名.成员2();
//文件B:导出 exports.成员1 = 值; exports.成员2 = function(){}; //导出 module.exports = { 变量名,函数名 }
ES6的模块化
//文件A:导出
class Animal{
constructor(name){
this.name=name;
}
}
function print(){
console.log("打印。。。。")
}
function play(){
console.log("播放")
}
//具名导出
export const show= () =>{
console.log("show...")
};
//具名导出
export {print,play};
//默认导出【每个文件只能写一次】
export default Animal;
//文件B:导入
import Animal,* as api from './output.js';
let ani = new Animal('jack')
console.log(ani);
api.print();
api.play();
api.show();
//文件C:导入
import Animal,{print,play,show} from './output.js';
let ani = new Animal('jack')
console.log(ani);
print();
play();
show();
fs模块【文件操作模块】
01 - 读取文件(异步)
-
语法:fs.readFile(filename, [options], callback)
-
filename 是文件名;[options] 是可选的参数 , 为一个对象,用于指定文件编码(encoding)及操作方式(flag);callback 是回调函数,传递异常err 和文件内容 data 的 2 个参数
var fs = require("fs"); // 引入 fs 模块 fs.readFile(".test.txt",”utf-8”,function(err,data){ if(err){ throw err; } // 读取文件成功 console.log(data); });
02 - 读取文件(同步)
-
语法:fs.readFileSync(filename, [options])
-
filename 是文件名;[options] 是可选的参数 , 为一个对象,用于指定文件编码(encoding)及操作方式(flag);
var fs = require("fs"); // 引入 fs 模块 var result = fs.readFileSync(".test.txt",”utf-8”); console.log(result);
03 - 写入文件(异步)
-
语法:fs.writeFile(filename,data,[options],callback)
-
filename是文件名称;data是要写入文件的数据;[options]是一个对象为可选参数,包含编码格式(encoding),模式(mode)以及操作方式(flag);callback 是回调函数
var fs = require("fs"); var data = " 嘿嘿嘿”; // 写入文件内容,如果文件不存在则创建文件 fs.writeFile("./test/11.txt",data,{"flag":"a"},function(err){ if(err) throw err; });
04 - 写入文件(同步)
-
语法:fs.writeFileSync(file, data[, options])
-
filename是文件名称;data是要写入文件的数据;[options]是一个对象为可选参数,包含编码格式(encoding),模式(mode)以及操作方式(flag)
var fs = require("fs"); var data = " 嘿嘿嘿”; // 返回undefined fs.writeFileSync("./test/11.txt",data,{"flag":"a"});
05 - 流式文件写入【适合大文件的写入,性能高,不易导致内存溢出】
var fs = require("fs");
//1.创建一个可写流
var ws = fs.createWriteStream("hello3.txt",{flags:'a'});
//2.绑定打开和关闭的一次性事件ws.once("open",function () {console.log("流打开了");});
ws.once("close",function () {console.log("流关闭了");});
//3.通过ws向文件中输出内容
ws.write("百度");
ws.write("IT培训");
//4.关闭流
ws.end();
06 - 流式文件读取【适合大文件的读取,性能高,不易导致内存溢出】
var fs = require("fs");
const rs = fs.createReadStream('./我的自白书.txt','utf-8')
//onData事件,代表读取中
rs.on('data',(data)=>{
//读取的过程中,同时写入
ws.write(data)
})
//onClose事件,表示读取结束
rs.on('close',function(){
//读取结束后,关闭ws
ws.end();
})
07 - 删除文件(异步)
-
语法:fs.unlink(path, callback)
-
path是文件路径;callback 是回调函数(参数只有一个error对象)
var fs = require("fs"); // 假设 'path/file.txt' 是一个普通文件。 fs.unlink('path/file.txt', (err) => { if (err) throw err; console.log('文件已删除'); });
08 - 删除文件(同步)
-
语法:fs.unlinkSync(path)
-
path是文件路径
var fs = require("fs"); // 没有返回值 fs.unlinkSync('path/file.txt');
09 - 创建文件夹(异步)
-
语法:fs.mkdir(path, [mode], callback)
path 是需要创建的目录;
[mode] 是目录的权限(不支持windows平台);
callback 是回调函数var fs = require("fs"); fs.mkdir("./wenjianjia",function(err){ if(err){ throw err; } console.log(" 创建目录成功 "); })
10 - 读取目录(异步)
-
语法:fs.readdir(path, callback)
path 是需要读取的目录;callback 是回调函数.
会返回error和一个数组,数组中是当前根目录下的所有文件夹名称和文件名称。var fs = require("fs"); fs.readdir("./wenjianjia",function(err,files){ if(err){ throw err; } console.log(files); });
11 - 删除目录(异步)
-
语法:fs.rmdir(path, callback)
path 是需要删除的目录;callback 是回调函数.
注意:只能删除空目录var fs = require("fs"); fs.rmdir("./wenjianjia",function(err){ if(err){ throw err; } });
12 - 查看文件与目录的信息
-
语法:fs.stat(path, callback)
path 要查看目录 / 文件的完整路径及名;[callback(err, stats)] 操作完成回
调函数,err 错误对象,stats fs.stat 一个对象实例,提供如 :isFile, isDirectory 等方法。var fs = require('fs'); fs.stat('./wenjianjia',function(err,stats){ if(stats.isDirectory()){ console.log('文件夹'); }; })
13 - 查看文件与目录是否存在
-
语法:fs.exists(path, callback)
path要查看目录 /文件的完整路径及名;[callback(exists)]操作完成回调函数,exists true 存在,false 表示不存在。var fs = require("fs"); fs.exists("./test/",function(exists){ if(exists){ console.log(" 文件夹存在 "); }else{ console.log(" 文件夹不存在 "); } });
14 - 重命名文件
-
语法:fs.rename(oldPath,newPath,callback)
异步地将 oldPath 上的文件重命名为 newPath 提供的路径名. 如果 newPath 已存在,则覆盖它let fs = require('fs') fs.rename('./foo.txt','./bar.txt',function (err) { if(err){ throw err } console.log('修改成功') })
URL模块L
- 一个完整的URL地址分为:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wHWsqQ1Q-1634128183222)(images/20210922171659.png)]
01 - parse函数
-
语法:url.parse(urlStr[, parseQueryString])
urlStr 输入一个 url 字符串;parseQueryString(默认为 false),如为true,则 urlObject.query 为解析后的对象var url = require("url"); var path ="http://user:pass@host.com:8080/p/a/t/h?query=string#hash"; var query= url.parse(path).query; //query=string var queyr= url.parse(path,true).query; //{query:'string'}
path模块
- 处理路径的工具模块
01 - path.basename
-
获取路径中的文件名,第一个参数 path 为必须指定的参数,ext 参数为可选参数。path 参数值必须为一个文件的完整路径,可以是相对路径,也可以使绝对路径。ext 参数值用于在方法所返回的文件名中去掉文件的扩展名
var path = require("path"); var fileName = path.basename("./test/aa.txt"); console.log(fileName); // aa.txt var name = path.basename("./test/aa.txt",".txt"); console.log(name); //aa
02 - path.dirname
-
提取出一个路径中的祖先路径
var path = require("path"); var dirname = path.dirname("./test/i/aa.txt"); console.log(dirname); //./test/i var dirname = path.dirname('./test/i'); console.log(dirname); //./test
03 - path.extname
-
获取一个路径中的扩展名
var path = require("path"); var extname = path.extname("./test/aa.txt"); console.log(extname);//.txt
day12 - Node模块(二)
继续Path模块
04 - path.join
- path.join([path1], [path2], […])
将多个参数值字符串结合成一个路径字符串
05 - path.resolve
- path.resolve([path1], [path2], […])
把一个路径或路径片段的序列解析为一个绝对路径,类似cd命令
06 - join和resolve的对比
var path = require("path");
path.join('/a', '/b') // Outputs '\a\b'
path.resolve('/a', '/b') // Outputs 'H:\b'
07 - path.normalize
-
于将非标准路径的字符串转化成标准路径字符串
var path = require("path"); var normalize = path.normalize("../a//b//d//..//c//d/") console.log(normalize);//..\a\b\c\d
querystring模块
01 - querystring.parse
-
querystring.parse(str[, sep[, eq[, options]]])
-
是将一个字符串反序列化为一个对象
-
str 指需要反序列化的字符串,sep(可选)指用于分割 str 这个字符串的字符或字符串,默认值为 “&”;eq(可选)指用于划分键和值的字符或字符串,默认值为 “=”;options(可选)该参数是一个对象,里面可设置 maxKeys 属性:传入一个 number 类型,指定解析键值对的最大值,默认值为 1000,如果设置为 0 时,则取消解析的数量限制
var querystring = require("querystring"); var newObj=querystring.parse("site=QQ&url=http://www.qq.com/"); var queryObj=querystring.parse("site=QQ#url=http://www.qq.com/ #age=5","#",null,{maxKeys:2});
02 - querystring.stringify
-
querystring.stringify(obj[, sep[, eq[, options]]])
-
将一个对象序列化成一个字符串
-
数 obj 指需要序列化的对象;sep(可选)用于连接键值对的字符或字符串,默认值为 “&”;eq(可选)用于连接键和值的字符或字符串,默认值为 “=”;
var querystring = require("querystring"); var str = querystring.stringify({name:"baidu",sex:"man"}); console.log(str); //name=baidu&sex=man console.log(queryStr); //name*baidu|sex*man
03 - querystring.escape
-
使传入的字符串进行编码
var querystring = require("querystring"); var str = querystring.escape("name= 百度 "); console.log(str);
04 - querystring.unescape
-
将含有 % 的字符串进行解码
var querystring = require("querystring"); var str = querystring.unescape("name%3D%E4%BC%98%E5%B0%B1%E4%B8%9A"); console.log(str);
创建HTTP服务
//创建一个http服务(就可以通过网址+端口号来访问了)
//1.引入http模块
const http = require('http')
const fs = require('fs')
//2.通过http对象创建Server
http.createServer(function(req,res){
console.log(req.url);
let data=null;
if(req.url=='/'){
//设置响应头,类型为text/html,可以解析html标签
res.writeHead(200,"ok",{'Content-Type':'text/html;charset=utf-8'})
//文件读取:网页
data = fs.readFileSync('./index.html','utf-8')
//将网页内容输出给浏览器
}else if(req.url.endsWith('.css')){
//设置响应头,类型为text/html,可以解析html标签
res.writeHead(200,"ok",{'Content-Type':'text/css;charset=utf-8'})
data = fs.readFileSync('.'+req.url,'utf-8');// ./public.css
}else if(req.url.endsWith('.js')){
res.writeHead(200,"ok",{'Content-Type':'text/javascript;charset=utf-8'})
data = fs.readFileSync('.'+req.url,'utf-8');// ./public.css
}else{
res.writeHead(200,"ok",{'Content-Type':'image/x-icon'})
data = fs.readFileSync('.'+req.url);// 图片
}
res.write(data);
res.end();//关闭[如果不关闭,页面就会一直加载中,一直等待]
}).listen(8888)
console.log('server startup on 8888')
//3.监听端口:8888
//4.访问地址:http://localhost:8888/
day13
学员管理系统,增删改登录
补充:
01 - 概念/安装/运行
node.js
是一个基于chrome V8
引擎的js
运行时(运行时指的就是“运行环境”)。- 浏览器的内核需要分为,渲染引擎、JS引擎。
- 安装:略(就是下一步!)
- 运行:略(cmd窗口:node 名.js)
02 -编码/解码
const querystring = require('querystring')\
// 原因:亚洲文字,特殊符号在网络传输中容易乱码
// 编码
let res = querystring.escape('哈哈=123')
// 解码
let res2 = querystring.unescape('%E5%93%88%E5%93%88=123')
day14 - Express框架
Express
NodeJS最有名的框架
原生Node开发存在的问题
- 呈递静态页面不方便,需要处理每个HTTP请求、考虑304问题及缓存问题等
- 路由处理代码不直观不清晰,需要写很多正则表达式和字符串函数
- 不能集中精力写业务,要考虑很多其他的东西
Express安装
npm install express
//或
yarn add express
//或
cnpm install express
基本结构
//app.js
var express = require("express");
var app = express();
//任何请求方式,任何地址都会进入,可用作拦截器使用
app.all("*",function(req,res,next){
next();
})
//匹配路由时,可以自动过滤掉query和hash部分
app.get('/', function (req, res) {
//res.sendFile(); 发送页面给浏览器
res.sendFile(__dirname + '/post_client.html');
//res.send(); 发送数据给浏览器
});
app.listen(8000)
使用express-generator脚手架
-
安装工具:npm install express-generator -g
-
生成项目:express —view=ejs myapp
-
安装依赖:cd myapp && npm install
-
启动项目:set DEBUG=myapp:* & npm start
-
打开浏览器:http://localhost:3000/
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wpc05ApJ-1634128183223)(images/Express脚手架.png)]
在Express中获取GET请求数据
var express = require("express");
var app = express();
app.get("/",function(req,res){
console.log(req.query);
res.send(req.query);
});
app.listen(3000);
在Express中获取POST请求数据
var express = require("express");
var bodyParser = require('body-parser')
var app = express();
app.use(bodyParser.urlencoded({ extended: false }))
app.get("/",function(req,res){
res.send(req.body.username);
});
app.listen(3000);
Koa2
近两年比较流行的框架
补充:
01 - express 默认导入,静态资源
// 默认引入
const express = require('express');
const app = express();
const path = require('path');
// 处理静态资源,要指定静态资源在哪里{assets文件夹中}(js,css,font,媒体)
app.use(express.static(path.ressolve(__dirname,'assets')));
day15 - 文件上传与RestFul
文件上传
-
安装:
npm install multer
-
配置:app.js
var indexRouter = require('./routes/index'); var usersRouter = require('./routes/users'); //【要写在业务模块的前面】 app.use(multer({dest: '/tmp/'}).array('myFile', 10)); //业务模块【indexRouter里面的路径可以直接访问,userRouter里的路径需要在前面加/users】 app.use('/', indexRouter); app.use('/users', usersRouter);
-
上传后端:/routes/users.js
//页面访问路径:/users/user/add router.post('/user/add',function(req,res){ //1000 - 9999 var max = 9999; var min = 1000; req.files.forEach(f=>{ //1.拼接一个新的文件名:毫秒值+随机数+后缀名组成 var t = Date.now(); var r = parseInt(Math.random()*(max-min))+min; //生成一个四位随机数 var ext = path.extname(f.originalname);//获取源文件的后缀名 .jpg var name = t + "" + r + ext; //2.读取原始文件 var data = fs.readFileSync(f.path); //3.将图片写入到我们指定的文件夹中 //__dirname:桌面\Express-app\routes //拼完的路径:桌面\Express-app\photo\123123213123.jpg fs.writeFile(path.resolve(__dirname,'../public/images',name),data,function(err){ if(err){ console.log("报错了:",err); throw err; } //如果照片上传成功,再将学员姓名添加到数组中,并返回success //POST的参数:req.body.参数名 console.log("学员姓名:",req.body.username); //将学员姓名和学员照片名以对象形式存储在数组里 students.unshift({ username:req.body.username, photo:name }); }) }) res.send("success"); })
-
上传前端:/views/add.html
-
html部分
<input type="text" name="username" id="username" placeholder="请填写学员姓名"><br> <input type="file" name="userphoto" id="userphoto"><br> <span></span> <button>提交</button>
js部分
$("button").click(function(){ var username = $("#username").val(); var userphoto = document.getElementById("userphoto"); var formData = new FormData(); formData.append('username',username); formData.append('userphoto',userphoto.files[0]) $.ajax({ url:'/users/user/add', method:'POST', data:formData, processData:false, //不要去处理文件数据内容 contentType:false, //告诉jQuery不要去设置Content-Type请求头 success:function(res){ console.log(res); }, xhr:function(){ let xhr = $.ajaxSettings.xhr(); if(xhr.upload){ //上传进度查看 xhr.upload.addEventListener('progress',function(e){ var percent = parseInt(e.loaded / e.total * 100); $("span").html(percent+"%"); }) } return xhr; } }) })
ESTFul API4
-
RESTful架构,是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。每一个URI代表一种资源;
客户端和服务器之间,传递这种资源的某种表现层;
客户端通过四个HTTP动词,对服务器端资源进行操作,实现”表现层状态转化” -
GET http://www.baidu.com/api/user # 获取列表
POST http://www.baidu.com/api/user # 创建用户
PUT http://www.baidu.com/api/user/{id} # 修改用户信息
DELETE http://www.baidu.com/api/user/{id} # 删除用户信息 -
在前端传递参数:
工号为12345
$.ajax({ url:'/teacher/12345', method:'GET', success:function(res){ console.log(res); } })
在Node中获取参数:
app.get("/teacher/:gonghao",function(req,res){ res.send(" 老师工号 " + req.params.gonghao); });
前后端分离架构模式
- 分离原则
- 前端静态化
- 后端数据化
- 平台无关化
- 构架分离化
- 分离模式的优势
- 前后端流量大幅减少
- 表现性能的提高
- 前后端平台无关和技术无关
- 安全性方面的集中优化开发的分离和构架的分离
day16 - MongoDB
数据库(DataBase)
- 传统数据库:MySQL、SQL Server、Oracle【关系型数据库】
- 非关系型数据库:MongoDB、Redis、MemoryCache
非关系型数据库的应用场景
- 日志管理
- 无关联的数据信息
- 数据量特别大
传统数据库层级结构
- Database->Table->Row
MongoDB数据库层级结构
- Database->Collection->document
mongoDB默认端口号:
- 27017
常用命令
打开CMD->输入:mongo 回车:
show dbs【查看所有数据库名称列表以及所占硬盘空间】
use 数据库名【切换到指定的数据库中,如果不存在则创建并切换】
db【查看当前的数据库名称】
show tables/show collections 【查看当前数据库中所有集合】
db.createCollection(“集合名称”) 【创建一个空集合】
db.集合名称.insert({key1:value1,key2:value2}) 【给这个集合插入一条数据】
db.集合名称.insert([{key1:value1},{key2:value2}])【给这个集合插入多条数据】
db.集合名称.find() 【查看当前集合的全部数据】
db.集合名称.remove({key:value}) 【删除符合指定key-value条件的数据】
db.集合名称.drop() 【删除当前集合】
db.集合名称.update({筛选条件},{更新的内容},{multi:true})【不加第三个参数,只更新一条】
db.集合名称.update({筛选条件},{$set:{更新的内容}},{multi:true})【$set:只更新要修改的内容,其他内容不变,如果要更新的内容之前没有,则添加进去】
db.集合名称.update({筛选条件},{$unset:{更新的内容}}) 【$unset:删除某些属性】
db.student.find({age:{$lt:20}}) 【筛选age小于20的学生】
db.student.find({age:{$lte:20}}) 【筛选age小于等于20的学生】
db.student.find({age:{$gte:20}}) 【筛选age大于等于20的学生】
db.student.find({age:{$gt:20}}) 【筛选age大于20的学生】
db.student.find({age:{$ne:20}}) 【筛选出年龄不是20的学生】
db.student.find({$or:[{sex:’女’},{age:{$lte:20}}]}) 【筛选出性别是女的或者年龄小于等于20的学生】
db.student.findOne({筛选条件}) 【只查找一条数据】
导出
mongoexport -d 数据库名称 -c 集合名称 -o “E:\mongosql.json”
导入
mongoimport -d 数据库名称 -c 集合名称 —file “E:\mongosql.json” —type=json
mongoose模块使用步骤
-
安装
npm install mongoose@5.0.15
-
创建DB.js文件【负责连接数据库】
const mongoose = require('mongoose'); /** * 数据库连接 */ mongoose.connect('mongodb://127.0.0.1/student_manage'); mongoose.connection.on('connected',function(){ console.log('mongodb connected') }) mongoose.connection.on('error',function(){ console.log('mongodb error') }) mongoose.connection.on('disconnected',function(){ console.log('mongodb disconnected') }) module.exports = { mongoose }
-
创建数据库集合模型【Schema、Model】例如:Student.js
const db = require('./DB'); /** * 创建学生模型【规则】 */ var StudentSchema = db.mongoose.Schema({ name:{type:String,required:true}, age:{type:Number,min:1,max:200}, sex:{type:String,enum:['男','女']}, tel:{type:Number}, tid:{type:String} },{versionKey: false}) //这里的student ,对应数据库里面叫做 students的表 var Student = db.mongoose.model('student',StudentSchema) module.exports = Student;
-
通过导出的实例完成CRUD【增删改查】
添加:
Student.create({ name:'小刘', age:20, sex:'女', tel:1012031, tid:'12313' },function(err,data) //或 var s = new Student({ name:'陈小姐', age:22, sex:'女', tel:123123, tid:"123123" }) s.save(function(err,res){})
更新:
Student.create({ name:'小刘', age:20, sex:'女', tel:1012031, tid:'12313' },function(err,data) //或 var s = new Student({ name:'陈小姐', age:22, sex:'女', tel:123123, tid:"123123" }) s.save(function(err,res){})
删除:
Student.deleteMany({name:'petper'},function(err,res) //或 Student.findByIdAndRemove(id,function(err,res)
day17 - Monse高级应用
在mongoose里面查询
语法:
模型.find(查询条件,显示字段,回调函数)
例子:
//显示字段:1代表显示该字段,0代表不显示该字段
//res:是一个数组
Student.find({name:'jack'},{name:1,age:1},function(err,res))
//查询年龄在 21 到 65 之间的学员
Student.find({age: {$gte: 21, $lte: 65}}, callback)
//查询年龄不等于50的学员
Student.find({age: {$ne: 50}}, callback)
//查询名字中包含m的学员
Student.find({'name':{$regex:/m/i}}, function(err, res)
模型.count(查询条件,回调函数)
例子:
//res:返回查询结果的个数
Student.count({name:'jack'},function(err,res))
模型.findById(id值,回调函数)
例子:
//根据id查询唯一的记录
Student.findById('wdadw',function(err,res){})
分页查询:
例子:
var pageSize = 5; //一页多少条
var currentPage = 1; //当前第几页
var sort = {'logindate':-1}; //排序(1 升序,-1降序)
var condition = {}; //条件
var skipnum = (currentPage - 1) * pageSize; //跳过数
User.find(condition).skip(skipnum).limit(pageSize).sort(sort).exec(fn)
关联查询:
例子:
//学生表的tid关联了老师表的_id
Student.aggregate([
{
$lookup:{
from:'teachers',
localField:'tid',
foreignField:'_id',
as:'results'
}
}
]).exec(function(err,result){
if(err){
console.log(err);
}
console.log(result)
})
HTML页面->Node服务->MongoDB
MVC
M:Model【Student.js、Teacher.js】
V: View 【页面】
C: Controller 【控制器】:业务处理代码
day18 - 跨域与JWT
同源策略
协议名、域名、端口 三者都保持一致
跨域
协议名、域名、端口只要有一个不同,就是跨域请求
http://127.0.0.1:3000和http://127.0.0.2:3000
http://127.0.0.1:3000和http://127.0.0.1:3001
http://127.0.0.1:3000和ftp://127.0.0.1:3001
跨域什么时候会失败呢?
Ajax请求时才会失败
普通的HTML标签不会失败:
<form>、<img src="">、<script src="">、<iframe>
如何解决跨域失败问题呢?
JSONP
CORS【跨域资源共享】
Nginx【应用服务器】
JSONP跨域原理是什么?
本质不是ajax,它并没有使用XMLHttpRequest对象,用的是动态创建script标签
JSONP有啥缺点?
只支持GET方式、书写比较复杂
JSONP优点?
兼容性非常好
CORS优点
支持所有请求方式:GET、POST、PUT、DELETE
md5加密
const crypto = require('crypto')
var md5 = crypto.createHash('md5');
function MD5(content){
var str = md5.update(content).digest('hex')
return str;
}
var pass = MD5("123")
JWT(Json WebToken)
使用流程:
- 安装express-jwt和jsonwebtoken
- 设置一个用于生成token和验证token的KEY值
- express-jwt验证token是否正确
- 通过username和password生成口令,设置过期时间
- 将口令发给页面
- 页面获取token,然后将token保存到本地
- 发起任意请求的时候都要在http的头部添加token