├── ES6 ├── 01-ES5中的严格模式.html ├── day1 │ └── 文档 │ │ ├── 01-ES5中的严格模式.html │ │ ├── 01-ES5中的严格模式.md │ │ ├── 02-ES5中的一些扩展.md │ │ ├── 02-对象的扩展.html │ │ ├── 03-ES6变量.html │ │ ├── 03-ES6的介绍和环境配置.md │ │ ├── 04-ES6:变量.md │ │ ├── 04:函数扩展.html │ │ ├── 05-ES6:函数扩展.md │ │ ├── 06-ES6:promise、async等.md │ │ └── 07-ES6:字符串、数组、对象的扩展.md ├── day2 │ └── 文档 │ │ ├── 01-ES6:promise、async等.html │ │ ├── 02-ES6:promise、async等.html │ │ ├── 03-ES6-symbol.html │ │ ├── 04-ES6-async-await.html │ │ └── 05-ES6:迭代器(Iterator).html └── day3 │ └── 文档 │ ├── 01-ES6:迭代器(Iterator).html │ ├── 02-ES6代理Proxy.html │ └── 03-ES6模块封装代码.html ├── Express ├── day1 │ └── 文档 │ │ └── 1-Express介绍和安装.html ├── day3 │ └── 文档 │ │ ├── 1-获取GET参数.html │ │ ├── 2-获取POST参数.html │ │ └── 3-中间件.html ├── day4 │ └── 文档 │ │ ├── 1-Express介绍和安装.md │ │ ├── 1-使用cookie.html │ │ ├── 2-Cookie加密.html │ │ ├── 2-Express路由.md │ │ ├── 3-EJS模板.md │ │ ├── 3-session应用.html │ │ ├── 3-获取GET参数.md │ │ ├── 4-获取POST参数.md │ │ ├── 4sobooks登陆功能.html │ │ ├── 5-中间件.md │ │ ├── 6-使用cookie.md │ │ ├── 7-Cookie加密.md │ │ ├── 8-session应用.md │ │ └── sobooks登陆功能.md └── day5 │ └── 文档 │ ├── 11-文件上传.md │ ├── 12-文件下载.md │ ├── 13-AJAX上传图片.md │ ├── 1sobook注册.html │ ├── 2-文件上传.html │ ├── 3-AJAX上传图片.html │ ├── 4-文件下载.html │ └── sobook注册.md ├── Node ├── day1 │ └── 文档 │ │ ├── 01.学习Node能够为我们带来什么.html │ │ ├── 02.Node.js简介.html │ │ ├── 03.Node.js安装和简单使用.html │ │ ├── 04.使用Nodemon自动重启项目.html │ │ ├── 05.Node.js模块化理解.html │ │ ├── 06.Node.js中module.exports和exports的区别.html │ │ ├── 07.Node中require加载第三方包的规则.html │ │ ├── 08.npm常见命令.html │ │ └── 09.Node中核心fs模块.html ├── day2 │ └── 文档 │ │ ├── 01-Node.js Stream(流).html │ │ └── 02-Node事件.html ├── day3 │ └── 文档 │ │ └── 01-cherrio.html ├── day4 │ └── 文档 │ │ └── 01Puppeteer.html ├── day5 │ └── 文档 │ │ ├── 1-网络通信原理.html │ │ ├── 2-HTTP协议.html │ │ └── 3-http模块.html ├── day6 │ └── 文档 │ │ ├── 2-爬虫总结.html │ │ ├── 3-http静态服务器实现.html │ │ ├── 4-根据数据和模板动态生成页面.html │ │ └── async_promise.html ├── day7 │ ├── 1-根据数据和模板动态生成页面.md │ ├── 2-列表的动态渲染.md │ ├── 3-正则路由的设定.md │ ├── 4-梳理框架流程.md │ └── 文档 │ │ ├── 1-列表的动态渲染.html │ │ └── 2-梳理框架流程.html └── day8 │ └── 文档 │ ├── 1node总结.md │ └── 2npm上传包.md ├── README.md ├── jQuery ├── day1 │ └── 文档 │ │ ├── 01-jQuery的介绍和选择器.html │ │ └── 02-jQuery操作DOM.html ├── day2 │ └── 文档 │ │ ├── 01-jQuery操作DOM.html │ │ ├── 02-jQuery动画详解.html │ │ └── 03-jQuery的事件机制和其他知识.html └── day3 │ └── 文档 │ └── 01-jQuery的事件机制和其他知识.html ├── mysql ├── day1 │ ├── Node连接MySQL.md │ └── 数据库.md ├── day2 │ └── 文档 │ │ ├── 1-数据库基本操作.html │ │ ├── 2-查询基本操作.html │ │ ├── 3-Node连接MySQL.html │ │ ├── 4-SQL基本操作.html │ │ ├── 5-SQL排序.html │ │ ├── 6-聚合.html │ │ ├── 7-分组.html │ │ └── 8-分页.html ├── day3 │ └── 文档 │ │ ├── 1-范式.html │ │ ├── 2-连接查询.html │ │ └── 3-子查询.html └── day4 │ └── 文档 │ ├── 1-范式.html │ ├── 1猫眼爬虫.md │ ├── 2-连接查询.html │ ├── 3-子查询.html │ ├── 3-视图.html │ └── 4-MySQL更新和删除.html └── 项目 ├── day1 └── 文档 │ └── 1项目需求.md ├── day2 └── 文档 │ └── 2-1个人信息页.md ├── day4 └── 文档 │ ├── 1权限管理.md │ └── 文档 │ └── 1权限管理.md └── day5 ├── 1-SVN.md └── 1项目需求.docx /ES6/day1/文档/01-ES5中的严格模式.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ## ES的几个重要版本 5 | 6 | - ES5 : 09年发布。 7 | 8 | - ES6(ES2015) : 2015年发布,也称为ECMA2015。 9 | 10 | - ES7(ES2016) : 2016年发布,也称为ECMA2016 (变化不大)。 11 | 12 | 13 | ## 严格模式的理解 14 | 15 | ### 概念 16 | 17 | **理解**:除了正常运行模式(混杂模式),ES5添加了第二种运行模式:"严格模式"(strict mode)。 18 | 19 | 顾名思义,这种模式使得Javascript在更严格的语法条件下运行。 20 | 21 | **目的**: 22 | 23 | - 消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为 24 | 25 | - 消除代码运行的一些不安全之处,为代码的安全运行保驾护航 26 | 27 | - 为未来新版本的Javascript做好铺垫 28 | 29 | ### 使用 30 | 31 | - 针对整个脚本文件:将`use strict`放在脚本文件的第一行,则整个脚本文件将以严格模式运行。 32 | 33 | - 针对单个函数:将`use strict`放在函数体的第一行,则整个函数以严格模式运行。 34 | 35 | PS:如果浏览器不支持,则这句话只解析为一条简单的语句, 没有任何副作用。 36 | 37 | 脚本文件的变通写法:因为第一种调用方法不利于文件合并,所以更好的做法是,借用第二种方法,将整个脚本文件放在一个立即执行的匿名函数之中。 38 | 39 | ### 语法和行为改变 40 | 41 | - 必须用var声明变量 42 | 43 | - 禁止自定义的函数中的this指向window 44 | 45 | - 对象不能有重名的属性 46 | 47 | 48 | ## 严格模式和普通模式的区别 49 | 50 | > 下面列举几条严格模式的内容。 51 | 52 | ### 全局变量显式声明 53 | 54 | 在正常模式中,如果一个变量没有声明就赋值,默认是全局变量。严格模式禁止这种用法,全局变量必须显式声明。 55 | 56 | 57 | ### 禁止this关键字指向全局对象: 58 | 59 | ```javascript 60 | var foo = function () { 61 | console.log(this); 62 | } 63 | 64 | foo(); 65 | ``` 66 | 上方代码中,普通模式打印的是window。严格模式下打印的是undefined。 67 | 68 | 69 | ### 构造函数必须通过new实例化对象 70 | 71 | 构造函数必须通过new实例化对象,否则报错。因为this为undefined,此时无法设置属性。 72 | 73 | 比如说: 74 | 75 | 76 | ``` 77 | var Cat = function (name) { 78 | this.name = name; 79 | } 80 | 81 | Cat('haha'); 82 | ``` 83 | 84 | 上方代码中,如果在严格模式下,则会报错。 85 | 86 | 87 | ### 属性相关 88 | 89 | 普通模式下,如果对象有多个重名属性,最后赋值的那个属性会覆盖前面的值。严格模式下,这属于语法错误。 90 | 91 | 92 | 普通模式下,如果函数有多个重名的参数,可以用arguments[i]读取。严格模式下,多个重名的参数属于语法错误。 93 | 94 | 95 | 比如下面这样的代码: 96 | 97 | ```javascript 98 | var obj = { 99 | username: 'smyh'; 100 | username: 'vae' 101 | } 102 | ``` 103 | 104 | 上面的代码,在严格模式下属于语法错误,因为有重名的属性。 105 | 106 | ### 函数必须声明在顶层 107 | 108 | 109 | 将来Javascript的新版本会引入"块级作用域"。为了与新版本接轨,严格模式只允许在全局作用域或函数作用域的顶层声明函数。也就是说,不允许在非函数的代码块内声明函数。 110 | 111 | ### 新增关键字 112 | 113 | 为了向将来Javascript的新版本过渡,严格模式新增了一些保留字:implements, interface, let, package, private, protected, public, static, yield。 114 | 115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /ES6/day1/文档/02-ES5中的一些扩展.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## JSON 对象 4 | 5 | 1、js对象(数组) --> json对象(数组): 6 | 7 | ```javascript 8 | JSON.stringify(obj/arr) 9 | ``` 10 | 11 | 2、json对象(数组) --> js对象(数组): 12 | 13 | 14 | ```javascript 15 | JSON.parse(json) 16 | ``` 17 | 18 | 19 | 上面这两个方法是ES5中提供的。 20 | 21 | 我们要记住,我们通常说的“json字符串”,只有两种:**json对象、json数组**。 22 | 23 | `typeof json字符串`的返回结果是string。 24 | 25 | ## Object的扩展 26 | 27 | ES5给Object扩展了一些静态方法,常用的有2个,我们接下来讲解。 28 | 29 | 30 | ### 方法一 31 | 32 | ```javascript 33 | Object.create(prototype, [descriptors]) 34 | ``` 35 | 36 | 作用: 以指定对象为原型,创建新的对象。同时,第二个参数可以为为新的对象添加新的属性,并对此属性进行描述。 37 | 38 | **举例1**:(没有第二个参数时) 39 | 40 | ```javascript 41 | var obj1 = {username: 'smyhvae', age: 26}; 42 | var obj2 = {address:'shenzhen'}; 43 | 44 | obj2 = Object.create(obj1); 45 | console.log(obj2); 46 | ``` 47 | 48 | 打印结果: 49 | 50 | ![](http://img.smyhvae.com/20180401_2150.png) 51 | 52 | 我们发现,obj1成为了obj2的原型。 53 | 54 | **举例2**:(有第二个参数时) 55 | 56 | 第二个参数可以给新的对象添加新的属性。我们修改上面的代码,尝试给obj2添加新属性`sex`: 57 | 58 | ```javascript 59 | var obj1 = {username: 'smyhvae', age: 26}; 60 | var obj2 = {address: 'shenzhen'}; 61 | 62 | obj2 = Object.create(obj1, { 63 | sex: {//给obj2添加新的属性`sex`。注意,这一行的冒号不要漏掉 64 | value: '男', //通过value关键字设置sex的属性值 65 | writable: false, 66 | configurable: true, 67 | enumerable: true 68 | } 69 | }); 70 | 71 | console.log(obj2); 72 | 73 | ``` 74 | 75 | 上方代码中,我们通过第5行的sex给obj2设置了一个新的属性`sex`,但是要通过`value`来设置属性值(第6行)。 76 | 77 | 设置完属性值后,这个属性值默认是不可修改的,要通过`writable`来设置。总而言之,这几个关键字的解释如下: 78 | 79 | - `value`:设置属性值。 80 | - `writable`:标识当前属性值是否可修改。如果不写的话,默认为false,不可修改。 81 | - `configurable`:标识当前属性是否可以被删除。默认为false,不可删除。 82 | - `enumerable`:标识当前属性是否能用 for in 枚举。 默认为false,不可。 83 | 84 | #### 单独设置属性 85 | 86 | ``` 87 | Object.defineProperty(obj2, 'sex', { 88 | value: 'cc', 89 | writable: true, 90 | configurable: true, 91 | enumerable: true, 92 | }); 93 | ``` 94 | 95 | 96 | 97 | ### 方法二 98 | 99 | > 这个方法有点难理解。 100 | 101 | 102 | ```javascript 103 | Object.defineProperties(object, descriptors) 104 | ``` 105 | 106 | **作用**:为指定对象定义扩展多个属性。 107 | 108 | 代码举例: 109 | 110 | 111 | ```javascript 112 | var obj2 = { 113 | firstName : 'smyh', 114 | lastName : 'vae' 115 | }; 116 | Object.defineProperties(obj2, { 117 | fullName : { 118 | get : function () { 119 | return this.firstName + '-' + this.lastName 120 | }, 121 | set : function (data) { //监听扩展属性,当扩展属性发生变化的时候自动调用,自动调用后将变化的值作为实参注入到set函数 122 | var names = data.split('-'); 123 | this.firstName = names[0]; 124 | this.lastName = names[1]; 125 | } 126 | } 127 | }); 128 | console.log(obj2.fullName); 129 | obj2.firstName = 'tim'; 130 | obj2.lastName = 'duncan'; 131 | console.log(obj2.fullName); 132 | obj2.fullName = 'kobe-bryant'; 133 | console.log(obj2.fullName); 134 | ``` 135 | 136 | - get :用来获取当前属性值的回调函数 137 | 138 | - set :修改当前属性值得触发的回调函数,并且实参即为修改后的值 139 | 140 | 存取器属性:setter,getter一个用来存值,一个用来取值。 141 | 142 | ## Object的扩展(二) 143 | 144 | obj对象本身就自带了两个方法。格式如下: 145 | 146 | 147 | ```javascript 148 | get 属性名(){} 用来得到当前属性值的回调函数 149 | 150 | set 属性名(){} 用来监视当前属性值变化的回调函数 151 | 152 | ``` 153 | 154 | 举例如下: 155 | 156 | 157 | 158 | ```javascript 159 | var obj = { 160 | firstName : 'kobe', 161 | lastName : 'bryant', 162 | get fullName(){ 163 | return this.firstName + ' ' + this.lastName 164 | }, 165 | set fullName(data){ 166 | var names = data.split(' '); 167 | this.firstName = names[0]; 168 | this.lastName = names[1]; 169 | } 170 | }; 171 | console.log(obj.fullName); 172 | obj.fullName = 'curry stephen'; 173 | console.log(obj.fullName); 174 | ``` 175 | 176 | 177 | ## 数组的扩展 178 | 179 | > 下面讲的这几个方法,都是给数组的实例用的。 180 | 181 | > 下面提到的数组的这五个方法,更详细的内容,可以看《03-JavaScript基础/15-数组的常见方法.md》 182 | 183 | **方法1**: 184 | 185 | 186 | ```javascript 187 | Array.prototype.indexOf(value) 188 | ``` 189 | 190 | 作用:获取 value 在数组中的第一个下标。 191 | 192 | **方法2**: 193 | 194 | 195 | ```javascript 196 | Array.prototype.lastIndexOf(value) 197 | ``` 198 | 199 | 作用:获取 value 在数组中的最后一个下标。 200 | 201 | **方法3**:遍历数组 202 | 203 | 204 | ```javascript 205 | Array.prototype.forEach(function(item, index){}) 206 | ``` 207 | 208 | 209 | **方法4**: 210 | 211 | ```javascript 212 | Array.prototype.map(function(item, index){}) 213 | ``` 214 | 215 | 作用:遍历数组返回一个新的数组,返回的是**加工之后**的新数组。 216 | 217 | 218 | **方法5**: 219 | 220 | ```javascript 221 | Array.prototype.filter(function(item, index){}) 222 | ``` 223 | 224 | 作用:遍历过滤出一个新的子数组,返回条件为true的值。 225 | 226 | ## 函数function的扩展:bind() 227 | 228 | > ES5中新增了`bind()`函数来改变this的指向。 229 | 230 | 231 | ```javascript 232 | Function.prototype.bind(obj) 233 | ``` 234 | 235 | 作用:将函数内的this绑定为obj, 并将函数返回。 236 | 237 | **面试题**: call()、apply()和bind()的区别: 238 | 239 | - 都能改变this的指向 240 | 241 | - call()/apply()是**立即调用函数** 242 | 243 | - bind():绑定完this后,不会立即调用当前函数,而是**将函数返回**,因此后面还需要再加`()`才能调用。 244 | 245 | PS:bind()传参的方式和call()一样。 246 | 247 | **分析**: 248 | 249 | 为什么ES5中要加入bind()方法来改变this的指向呢?因为bind()不会立即调用当前函数。 250 | 251 | bind()通常使用在回调函数中,因为回调函数并不会立即调用。如果你希望在回调函数中改变this,不妨使用bind()。 252 | 253 | -------------------------------------------------------------------------------- /ES6/day1/文档/03-ES6的介绍和环境配置.md: -------------------------------------------------------------------------------- 1 | ### 前言 2 | 3 | ECMAScript 是 JS 的语言标准。而 ES6 是新的 JS 语法标准。 4 | 5 | PS:严格来说,ECMAScript 还包括其他很多语言的语言标准。 6 | 7 | 很多人在做业务选型的时候,会倾向于选jQuery。其实jQuery的语法是偏向于ES3的。而现在主流的框架 Vue.js 和React.js的语法,是用的ES6。 8 | 9 | ES6中增加了很多功能上的不足。比如:**常量、作用域、对象代理、异步处理、类、继承**等。这些在ES5中想实现,比较复杂,但是ES6对它们进行了封装。 10 | 11 | ### ECMAScript 发展历史 12 | 13 | - 1995年:ECMAScript 诞生。 14 | 15 | - 1997年:ECMAScript 标准确立。 16 | 17 | - 1999年:ES3 出现,与此同时,IE5 风靡一时。 18 | 19 | - 2009年,ES5 出现,例如 foreach、Object.keys、Object.create 和 json 标准。 20 | 21 | - 2015年6月,ES6正式发布。 22 | 23 | ES6 的目标是:让 JS 语言可以编写复杂的大型应用程序,成为企业级开发语言。 24 | 25 | ### ECMAScript 的各大版本 26 | 27 | - ES5 : 09年发布。 28 | 29 | - ES6:ECMAScript 2015年6月 30 | 31 | - ES7:ECMAScript 2016 32 | 33 | - ES8:ECMAScript 2017 34 | 35 | ### ES6 的其他优势 36 | 37 | - 使用 babel 语法转换器,支持低端浏览器。 38 | 39 | - 流行的库基本都是基于 ES6 构建。 React 默认使用 ES6 标准开发。 40 | 41 | ## ES6的环境配置(为了兼容 ES5) 42 | 43 | 掌握 ES6 之后,如果要考虑 ES5 的兼容性,可以这样做:写 ES6 语法的 js 代码,然后通过 `Babel`将 ES6 转换为 ES5。 44 | 45 | 但是,在这之前,我们需要配置一下相关的环境。 46 | 47 | ### 建立工程目录 48 | 49 | (1)先建立一个空的工程目录 `ES6Demo`,并在目录下建立两个文件夹 `src`和 `dist`: 50 | 51 | - `src`:书写ES6代码,我们写的 js 程序都放在这里。 52 | 53 | - `dist`:利用 Babel 编译生成的 ES5 代码。**我们在 HTML 页面需要引入 dist 里的 js 文件**。 54 | 55 | (2)在 src 里新建文件 `index.html`: 56 | 57 | ```html 58 | 59 | 60 | 61 | 62 | 63 | 64 | Document 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | ``` 73 | 74 | **注意**,上方代码中,我们引入的是`dist`目录下的 js 文件。 75 | 76 | 然后我们新建文件 `src/index.js`: 77 | 78 | ```javascript 79 | let a = 'smyhvae'; 80 | const b = 'qianguyihao'; 81 | 82 | console.log(a); 83 | console.log(b); 84 | ``` 85 | 86 | 87 | 这个文件是一个 ES6语法 的js文件,稍后,我们尝试把这个 ES6 语法的 js 文件转化为 ES5 的 js 文件。 88 | 89 | PS:我们在写代码时,能用单引号尽量用单引号,而不是双引号,前者在压缩之后,程序执行会更快。 90 | 91 | ### 全局安装 Babel-cli 92 | 93 | (1)初始化项目: 94 | 95 | 在安装Babel之前,需要先用 npm init 先初始化我们的项目。打开终端或者通过cmd打开命令行工具,进入项目目录,输入如下命令: 96 | 97 | 98 | ```bash 99 | npm init -y 100 | ``` 101 | 102 | 上方代码中,`-y` 代表全部默认同意,就不用一次次按回车了(稍后再根据需要,在文件中手动修改)。命令执行完成后,会在项目的根目录下生成package.json文件: 103 | 104 | ```json 105 | { 106 | "name": "es6demo", 107 | "version": "1.0.0", 108 | "description": "", 109 | "main": "index.js", 110 | "scripts": { 111 | "test": "echo \"Error: no test specified\" && exit 1" 112 | }, 113 | "author": "smyhvae", 114 | "license": "ISC" 115 | } 116 | 117 | ``` 118 | 119 | 120 | PS:VS Code 里打开终端的快捷键是:`Contol + ~`。 121 | 122 | (2)全局安装 Babel-cli: 123 | 124 | 在终端中输入以下命令: 125 | 126 | ```bash 127 | npm install -g babel-cli 128 | ``` 129 | 130 | 131 | ![](http://img.smyhvae.com/20180304_1305.png) 132 | 133 | 如果安装比较慢的话,Mac 下可以使用`cnpm`进行安装 ,windows 下可以使用`nrm`切换到 taobao 的镜像。 134 | 135 | 136 | (3)本地安装 babel-preset-es2015 和 babel-cli: 137 | 138 | ```bash 139 | npm install --save-dev babel-preset-es2015 babel-cli 140 | ``` 141 | 142 | ![](http://img.smyhvae.com/20180304_1307.png) 143 | 144 | 安装完成后,会发现`package.json`文件,已经多了 devDependencies 选项: 145 | 146 | ![](http://img.smyhvae.com/20180304_1308.png) 147 | 148 | (4)新建.babelrc: 149 | 150 | 在根目录下新建文件`.babelrc`,输入如下内容: 151 | 152 | ``` 153 | { 154 | "presets":[ 155 | "es2015" 156 | ], 157 | "plugins":[] 158 | } 159 | ``` 160 | 161 | 162 | (5)开始转换: 163 | 164 | 现在,我们应该可以将 ES6 的文件转化为 ES5 的文件了,命令如下:(此命令略显复杂) 165 | 166 | ``` 167 | babel src/index.js -o dist/index.js 168 | ``` 169 | 170 | 我们可以将上面这个命令进行简化一下。操作如下: 171 | 172 | 在文件 `package.json` 中修改键 `scripts`中的内容: 173 | 174 | 175 | ```json 176 | "scripts": { 177 | "build": "babel src/index.js -o dist/index.js" 178 | }, 179 | ``` 180 | 181 | 修改后的效果如下: 182 | 183 | ![](http://img.smyhvae.com/20180304_1315.png) 184 | 185 | 目前为止,环境配置好了。以后,我们执行如下命令,即可将`src/index.js`这个 ES6 文件转化为 `dist/index.js`这个 ES5 文件: 186 | 187 | 188 | ```bash 189 | npm run build 190 | ``` 191 | 192 | 193 | 我们执行上面的命令之后,会发现, dist目录下会生成 ES5 的 js 文件: 194 | 195 | index.js: 196 | 197 | ```javascript 198 | 'use strict'; 199 | 200 | var a = 'smyhvae'; 201 | var b = 'qianguyihao'; 202 | 203 | console.log(a); 204 | console.log(b); 205 | 206 | ``` 207 | 208 | 当我们打开网页后,就可以在浏览器的控制台,看到代码的输出结果。 209 | 210 | 211 | 212 | -------------------------------------------------------------------------------- /ES6/day1/文档/04-ES6:变量.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## ES6 的变量声明 4 | 5 | ES6 中新增了 let 和 const 来定义变量: 6 | 7 | - `var`:ES5 和 ES6中,定义**全局变量**(是variable的简写)。 8 | 9 | - `let`:定义**局部变量**,替代 var。 10 | 11 | - `const`:定义**常量**(定义后,不可修改)。 12 | 13 | ### var:全局变量 14 | 15 | 看下面的代码: 16 | 17 | ```javascript 18 | { 19 | var a = 1; 20 | } 21 | 22 | console.log(a); //这里的 a,指的是 区块 里的 a 23 | ``` 24 | 25 | 上方代码是可以输出结果的,输出结果为 1。因为 var 是全局声明的,所以,即使是在区块里声明,但仍然在全局起作用。 26 | 27 | 再来看下面这段代码: 28 | 29 | ```javascript 30 | var a = 1; 31 | { 32 | var a = 2; 33 | } 34 | 35 | console.log(a); //这里的 a,指的是 区块 里的 a 36 | ``` 37 | 38 | 上方代码的输出结果为 2 ,因为 var 是全局声明的。 39 | 40 | **总结:** 41 | 42 | 用 var 定义的全部变量,有时候会污染整个 js 的作用域。 43 | 44 | ### let:定义局部变量 45 | 46 | ```javascript 47 | var a = 2; 48 | { 49 | let a = 3; 50 | } 51 | 52 | console.log(a); 53 | ``` 54 | 55 | 上方代码的输出结果为 2。用 let 声明的变量,只在局部(**块级作用域内**)起作用。 56 | 57 | let是防止数据污染,我们来看下面这个 **for 循环**的例子,很经典。 58 | 59 | 1、用 var 声明变量:() 60 | 61 | ```javascript 62 | for (var i = 0; i < 10; i++) { 63 | console.log('循环体中:' + i); // 每循环一次,就会在 { } 所在的块级作用域中,重新定义一个新的 i 64 | } 65 | 66 | console.log('循环体外:' + i); 67 | ``` 68 | 69 | 上方代码可以正常打印结果,且最后一行的打印结果是10。说明循环体外定义的变量 i,是在全局起作用的。 70 | 71 | 2、用let声明变量: 72 | 73 | ```javascript 74 | for (let i = 0; i < 10; i++) { 75 | console.log('循环体中:' + i); 76 | } 77 | 78 | console.log('循环体外:' + i); 79 | 80 | ``` 81 | 82 | 上方代码的最后一行无法打印结果,也就是说打印会报错。因为用 let 定义的变量 i,只在`{ }`这个**块级作用域**里生效。 83 | 84 | **总结:**我们要习惯用 let 声明,减少var声明带来的**污染全局空间**。 85 | 86 | 为了进一步说明 let 不会带来污染,需要说明的是:当我们定义了`let a = 1`时,如果我们在同一个作用域内继续定义`let a = 2`,是会报错的。 87 | 88 | ### const:定义常量 89 | 90 | 在程序开发中,有些变量是希望声明后,在业务层就不再发生变化,此时可以用 const 来定义。 91 | 92 | 举例: 93 | 94 | ```javascript 95 | const name = 'smyhvae'; //定义常量 96 | ``` 97 | 98 | 用 const 声明的变量,只在局部(块级作用域内)起作用。 99 | 100 | ### let 和 const 的作用【重要】 101 | 102 | let 和 const 的作用如下: 103 | 104 | - 禁止重复声明 105 | 106 | - 支持块级作用域 107 | 108 | - 控制修改 109 | 110 | 相反, 用`var`声明的变量:可以重复声明、**没有块级作用域**、不能限制。 111 | 112 | ### for循环举例【经典案例】 113 | 114 | **代码1**、我们先来看看如下代码:(用 var 定义变量 i) 115 | 116 | ```html 117 | 118 | 119 | 120 | 121 | 122 | 123 | Document 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 140 | 141 | 142 | ``` 143 | 144 | 上方代码中的运行效果如下: 145 | 146 | ![](http://img.smyhvae.com/20190904_1030.gif) 147 | 148 | 你可能会感到诧异,为何点击任何一个按钮,弹出的内容都是4呢?这是因为,我们用 var 定义的变量 i,是在全局作用域声明的。整个代码中,自始至终只有一个变量。当我们还没点击按钮之前,变量 i 已经循环到4了。 149 | 150 | 也就是说,上面的 for 循环,相当于如下代码: 151 | 152 | ```javascript 153 | var i = 0; 154 | myBtn[0].onclick = function() { 155 | alert(i); 156 | }; 157 | i++; 158 | 159 | myBtn[1].onclick = function() { 160 | alert(i); 161 | }; 162 | i++; 163 | 164 | myBtn[2].onclick = function() { 165 | alert(i); 166 | }; 167 | i++; 168 | 169 | myBtn[3].onclick = function() { 170 | alert(i); 171 | }; 172 | i++; // 到这里,i 的值已经是4了。因此,当我们点击按钮时,i的值一直都是4 173 | ``` 174 | 175 | 176 | **代码2**、上面的代码中,如果我们改为用 let 定义变量 i: 177 | 178 | ```html 179 | 180 | 181 | 182 | 183 | 184 | 185 | Document 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 202 | 203 | 204 | ``` 205 | 206 | 上方代码中的运行效果如下: 207 | 208 | ![](http://img.smyhvae.com/20190904_1040.gif) 209 | 210 | 上面这个运行结果,才是我们预期的效果。我们用 let 定义变量 i,在循环的过程中,每执行一次循环体,就会诞生一个新的 i。循环体执行4次,就会有四个 i。 211 | 212 | ## 变量的解构赋值 213 | 214 | ES6允许我们,通过数组或者对象的方式,对一组变量进行赋值,这被称为解构。 215 | 216 | 解构赋值在实际开发中可以大量减少我们的代码量,并且让程序结构更清晰。 217 | 218 | ### 数组的解构赋值 219 | 220 | **举例:** 221 | 222 | 通常情况下,我们在为一组变量赋值时,一般是这样写: 223 | 224 | 225 | ```javascript 226 | let a = 0; 227 | let b = 1; 228 | let c = 2; 229 | 230 | ``` 231 | 232 | 现在我们可以通过数组解构的方式进行赋值: 233 | 234 | ```javascript 235 | let [a, b, c] = [1, 2, 3]; 236 | ``` 237 | 238 | 二者的效果是一样的。 239 | 240 | **解构的默认值:** 241 | 242 | 在解构赋值时,是允许使用默认值的。举例如下: 243 | 244 | 245 | ```javascript 246 | { 247 | //一个变量时 248 | let [foo = true] = []; 249 | console.log(foo); //输出结果:true 250 | } 251 | 252 | { 253 | //两个变量时 254 | let [a, b] = ['生命壹号'] //a 赋值为:生命壹号。b没有赋值 255 | console.log(a + ',' + b); //输出结果:生命壹号,undefined 256 | } 257 | 258 | 259 | { 260 | //两个变量时 261 | let [a, b = 'smyhvae'] = ['生命壹号'] //a 赋值为:生命壹号。b 采用默认值 smyhvae 262 | console.log(a + ',' + b); //输出结果:生命壹号,smyhvae 263 | } 264 | 265 | ``` 266 | 267 | `undefined`和`null`的区别: 268 | 269 | 如果我们在赋值时,采用的是 `undefined`或者`null`,那会有什么区别呢? 270 | 271 | 272 | 273 | ```javascript 274 | { 275 | let [a, b = 'smyhvae'] = ['生命壹号', undefined]; //b 虽然被赋值为 undefined,但是 b 会采用默认值 276 | console.log(a + ',' + b); //输出结果:生命壹号,smyhvae 277 | } 278 | 279 | { 280 | let [a, b = 'smyhvae'] = ['生命壹号', null]; //b 被赋值为 null 281 | console.log(a + ',' + b); //输出结果:生命壹号,null 282 | } 283 | 284 | 285 | ``` 286 | 287 | 上方代码分析: 288 | 289 | - undefined:相当于什么都没有,此时 b 采用默认值。 290 | 291 | - null:相当于有值,但值为 null。 292 | 293 | ### 对象的解构赋值 294 | 295 | 通常情况下,我们从接口拿到json数据后,一般这么赋值: 296 | 297 | ```javascript 298 | var a = json.a; 299 | 300 | var b = json.b; 301 | 302 | bar c = json.c; 303 | ``` 304 | 305 | 上面这样写,过于麻烦了。 306 | 307 | 现在,我们同样可以针对对象,进行结构赋值。 308 | 309 | **举例如下:** 310 | 311 | ``` 312 | let { foo, bar } = { bar: '我是 bar 的值', foo: '我是 foo 的值' }; 313 | console.log(foo + ',' + bar); //输出结果:我是键 foo 的值,我是键 bar 的值 314 | 315 | ``` 316 | 317 | 上方代码可以看出,对象的解构与数组的结构,有一个重要的区别:**数组**的元素是按次序排列的,变量的取值由它的**位置**决定;而**对象的属性没有次序**,是**根据键来取值**的。 318 | 319 | **圆括号的使用**: 320 | 321 | 如果变量 foo 在解构之前就已经定义了,此时你再去解构,就会出现问题。下面是错误的代码,编译会报错: 322 | 323 | 324 | ```javascript 325 | let foo = 'haha'; 326 | { foo } = { foo: 'smyhvae' }; 327 | console.log(foo); 328 | 329 | ``` 330 | 331 | 要解决报错,只要在解构的语句外边,加一个圆括号即可: 332 | 333 | 334 | ```javascript 335 | let foo = 'haha'; 336 | ({ foo } = { foo: 'smyhvae' }); 337 | console.log(foo); //输出结果:smyhvae 338 | 339 | ``` 340 | 341 | ### 字符串解构 342 | 343 | 字符串也可以解构,这是因为,此时字符串被转换成了一个类似数组的对象。举例如下: 344 | 345 | ```javascript 346 | const [a, b, c, d] = 'smyhvae'; 347 | console.log(a); 348 | console.log(b); 349 | console.log(c); 350 | console.log(d); 351 | 352 | console.log(typeof a); //输出结果:string 353 | ``` 354 | 355 | 输出结果: 356 | 357 | ![](http://img.smyhvae.com/20180304_1626.png) 358 | 359 | ## for ... of 循环 360 | 361 | ES6 中,如果我们要遍历一个数组,可以这样做: 362 | 363 | ``` 364 | let arr1 = [1, 2, 3, 4, 5]; 365 | 366 | for (let value of arr1) { 367 | console.log(value); 368 | } 369 | ``` 370 | 371 | 输出结果: 372 | 373 | ![](http://img.smyhvae.com/20180304_2016.png) 374 | 375 | for…of 的循环可以避免我们开拓内存空间,增加代码运行效率,所以建议大家在以后的工作中使用for…of循环。 376 | 377 | 注意,上面的数组中,`for ... of`获取的是数组里的值;`for ... in`获取的是index索引值。 378 | 379 | ### Map对象的遍历 380 | 381 | `for ... of`既可以遍历数组,也可以遍历Map对象。 382 | 383 | ## 模板字符串 384 | 385 | 我们以前让字符串进行拼接的时候,是这样做的:(传统写法的字符串拼接) 386 | 387 | ```javascript 388 | var name = 'smyhvae'; 389 | var age = '26'; 390 | console.log('name:'+name+',age:'+age); //传统写法 391 | ``` 392 | 393 | 394 | 这种写法,比较繁琐,而且容易出错。 395 | 396 | 现在有了 ES6 语法,字符串拼接可以这样写: 397 | 398 | ```javascript 399 | var name = 'smyhvae'; 400 | var age = '26'; 401 | 402 | console.log('name:'+name+',age:'+age); //传统写法 403 | 404 | console.log(`name:${name},age:${age}`); //ES6 写法 405 | 406 | ``` 407 | 408 | **注意**,上方代码中,倒数第二行用的符号是单引号,最后一行用的符号是反引号(在tab键的上方)。 409 | 410 | 411 | 412 | 413 | -------------------------------------------------------------------------------- /ES6/day1/文档/05-ES6:函数扩展.md: -------------------------------------------------------------------------------- 1 | 2 | ## 前言 3 | 4 | ES6在**函数扩展**方面,新增了很多特性。例如: 5 | 6 | - 箭头函数 7 | 8 | - 参数默认值 9 | 10 | - 参数结构赋值 11 | 12 | - 扩展运算符 13 | 14 | - rest参数 15 | 16 | - this绑定 17 | 18 | - 尾调用 19 | 20 | ## 箭头函数 21 | 22 | 定义和调用函数:(传统写法) 23 | 24 | ```javascript 25 | function fn1(a, b) { 26 | return a + b; 27 | } 28 | 29 | console.log(fn1(1, 2)); //输出结果:3 30 | ``` 31 | 32 | 定义和调用函数:(ES6中的写法) 33 | 34 | ```javascript 35 | 36 | var fn2 = (a, b) => a + b; 37 | 38 | console.log(fn2(1, 2)); //输出结果:3 39 | 40 | ``` 41 | 42 | 二者的效果是一样的。 43 | 44 | 在箭头函数中,如果方法体内有两句话,那就需要在方法体外边加上{}括号。如下: 45 | 46 | ```javascript 47 | var fn2 = (a, b) => { 48 | console.log('haha'); 49 | return a + b; 50 | }; 51 | console.log(fn2(1, 2)); //输出结果:3 52 | 53 | ``` 54 | 55 | 从上面的箭头函数中,我们可以很清晰地找到函数名、参数名、方法体。 56 | 57 | 上方代码中: 58 | 59 | - 如果有且仅有1个参数,则`()`可以省略 60 | 61 | - 如果方法体内有且仅有1条语句,则`{}`可以省略,但前提是,这条语句必须是 return。 62 | 63 | ### this的指向 64 | 65 | > 箭头函数只是为了让函数写起来更优雅吗?当然不是,还有一个很大的作用是与this的指向有关。 66 | 67 | ES5 中,this指向的是函数被调用的对象;而 ES6 的箭头函数中,this指向的是函数被定义时。 68 | 69 | 简单来说,箭头函数中的this,是不会变的,是永远绑定在当前的环境下。 70 | 71 | ## 参数默认值 72 | 73 | **传统写法**: 74 | 75 | ```javascript 76 | function fn(param) { 77 | let p = param || 'hello'; 78 | console.log(p); 79 | } 80 | ``` 81 | 82 | 上方代码中,函数体内的写法是:如果 param 不存在,就用 `hello`字符串做兜底。这样写比较啰嗦。 83 | 84 | **ES6 写法**:(参数默认值的写法,很简洁) 85 | 86 | ```javascript 87 | function fn(param = 'hello') { 88 | console.log(param); 89 | } 90 | ``` 91 | 92 | 93 | 在 ES6 中定义方法时,我们可以给方法里的参数加一个**默认值**(缺省值): 94 | 95 | - 方法被调用时,如果没有给参数赋值,那就是用默认值; 96 | 97 | - 方法被调用时,如果给参数赋值了新的值,那就用新的值。 98 | 99 | 如下: 100 | 101 | ```javascript 102 | var fn2 = (a, b = 5) => { 103 | console.log('haha'); 104 | return a + b; 105 | }; 106 | console.log(fn2(1)); //第二个参数使用默认值 5。输出结果:6 107 | 108 | console.log(fn2(1, 8)); //输出结果:9 109 | 110 | ``` 111 | 112 | **提醒1**:默认值的后面,不能再有**没有默认值的变量**。比如`(a,b,c)`这三个参数,如果我给b设置了默认值,那么就一定要给c设置默认值。 113 | 114 | **提醒2**: 115 | 116 | 我们来看下面这段代码: 117 | 118 | ```javascript 119 | let x = 'smyh'; 120 | function fn(x, y = x) { 121 | console.log(x, y); 122 | } 123 | fn('vae'); 124 | ``` 125 | 126 | 注意第二行代码,我们给y赋值为`x`,这里的`x`是括号里的第一个参数,并不是第一行代码里定义的`x`。打印结果:`vae vae`。 127 | 128 | 如果我把第一个参数改一下,改成: 129 | 130 | ```javascript 131 | let x = "smyh"; 132 | function fn(z, y = x) { 133 | console.log(z, y); 134 | } 135 | fn("vae"); 136 | ``` 137 | 138 | 此时打印结果是:`vae smyh`。 139 | 140 | ## 扩展运算符 141 | 142 | 注意区分: 143 | 144 | - 扩展运算符的格式为`...` 145 | 146 | - rest运算符的格式为`...变量名` 147 | 148 | 有了ES6,当我们在定义一个方法,但是不确定其参数的个数时,我们就可以用**扩展运算符**作为参数。 149 | 150 | 以前,我们在定义方法时,参数要确定个数,如下:(程序会报错) 151 | 152 | ```javascript 153 | function fn(a, b, c) { 154 | console.log(a); 155 | console.log(b); 156 | console.log(c); 157 | console.log(d); 158 | } 159 | 160 | fn(1, 2, 3); 161 | ``` 162 | 163 | 上方代码中,因为方法的参数是三个,但使用时是用到了四个参数,所以会报错: 164 | 165 | ![](http://img.smyhvae.com/20180304_1638.png) 166 | 167 | 现在,我们有了扩展运算符,就不用担心报错的问题了。代码可以这样写: 168 | 169 | ```javascript 170 | function fn(...arg) { //当不确定方法的参数时,可以使用扩展运算符 171 | console.log(arg[0]); 172 | console.log(arg[1]); 173 | console.log(arg[2]); 174 | console.log(arg[3]); 175 | } 176 | 177 | fn(1, 2, 3); //方法中定义了四个参数,但只引用了三个参数,ES6 中并不会报错。 178 | 179 | ``` 180 | 181 | ![](http://img.smyhvae.com/20180304_1650.png) 182 | 183 | 上方代码中注意,arg参数之后,不能再加别的参数,否则编译报错。 184 | 185 | **举例:**数组赋值的问题 186 | 187 | 我们来分析一段代码:(将数组 arr1 赋值给 arr2) 188 | 189 | ```javascript 190 | let arr1 = ['www', 'smyhvae', 'com']; 191 | let arr2 = arr1; // 将 arr1 赋值给 arr2,其实是让 arr2 指向 arr1 的内存地址 192 | console.log('arr1:' + arr1); 193 | console.log('arr2:' + arr2); 194 | console.log('---------------------'); 195 | 196 | arr2.push('你懂得'); //往arr2 里添加一部分内容 197 | console.log('arr1:' + arr1); 198 | console.log('arr2:' + arr2); 199 | ``` 200 | 201 | 运行结果: 202 | 203 | ![](http://img.smyhvae.com/20180304_1950.png) 204 | 205 | 上方代码中,我们往往 arr2 里添加了`你懂的`,却发现,arr1 里也有这个内容。原因是:`let arr2 = arr1;`其实是让 arr2 指向 arr1 的地址。也就是说,二者指向的是同一个内存地址。 206 | 207 | 如果不想让 arr1 和 arr2 指向同一个内存地址,我们可以借助扩展运算符来做: 208 | 209 | ```javascript 210 | let arr1 = ['www', 'smyhvae', 'com']; 211 | let arr2 = [...arr1]; //arr2 会重新开辟内存地址 212 | console.log('arr1:' + arr1); 213 | console.log('arr2:' + arr2); 214 | console.log('---------------------'); 215 | 216 | arr2.push('你懂得'); //往arr2 里添加一部分内容 217 | console.log('arr1:' + arr1); 218 | console.log('arr2:' + arr2); 219 | ``` 220 | 221 | 运行结果: 222 | 223 | ![](http://img.smyhvae.com/20180304_1951.png) 224 | 225 | 我们明白了这个例子,就可以避免开发中的很多业务逻辑上的 bug。 226 | 227 | ## `rest` 运算符 228 | 229 | `rest` 在英文中指的是**剩余部分**(不是指休息)。我们来举个例子,理解剩余部分的含义: 230 | 231 | ```javascript 232 | function fn(first, second, ...arg) { 233 | console.log(arg.length); 234 | } 235 | 236 | fn(0, 1, 2, 3, 4, 5, 6); //调用函数后,输出结果为 5 237 | ``` 238 | 239 | 上方代码的输出结果为 5。 调用`fn()`时,里面有七个参数,而`arg`指的是剩下的部分(因为除去了`first`和`second`)。 240 | 241 | 从上方例子中可以看出,`rest`运算符适用于:知道前面的一部分参数的数量,但对于后面剩余的参数数量未知的情况。 242 | 243 | 244 | -------------------------------------------------------------------------------- /ES6/day1/文档/06-ES6:promise、async等.md: -------------------------------------------------------------------------------- 1 | 2 | ## 前言 3 | 4 | 文本主要内容: 5 | 6 | - Promise(比较重要) 7 | 8 | - Symbol 9 | 10 | - async函数 11 | 12 | ## Promise 13 | 14 | ### 概述 15 | 16 | Promise对象: 代表了未来某个将要发生的事件(通常是一个异步操作)。 17 | 18 | 19 | ES6中的promise对象, 可以**将异步操作以同步的流程表达出来,**很好地解决了**回调地狱**的问题(避免了层层嵌套的回调函数)。在使用ES5的时候,在多层嵌套回调时,写完的代码层次过多,很难进行维护和二次开发。 20 | 21 | 22 | ### 回调地狱的举例 23 | 24 | 假设买菜、做饭、洗碗都是异步的。 25 | 26 | 现在的流程是:买菜成功之后,才能开始做饭。做饭成功后,才能开始洗碗。这里面就涉及到了回调的嵌套。 27 | 28 | 29 | ES6的Promise是一个构造函数, 用来生成promise实例。 30 | 31 | 32 | ### promise对象的3个状态 33 | 34 | - 初始化状态(等待状态):pending 35 | 36 | - 成功状态:fullfilled 37 | 38 | - 失败状态:rejected 39 | 40 | ### 使用promise的基本步骤 41 | 42 | (1)创建promise对象 43 | 44 | (2)调用promise的**回调函数**then() 45 | 46 | 47 | 代码格式如下: 48 | 49 | ```javascript 50 | let promise = new Promise((resolve, reject) => { 51 | //进来之后,状态为pending 52 | console.log('111'); //这一行代码是同步的 53 | //开始执行异步操作(这里开始,写异步的代码,比如ajax请求 or 开启定时器) 54 | if (异步的ajax请求成功) { 55 | console.log('333'); 56 | resolve();//如果请求成功了,请写resolve(),此时,promise的状态会被自动修改为fullfilled 57 | } else { 58 | reject();//如果请求失败了,请写reject(),此时,promise的状态会被自动修改为rejected 59 | } 60 | }) 61 | console.log('222'); 62 | 63 | //调用promise的then() 64 | promise.then(() => { 65 | //如果promise的状态为fullfilled,则执行这里的代码 66 | console.log('成功了'); 67 | } 68 | , () => { 69 | //如果promise的状态为rejected,则执行这里的代码 70 | console.log('失败了'); 71 | 72 | } 73 | ) 74 | ``` 75 | 76 | 代码解释: 77 | 78 | (1)当new Promise()执行之后,promise对象的状态会被初始化为`pending`,这个状态是初始化状态。`new Promise()`这行代码,括号里的内容是同步执行的。括号里定义一个function,function有两个参数:resolve和reject。如下: 79 | 80 | 81 | - 如果请求成功了,请写resolve(),此时,promise的状态会被自动修改为fullfilled。 82 | 83 | - 如果请求失败了,请写reject(),此时,promise的状态会被自动修改为rejected 84 | 85 | (2)promise.then()方法,括号里面有两个参数,分别代表两个函数 function1 和 function2: 86 | 87 | - 如果promise的状态为fullfilled(意思是:如果请求成功),则执行function1里的内容 88 | 89 | - 如果promise的状态为rejected(意思是,如果请求失败),则执行function2里的内容 90 | 91 | 另外,resolve()和reject()这两个方法,是可以给promise.then()传递参数的。如下: 92 | 93 | 94 | ```javascript 95 | let promise = new Promise((resolve, reject) => { 96 | //进来之后,状态为pending 97 | console.log('111'); //这行代码是同步的 98 | //开始执行异步操作(这里开始,写异步的代码,比如ajax请求 or 开启定时器) 99 | if (异步的ajax请求成功) { 100 | console.log('333'); 101 | resolve('haha');//如果请求成功了,请写resolve(),此时,promise的状态会被自动修改为fullfilled 102 | } else { 103 | reject('555');//如果请求失败了,请写reject(),此时,promise的状态会被自动修改为rejected 104 | } 105 | }) 106 | console.log('222'); 107 | 108 | //调用promise的then() 109 | promise.then((successMsg) => { 110 | //如果promise的状态为fullfilled,则执行这里的代码 111 | console.log(successMsg, '成功了'); 112 | } 113 | , (errorMsg) => { 114 | //如果promise的状态为rejected,则执行这里的代码 115 | console.log(errorMsg, '失败了'); 116 | 117 | } 118 | ) 119 | ``` 120 | 121 | 122 | 123 | ### ajax请求的举例(涉及到嵌套的回调) 124 | 125 | ```javascript 126 | //定义一个请求news的方法 127 | function getNews(url) { 128 | //创建一个promise对象 129 | let promise = new Promise((resolve, reject) => { 130 | //初始化promise状态为pending 131 | //启动异步任务 132 | let request = new XMLHttpRequest(); 133 | request.onreadystatechange = function () { 134 | if (request.readyState === 4) { 135 | if (request.status === 200) { 136 | let news = request.response; 137 | resolve(news); 138 | } else { 139 | reject('请求失败了。。。'); 140 | } 141 | } 142 | }; 143 | request.responseType = 'json';//设置返回的数据类型 144 | request.open("GET", url);//规定请求的方法,创建链接 145 | request.send();//发送 146 | }) 147 | return promise; 148 | } 149 | 150 | getNews('http://localhost:3000/news?id=2') 151 | .then((news) => { 152 | console.log(news); 153 | document.write(JSON.stringify(news)); 154 | console.log('http://localhost:3000' + news.commentsUrl); 155 | return getNews('http://localhost:3000' + news.commentsUrl); 156 | }, (error) => { 157 | alert(error); 158 | }) 159 | .then((comments) => { 160 | console.log(comments); 161 | document.write('




' + JSON.stringify(comments)); 162 | }, (error) => { 163 | alert(error); 164 | }) 165 | 166 | ``` 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | ## Symbol 177 | 178 | ### 概述 179 | 180 | 背景:ES5中对象的属性名都是字符串,容易造成重名,污染环境。 181 | 182 | **概念**:ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。 183 | 184 | 185 | **特点:** 186 | 187 | - Symbol属性对应的值是唯一的,解决**命名冲突问题** 188 | 189 | - Symbol值不能与其他数据进行计算,包括同字符串拼串 190 | 191 | - for in、for of 遍历时不会遍历Symbol属性。 192 | 193 | 194 | ### 创建Symbol属性值 195 | 196 | Symbol是函数,但并不是构造函数。创建一个Symbol数据类型: 197 | 198 | ```javascript 199 | let mySymbol = Symbol(); 200 | 201 | console.log(typeof mySymbol); //打印结果:symbol 202 | console.log(mySymbol); //打印结果:Symbol() 203 | ``` 204 | 205 | 打印结果: 206 | 207 | ![](http://img.smyhvae.com/20180317_1134.png) 208 | 209 | 下面来讲一下Symbol的使用。 210 | 211 | ### 1、将Symbol作为对象的属性值 212 | 213 | ```javascript 214 | let mySymbol = Symbol(); 215 | 216 | let obj = { 217 | name: 'smyhvae', 218 | age: 26 219 | }; 220 | 221 | //obj.mySymbol = 'male'; //错误:不能用 . 这个符号给对象添加 Symbol 属性。 222 | obj[mySymbol] = 'hello'; //正确:通过**属性选择器**给对象添加 Symbol 属性。后面的属性值随便写。 223 | 224 | console.log(obj); 225 | ``` 226 | 227 | 上面的代码中,我们尝试给obj添加一个Symbol类型的属性值,但是添加的时候,不能采用`.`这个符号,而是应该用`属性选择器`的方式。打印结果: 228 | 229 | ![](http://img.smyhvae.com/20180317_1134.png) 230 | 231 | 现在我们用for in尝试对上面的obj进行遍历: 232 | 233 | ```javascript 234 | let mySymbol = Symbol(); 235 | 236 | let obj = { 237 | name: 'smyhvae', 238 | age: 26 239 | }; 240 | 241 | obj[mySymbol] = 'hello'; 242 | 243 | console.log(obj); 244 | 245 | //遍历obj 246 | for (let i in obj) { 247 | console.log(i); 248 | } 249 | ``` 250 | 251 | 打印结果: 252 | 253 | ![](http://img.smyhvae.com/20180317_1134.png) 254 | 255 | 从打印结果中可以看到:for in、for of 遍历时不会遍历Symbol属性。 256 | 257 | ### 创建Symbol属性值时,传参作为标识 258 | 259 | 如果我通过 Symbol()函数创建了两个值,这两个值是不一样的: 260 | 261 | ```javascript 262 | let mySymbol1 = Symbol(); 263 | let mySymbol2 = Symbol(); 264 | 265 | console.log(mySymbol1 == mySymbol2); //打印结果:false 266 | console.log(mySymbol1); //打印结果:Symbol() 267 | console.log(mySymbol2); //打印结果:Symbol() 268 | ``` 269 | 270 | ![](http://img.smyhvae.com/20180317_1134.png) 271 | 272 | 上面代码中,倒数第三行的打印结果也就表明了,二者的值确实是不相等的。 273 | 274 | 最后两行的打印结果却发现,二者的打印输出,肉眼看到的却相同。那该怎么区分它们呢? 275 | 276 | 既然Symbol()是函数,函数就可以传入参数,我们可以通过参数的不同来作为**标识**。比如: 277 | 278 | 279 | ```javascript 280 | //在括号里加入参数,来标识不同的Symbol 281 | let mySymbol1 = Symbol('one'); 282 | let mySymbol2 = Symbol('two'); 283 | 284 | console.log(mySymbol1 == mySymbol2); //打印结果:false 285 | console.log(mySymbol1); //打印结果:Symbol(one) 286 | console.log(mySymbol2); //打印结果:Symbol(two)。颜色为红色。 287 | console.log(mySymbol2.toString());//打印结果:Symbol(two)。颜色为黑色。 288 | ``` 289 | 290 | 打印结果: 291 | 292 | ![](http://img.smyhvae.com/20180317_1134.png) 293 | 294 | ### 定义常量 295 | 296 | Symbol 可以用来定义常量: 297 | 298 | 299 | ```javascript 300 | const MY_NAME = Symbol('my_name'); 301 | ``` 302 | 303 | 304 | ## async函数(异步函数) 305 | 306 | ### 概述 307 | 308 | > async 函数是在 ES2017 引入的。 309 | 310 | 概念:真正意义上去解决异步回调的问题,同步流程表达异步操作。 311 | 312 | 本质: Generator 的语法糖。 313 | 314 | async比之前的 Promise、Generator要好用一些。 315 | 316 | 317 | 语法: 318 | 319 | ```javascript 320 | async function foo() { 321 | await 异步操作; 322 | await 异步操作; 323 | } 324 | ``` 325 | 326 | 我们在普通的函数前面加上 async 关键字,就成了 async 函数。 327 | 328 | 329 | ### async、Promise、Generator的对比(async的特点) 330 | 331 | 1、不需要像Generator去调用next方法,遇到await等待,当前的异步操作完成就往下执行。 332 | 333 | 2、async返回的总是Promise对象,可以用then方法进行下一步操作。 334 | 335 | 3、async取代Generator函数的星号*,await取代Generator的yield。 336 | 337 | 4、语意上更为明确,使用简单,经临床验证,暂时没有任何副作用。 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | -------------------------------------------------------------------------------- /ES6/day1/文档/07-ES6:字符串、数组、对象的扩展.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## 字符串的扩展 4 | 5 | ES6中的字符串扩展,用得少,而且逻辑相对简单。如下: 6 | 7 | - `includes(str)`:判断是否包含指定的字符串 8 | 9 | - `startsWith(str)`:判断是否以指定字符串开头 10 | 11 | - `endsWith(str)`:判断是否以指定字符串结尾 12 | 13 | - `repeat(count)`:重复指定次数 14 | 15 | 16 | 举例如下: 17 | 18 | ```javascript 19 | let str = 'abcdefg'; 20 | 21 | console.log(str.includes('a'));//true 22 | console.log(str.includes('h'));//false 23 | 24 | //startsWith(str) : 判断是否以指定字符串开头 25 | console.log(str.startsWith('a'));//true 26 | console.log(str.startsWith('d'));//false 27 | 28 | //endsWith(str) : 判断是否以指定字符串结尾 29 | console.log(str.endsWith('g'));//true 30 | console.log(str.endsWith('d'));//false 31 | 32 | //repeat(count) : 重复指定次数a 33 | console.log(str.repeat(5)); 34 | ``` 35 | 36 | 打印结果: 37 | 38 | ![](http://img.smyhvae.com/20180402_1050.png) 39 | 40 | ## Number 的扩展 41 | 42 | - 二进制与八进制数值表示法: 二进制用`0b`, 八进制用`0o`。 43 | 44 | 举例: 45 | 46 | ```javascript 47 | console.log(0b1010);//10 48 | console.log(0o56);//46 49 | ``` 50 | 51 | - `Number.isFinite(i)`:判断是否为有限大的数。比如`Infinity`这种无穷大的数,返回的就是false。 52 | 53 | - `Number.isNaN(i)`:判断是否为NaN。 54 | 55 | - `Number.isInteger(i)`:判断是否为整数。 56 | 57 | - `Number.parseInt(str)`:将字符串转换为对应的数值。 58 | 59 | - `Math.trunc(i)`:去除小数部分。 60 | 61 | 举例: 62 | 63 | ```javascript 64 | //Number.isFinite(i) : 判断是否是有限大的数 65 | console.log(Number.isFinite(NaN)); //false 66 | console.log(Number.isFinite(5)); //true 67 | console.log(Number.isFinite(Infinity)); //false 68 | 69 | //Number.isNaN(i) : 判断是否是NaN 70 | console.log(Number.isNaN(NaN));//true 71 | console.log(Number.isNaN(5));//falsse 72 | 73 | //Number.isInteger(i) : 判断是否是整数 74 | console.log(Number.isInteger(5.23));//false 75 | console.log(Number.isInteger(5.0));//true 76 | console.log(Number.isInteger(5));//true 77 | 78 | //Number.parseInt(str) : 将字符串转换为对应的数值 79 | console.log(Number.parseInt('123abc'));//123 80 | console.log(Number.parseInt('a123abc'));//NaN 81 | 82 | // Math.trunc(i) : 直接去除小数部分 83 | console.log(Math.trunc(13.123));//13 84 | ``` 85 | 86 | ## 数组的扩展 87 | 88 | > 下面提到的数组的几个方法,更详细的内容,可以看《04-JavaScript基础/17-数组的常见方法.md》。 89 | 90 | ### 扩展1:Array.from() 91 | 92 | ```javascript 93 | Array.from(伪数组/可遍历的对象) 94 | ``` 95 | 96 | **作用**:将**伪数组**或可遍历对象转换为**真数组**。 97 | 98 | 99 | ### 扩展2:Array.of() 100 | 101 | ```javascript 102 | Array.of(value1, value2, value3) 103 | ``` 104 | 105 | **作用**:将一系列值转换成数组。 106 | 107 | ### 扩展3:find() 和 findIndex() 108 | 109 | **方法1**: 110 | 111 | 112 | ```javascript 113 | find(function(item, index, arr){return true}) 114 | ``` 115 | 116 | **作用**:找出**第一个**满足「指定条件返回true」的元素。 117 | 118 | **方法2**: 119 | 120 | ```javascript 121 | findIndex(function(item, index, arr){return true}) 122 | ``` 123 | 124 | **作用**:找出第一个满足「指定条件返回true」的元素的index。 125 | 126 | ## 对象的扩展 127 | 128 | ### 扩展1 129 | 130 | 131 | ```javascript 132 | Object.is(v1, v2) 133 | ``` 134 | 135 | **作用:**判断两个数据是否完全相等。底层是通过**字符串**来判断的。 136 | 137 | 我们先来看下面这两行代码的打印结果: 138 | 139 | 140 | ```javascript 141 | console.log(0 == -0); 142 | console.log(NaN == NaN); 143 | ``` 144 | 145 | 打印结果: 146 | 147 | ``` 148 | true 149 | false 150 | ``` 151 | 152 | 上方代码中,第一行代码的打印结果为true,这个很好理解。第二行代码的打印结果为false,因为NaN和任何值都不相等。 153 | 154 | 但是,如果换成下面这种方式来比较: 155 | 156 | ```javascript 157 | console.log(Object.is(0, -0)); 158 | console.log(Object.is(NaN, NaN)); 159 | ``` 160 | 161 | 打印结果却是: 162 | 163 | ``` 164 | false 165 | true 166 | ``` 167 | 168 | 代码解释:还是刚刚说的那样,`Object.is(v1, v2)`比较的是字符串是否相等。 169 | 170 | ### 扩展2(重要) 171 | 172 | ```javascript 173 | Object.assign(目标对象, 源对象1, 源对象2...) 174 | ``` 175 | 176 | **作用:** 将源对象的属性追加到目标对象上。如果对象里属性名相同,会被覆盖。 177 | 178 | 其实可以理解成:将多个对象**合并**为一个新的对象。 179 | 180 | 181 | 182 | 举例: 183 | 184 | ```javascript 185 | let obj1 = { name: 'smyhvae', age: 26 }; 186 | let obj2 = { city: 'shenzhen' }; 187 | let obj3 = {}; 188 | 189 | Object.assign(obj3, obj1, obj2); 190 | console.log(obj3); 191 | ``` 192 | 193 | 打印结果: 194 | 195 | ![](http://img.smyhvae.com/20180404_2240.png) 196 | 197 | 上图显示,成功将obj1和obj2的属性复制给了obj3。 198 | 199 | 200 | 201 | ### 扩展3:`__proto__`属性 202 | 203 | 举例: 204 | 205 | ```javascript 206 | let obj1 = {name:'smyhvae'}; 207 | let obj2 = {}; 208 | 209 | obj2.__proto__ = obj1; 210 | 211 | console.log(obj1); 212 | console.log(obj2); 213 | console.log(obj2.name); 214 | ``` 215 | 216 | 打印结果: 217 | 218 | ![](http://img.smyhvae.com/20180404_2251.png) 219 | 220 | 上方代码中,obj2本身是没有属性的,但是通过`__proto__`属性和obj1产生关联,于是就可以获得obj1里的属性。 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | ```javascript 254 | 255 | ``` 256 | 257 | 258 | 259 | -------------------------------------------------------------------------------- /Express/day4/文档/1-Express介绍和安装.md: -------------------------------------------------------------------------------- 1 | ### 前言 2 | 3 | ndoe.js,一个基于javsscript运行环境的服务器语言,它的出现使得javascript有能力去实现服务器操作。在gitHub上ndoe.js的star数已接近6万,可见其受欢迎程度;而基于node.js的Express则把原先的许多操作变的简单灵活,一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具。使用 Express 可以快速地搭建一个完整功能的网站。 4 | 5 | express官方网址:www.expressjs.com.cn 6 | 7 | ### Express的安装方式 8 | 9 | Express的安装可直接使用npm包管理器上的项目,在安装npm之前可先安装淘宝镜像: 10 | 11 | ``` 12 | npm install -g cnpm --registry=https://registry.npm.taobao.org 13 | ``` 14 | 15 | 16 | 这样我们使用cnpm的来代替npm,这使得下载速度提高很多;其次你需要在你项目目录下运行以下指令来初始化npm,期间所有提示按enter键即可,这会生成package.json,它是用于描述项目文件的。 17 | 18 | ``` 19 | cnpm init 20 | ``` 21 | 22 | 23 | 再输入 24 | 25 | ``` 26 | cnpm install 27 | ``` 28 | 29 | 30 | 这下项目目录中又会多出一个叫node_modules文件夹,里面是node.js为我们提供的模块,当然现在没有。接下来便是真正的安装express了,执行: 31 | 32 | ``` 33 | cnpm install express --save 34 | ``` 35 | 36 | 这时,我们看到node_modules文件夹多了许多不同版本的应用文件夹,接下来执行 37 | 38 | ``` 39 | express --version 40 | ``` 41 | 42 | 查看express是否安装成功,如果显示版本号,则安装正确。 43 | 44 | ### Express脚手架的安装 45 | 46 | 安装Express脚手架有两种方式: 47 | 48 | #### 1、使用express-generator安装 49 | 50 | 使用命令行进入项目目录,依次执行: 51 | 52 | ``` 53 | cnpm i express-generator 54 | ``` 55 | 56 | 57 | 可通过express -h查看命令行的指令含义 58 | 59 | ``` 60 | express -h 61 | ``` 62 | 63 | 64 | 65 | Usage: express [options] [dir] 66 | 67 | 68 | ​ 69 | Options: 70 | --version 输出版本号 71 | -e, --ejs 添加对 ejs 模板引擎的支持 72 | --pug 添加对 pug 模板引擎的支持 73 | --hbs 添加对 handlebars 模板引擎的支持 74 | -H, --hogan 添加对 hogan.js 模板引擎的支持 75 | -v, --view 添加对视图引擎(view) 的支持 (ejs|hbs|hjs|jade|pug|twig|vash) (默认是 jade 模板引擎) 76 | --no-view 创建不带视图引擎的项目 77 | -c, --css 添加样式表引擎 的支持 (less|stylus|compass|sass) (默认是普通的 css 文件) 78 | --git 添加 .gitignore 79 | -f, --force 强制在非空目录下创建 80 | -h, --help 输出使用方法 81 | 82 | 创建了一个名为 myapp 的 Express 应用,并使用ejs模板引擎 83 | 84 | ``` 85 | express --view=ejs app 86 | ``` 87 | 88 | 89 | 进入app,并安装依赖 90 | 91 | ``` 92 | cd myapp 93 | npm install 94 | ``` 95 | 96 | **在Windows 下,使用以下命令启Express应用:** 97 | 98 | ``` 99 | set DEBUG=app:* & npm start 100 | ``` 101 | 102 | **在 MacOS 或 Linux 下,使用以下命令启Express应用:** 103 | 104 | ``` 105 | DEBUG=app:* npm start 106 | ``` 107 | 108 | 109 | 110 | #### 2、使用 express 命令 来快速从创建一个项目目录 111 | 112 | express 项目文件夹的名字 -e 113 | 如 使用命令行进入项目目录,依次执行: 114 | 115 | ``` 116 | express app -e 117 | ``` 118 | 119 | ``` 120 | cd app 121 | cnpm install 122 | ``` 123 | 124 | 125 | 126 | 这时,你也可以看到在app文件夹下的文件结构; 127 | 128 | ``` 129 | bin: 启动目录 里面包含了一个启动文件 www 默认监听端口是 3000 (直接node www执行即可) 130 | node_modules:依赖的模块包 131 | public:存放静态资源 132 | routes:路由操作 133 | views:存放ejs模板引擎 134 | app.js:主文件 135 | package.json:项目描述文件 136 | ``` 137 | 138 | 这时,你也可以看到在app文件夹下的文件结构; 139 | bin: 启动目录 里面包含了一个启动文件 www 默认监听端口是 3000 (直接node www执行即可) 140 | node_modules:依赖的模块包 141 | public:存放静态资源 142 | routes:路由操作 143 | views:存放ejs模板引擎 144 | app.js:主文件 145 | package.json:项目描述文件 146 | 147 | 148 | 149 | ### 第一个Express应用“Hello World” 150 | 151 | 在这里,我们不使用npm构建的脚手架,而是向最开始那样直接在主目录中新建一个app.js文件。 152 | 153 | 在app.js中输入 154 | 155 | ```javascript 156 | const express = require('express'); //引入express模块 157 | var app= express(); //express()是express模块顶级函数 158 | 159 | app.get('/',function(req,res){ //访问根路径时输出hello world 160 | res.send(`

hello world

`); 161 | }); 162 | 163 | app.listen(8080); //设置访问端口号 164 | 165 | ``` 166 | 167 | 168 | 命令行进入项目文件夹后,键入 169 | 170 | ``` 171 | node app.js 172 | ``` 173 | 174 | 175 | 即已开启服务器,接下来只需在浏览器中运行 http://localhost:8080/ 就可以访问到服务器得到响应后的数据 176 | 177 | -------------------------------------------------------------------------------- /Express/day4/文档/2-Express路由.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### 一、Express路由简介 4 | 5 | 路由表示应用程序端点 (URI) 的定义以及响应客户端请求的方式。它包含一个请求方时(methods)、路径(path)和路由匹配时的函数(callback); 6 | 7 | ``` 8 | app.methods(path, callback); 9 | ``` 10 | 11 | ### 二、Express路由方法 12 | 13 | Express方法源于 HTTP 方法之一,附加到 express 类的实例。它可请求的方法包括: 14 | 15 | get、post、put、head、delete、options、trace、copy、lock、mkcol、move、purge、propfind、proppatch、unlock、report、mkactivity、checkout、merge、m-search、notify、subscribe、unsubscribe、patch、search 和 connect。 16 | 17 | ### 三、路径 18 | 19 | Express路径包含三种表达形式,分别为字符串、字符串模式、正则表达式 20 | 21 | #### 1.字符串路径 22 | 23 | ``` 24 | app.get("/login",function(req,res){ 25 | res.send("heng... women"); 26 | }) 27 | ``` 28 | 29 | 30 | 此路径地址将与/login匹配 31 | 32 | #### 2.字符串模式路径 33 | 34 | 此路由路径将与`acd`和相匹配`abcd`。 35 | 36 | ```javascript 37 | app.get('/ab?cd', function (req, res) { 38 | res.send('ab?cd') 39 | }) 40 | ``` 41 | 42 | 这条路线的路径将会匹配`abcd`,`abbcd`,`abbbcd`,等等。 43 | 44 | ```javascript 45 | app.get('/ab+cd', function (req, res) { 46 | res.send('ab+cd') 47 | }) 48 | ``` 49 | 50 | 这条路线的路径将会匹配`abcd`,`abxcd`,`abRANDOMcd`,`ab123cd`,等。 51 | 52 | ```javascript 53 | app.get('/ab*cd', function (req, res) { 54 | res.send('ab*cd') 55 | }) 56 | ``` 57 | 58 | 此路由路径将与`/abe`和相匹配`/abcde`。 59 | 60 | ```javascript 61 | app.get('/ab(cd)?e', function (req, res) { 62 | res.send('ab(cd)?e') 63 | }) 64 | ``` 65 | 66 | #### 3.正则表达式路径 67 | 68 | 此路由路径将匹配其中带有“ a”的任何内容。 69 | 70 | ```javascript 71 | app.get(/a/, function (req, res) { 72 | res.send('/a/') 73 | }) 74 | ``` 75 | 76 | 这条路线的路径将匹配`butterfly`和`dragonfly`,但不`butterflyman`,`dragonflyman`等。 77 | 78 | ```javascript 79 | app.get(/.*fly$/, function (req, res) { 80 | res.send('/.*fly$/') 81 | }) 82 | ``` 83 | 84 | #### 85 | 86 | ### 四、基础路由 87 | 88 | ``` 89 | const express = require("express"); 90 | var app = express(); 91 | 92 | app.get("/",function(req,res){ 93 | res.send(`

主页

`); 94 | }); 95 | app.get("/login",function(req,res){ 96 | res.send(“登录页面”); 97 | }); 98 | app.get("/registe",function(req,res){ 99 | res.send(“注册页面”); 100 | }); 101 | 102 | app.listen(8080); 103 | ``` 104 | 105 | 106 | 输入http://127.0.0.1:8080/login和http://127.0.0.1:8080/registe都能进入不同路由。 107 | 108 | ### 五、动态路由 109 | 110 | ### 路线参数 111 | 112 | 路由参数被命名为URL段,用于捕获URL中在其位置处指定的值。捕获的值将填充到`req.params`对象中,并将路径中指定的route参数的名称作为其各自的键。 113 | 114 | ``` 115 | Route path: /users/:userId/books/:bookId 116 | Request URL: http://localhost:3000/users/34/books/8989 117 | req.params: { "userId": "34", "bookId": "8989" } 118 | ``` 119 | 120 | 要使用路由参数定义路由,只需在路由路径中指定路由参数,如下所示。 121 | 122 | ```javascript 123 | app.get('/users/:userId/books/:bookId', function (req, res) { 124 | res.send(req.params) 125 | }) 126 | ``` 127 | 128 | 路径参数的名称必须由“文字字符”([A-Za-z0-9_])组成。 129 | 130 | 由于连字符(`-`)和点(`.`)是按字面解释的,因此可以将它们与路由参数一起使用,以实现有用的目的。 131 | 132 | ``` 133 | Route path: /flights/:from-:to 134 | Request URL: http://localhost:3000/flights/LAX-SFO 135 | req.params: { "from": "LAX", "to": "SFO" } 136 | Route path: /plantae/:genus.:species 137 | Request URL: http://localhost:3000/plantae/Prunus.persica 138 | req.params: { "genus": "Prunus", "species": "persica" } 139 | ``` 140 | 141 | 要更好地控制可以由route参数匹配的确切字符串,可以在括号(`()`)后面附加一个正则表达式: 142 | 143 | ``` 144 | Route path: /user/:userId(\d+) 145 | Request URL: http://localhost:3000/user/42 146 | req.params: {"userId": "42"} 147 | ``` 148 | 149 | 由于正则表达式通常是文字字符串的一部分,因此请确保`\`使用其他反斜杠对所有字符进行转义,例如`\\d+`。 150 | 151 | 在Express 4.x中,[不以常规方式解释正则表达式中](https://github.com/expressjs/express/issues/2495)[的`*`字符](https://github.com/expressjs/express/issues/2495)。解决方法是使用`{0,}`代替`*`。这可能会在Express 5中修复。 152 | 153 | ## 路线处理程序 154 | 155 | 您可以提供行为类似于[中间件的](http://www.expressjs.com.cn/en/guide/using-middleware.html)多个回调函数来处理请求。唯一的例外是这些回调可能会调用`next('route')`以绕过其余的路由回调。您可以使用此机制在路由上施加先决条件,然后在没有理由继续使用当前路由的情况下将控制权传递给后续路由。 156 | 157 | 路由处理程序可以采用函数,函数数组或二者组合的形式,如以下示例所示。 158 | 159 | 单个回调函数可以处理路由。例如: 160 | 161 | ```javascript 162 | app.get('/example/a', function (req, res) { 163 | res.send('Hello from A!') 164 | }) 165 | ``` 166 | 167 | 多个回调函数可以处理一条路由(确保指定了`next`对象)。例如: 168 | 169 | ```javascript 170 | app.get('/example/b', function (req, res, next) { 171 | console.log('the response will be sent by the next function ...') 172 | next() 173 | }, function (req, res) { 174 | res.send('Hello from B!') 175 | }) 176 | ``` 177 | 178 | 回调函数数组可以处理路由。例如: 179 | 180 | ```javascript 181 | var cb0 = function (req, res, next) { 182 | console.log('CB0') 183 | next() 184 | } 185 | 186 | var cb1 = function (req, res, next) { 187 | console.log('CB1') 188 | next() 189 | } 190 | 191 | var cb2 = function (req, res) { 192 | res.send('Hello from C!') 193 | } 194 | 195 | app.get('/example/c', [cb0, cb1, cb2]) 196 | ``` 197 | 198 | 独立功能和功能数组的组合可以处理路由。例如: 199 | 200 | ```javascript 201 | var cb0 = function (req, res, next) { 202 | console.log('CB0') 203 | next() 204 | } 205 | 206 | var cb1 = function (req, res, next) { 207 | console.log('CB1') 208 | next() 209 | } 210 | 211 | app.get('/example/d', [cb0, cb1], function (req, res, next) { 212 | console.log('the response will be sent by the next function ...') 213 | next() 214 | }, function (req, res) { 215 | res.send('Hello from D!') 216 | }) 217 | ``` 218 | 219 | ## 应对方法 220 | 221 | `res`下表中响应对象()上的方法可以将响应发送到客户端,并终止请求-响应周期。如果从路由处理程序中未调用这些方法,则客户端请求将被挂起。 222 | 223 | | 方法 | 描述 | 224 | | ------------------------------------------------------------ | ------------------------------------------------------ | 225 | | [res.download()](http://www.expressjs.com.cn/en/4x/api.html#res.download) | 提示要下载的文件。 | 226 | | [res.end()](http://www.expressjs.com.cn/en/4x/api.html#res.end) | 结束响应过程。 | 227 | | [res.json()](http://www.expressjs.com.cn/en/4x/api.html#res.json) | 发送JSON响应。 | 228 | | [res.jsonp()](http://www.expressjs.com.cn/en/4x/api.html#res.jsonp) | 发送带有JSONP支持的JSON响应。 | 229 | | [res.redirect()](http://www.expressjs.com.cn/en/4x/api.html#res.redirect) | 重定向请求。 | 230 | | [res.render()](http://www.expressjs.com.cn/en/4x/api.html#res.render) | 渲染视图模板。 | 231 | | [res.send()](http://www.expressjs.com.cn/en/4x/api.html#res.send) | 发送各种类型的响应。 | 232 | | [res.sendFile()](http://www.expressjs.com.cn/en/4x/api.html#res.sendFile) | 将文件作为八位字节流发送。 | 233 | | [res.sendStatus()](http://www.expressjs.com.cn/en/4x/api.html#res.sendStatus) | 设置响应状态代码,并将其字符串表示形式发送为响应正文。 | 234 | 235 | ## app.route() 236 | 237 | 您可以使用来为路由路径创建可链接的路由处理程序`app.route()`。由于路径是在单个位置指定的,因此创建模块化路由非常有帮助,减少冗余和错别字也很有帮助。有关路由的更多信息,请参见:[Router()文档](http://www.expressjs.com.cn/en/4x/api.html#router)。 238 | 239 | 这是使用定义的链式路由处理程序的示例`app.route()`。 240 | 241 | ```javascript 242 | app.route('/book') 243 | .get(function (req, res) { 244 | res.send('Get a random book') 245 | }) 246 | .post(function (req, res) { 247 | res.send('Add a book') 248 | }) 249 | .put(function (req, res) { 250 | res.send('Update the book') 251 | }) 252 | ``` 253 | 254 | ## 快速路由器 255 | 256 | 使用`express.Router`该类创建模块化的,可安装的路由处理程序。一个`Router`实例是一个完整的中间件和路由系统; 因此,它通常被称为“迷你应用程序”。 257 | 258 | 以下示例将路由器创建为模块,在其中加载中间件功能,定义一些路由,并将路由器模块安装在主应用程序的路径上。 259 | 260 | `birds.js`在app目录中创建一个名为以下内容的路由器文件: 261 | 262 | ```javascript 263 | var express = require('express') 264 | var router = express.Router() 265 | 266 | // middleware that is specific to this router 267 | router.use(function timeLog (req, res, next) { 268 | console.log('Time: ', Date.now()) 269 | next() 270 | }) 271 | // define the home page route 272 | router.get('/', function (req, res) { 273 | res.send('Birds home page') 274 | }) 275 | // define the about route 276 | router.get('/about', function (req, res) { 277 | res.send('About birds') 278 | }) 279 | 280 | module.exports = router 281 | ``` 282 | 283 | 然后,在应用程序中加载路由器模块: 284 | 285 | ```javascript 286 | var birds = require('./birds') 287 | // ... 288 | app.use('/birds', birds) 289 | ``` 290 | 291 | 该应用程序现在将能够处理对`/birds`和的请求`/birds/about`,以及调用`timeLog`特定于该路线的中间件功能。 -------------------------------------------------------------------------------- /Express/day4/文档/3-EJS模板.md: -------------------------------------------------------------------------------- 1 | ### 一、简介 2 | 3 | 相比于jade模板引擎,ejs对原HTML语言就未作出结构上的改变,只不过在其交互数据方面做出了些许修改,相比于jade更加简单易用。因此其学习成本是很低的。您也可参考ejs官网:https://ejs.bootcss.com/ 4 | 5 | ### 二、ejs基本使用 6 | 7 | 这里我们使用如下配置文件: 8 | 9 | 我们啊可以通过下面的方式实现基本的ejs操作: 10 | app.js文件: 11 | 12 | ``` 13 | const express=require("express"); 14 | const ejs=require("ejs"); 15 | const fs=require("fs"); 16 | 17 | var app=express(); 18 | 19 | //引用ejs 20 | app.set('views',"public"); //设置视图的对应目录 21 | app.set("view engine","ejs"); //设置默认的模板引擎 22 | app.engine('ejs', ejs.__express); //定义模板引擎 23 | 24 | app.get("/",function(req,res){ 25 | res.render("index.ejs",{title: "

express

"}); 26 | }); 27 | 28 | app.listen(8080); 29 | ``` 30 | 31 | 32 | ejs文件: 33 | 34 | ```HTML 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | <% for(var i=0;i<10;i++){ %> 43 | <%= i %> 44 | <% } %> 45 | 46 |
47 |

获取变量:

48 | <%- title %> 49 | <%= title %> 50 |
51 | 52 | 53 | ``` 54 | 55 | 这时我们会得到如下图的结果: 56 | 57 | 由此可以知道: 58 | 59 | ``` 60 | <% xxx %>:里面写入的是js语法, 61 | <%= xxx %>:里面是服务端发送给ejs模板转义后的变量,输出为原html 62 | <%- xxx %>:里面也是服务端发送给ejs模板后的变量,不过他会把html输出来 63 | <%# 注释标签,不执行、不输出内容 64 | ``` 65 | 66 | 同理res.render()函数也是支持回调的: 67 | 68 | ``` 69 | res.render('user', { name: 'Tobi' }, function(err, html) { 70 | console.log(html); 71 | }); 72 | ``` 73 | 74 | 75 | 这样我们即可将看到heml的内容。另外值得说明的是ejs模块也有ejs.render()和ejs.renderFile()方法,他在这里与res.render()作用类似,如下: 76 | 77 | ``` 78 | ejs.render(str, data, options); 79 | 80 | ejs.renderFile(filename, data, options, function(err, str){ 81 | // str => 输出绘制后的 HTML 82 | }); 83 | ``` 84 | 85 | ### 三、ejs标签各种含义 86 | 87 | ``` 88 | <% '脚本' 标签,用于流程控制,无输出。 89 | <%_ 删除其前面的空格符 90 | <%= 输出数据到模板(输出是转义 HTML 标签) 91 | <%- 输出非转义的数据到模板 92 | <%# 注释标签,不执行、不输出内容 93 | <%% 输出字符串 '<%' 94 | %> 一般结束标签 95 | -%> 删除紧随其后的换行符 96 | _%> 将结束标签后面的空格符删除 97 | ``` 98 | 99 | 100 | 以上就为ejs基本用法,往后对数据库操作就直接把json数据从服务器返送给模板引擎就行; 101 | 102 | -------------------------------------------------------------------------------- /Express/day4/文档/3-获取GET参数.md: -------------------------------------------------------------------------------- 1 | ### 一、关于get请求 2 | 3 | 一般在网站开发中,get都用作数据获取和查询,类似于数据库中的查询操作,当服务器解析前台资源后即传输相应内容;而查询字符串是在URL上进行的,形如: 4 | 5 | ``` 6 | http://localhost:8080/login?goods1=0001&goods2=0002 7 | ``` 8 | 9 | 10 | 11 | ### 二、获取前台get请求 12 | 13 | 通过req.query可以获得用户发送的get请求,之后通过node操作将相应数据返回给用户。 14 | 15 | 如果发送的是: 16 | 17 | ``` 18 | http://localhost:8080/login?goods1=0001&goods2=0002 19 | ``` 20 | 21 | 22 | 响应的话则通过: 23 | 24 | ``` 25 | req.query 26 | ``` 27 | 28 | 29 | 他会获取到全部数据,或 30 | 31 | ``` 32 | req.query.goods1 33 | req.query.goods2 34 | ``` 35 | 36 | 37 | 来单独或去每一个数据。总之不同的需求对应不同的业务,大家按自己的需要来获取; 38 | 39 | ### 三、实例 40 | 41 | 下面通过一个实例来对获取get参数进行一个总结: 42 | 43 | HTML: 44 | 45 | ``` 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 |
54 | 用户: 55 | 56 |
57 | 密码: 58 | 59 |
60 | 61 |
62 | 63 | 64 | ``` 65 | 66 | NODE: 67 | 68 | ``` 69 | const express = require("express"); 70 | var app = express(); 71 | 72 | app.get("/",function(req,res){ 73 | res.send("主页"); 74 | }); 75 | 76 | app.get("/login",function(req,res){ 77 | console.log(req.query); 78 | res.send("登录路由,user为:"+req.query.user+"==> password为:"+req.query.password); 79 | }); 80 | 81 | app.listen(8080); 82 | ``` 83 | 84 | 85 | 86 | 当在html页面中输入用户和密码提交后: 87 | 88 | 就能得到想要的传输数据; 89 | 总之,虽然获取get参数并不复杂,但使用频率却相当的高,对于任何技术我们都应该秉持认真的态度去了解和学习它。 90 | -------------------------------------------------------------------------------- /Express/day4/文档/4-获取POST参数.md: -------------------------------------------------------------------------------- 1 | ### 一、关于POST请求 2 | 3 | post方法作为http请求很重要的一部分,几乎所有的网站都有用到它,与get不同,post请求更像是在服务器上做修改操作,它一般用于数据资源的更新。 4 | 相比于get请求,post所请求的数据会更加安全。上一章中我们发现get请求会在地址栏显示输入的用户名和密码(有中文时会转化为BASE64加密),而post请求则会将数据放入http包的包体中,这使得别人无法直接看到用户名和密码! 5 | 6 | ### 二、Express如何设置POST请求 7 | 8 | 1.我们的知道,首先我们得知道在form表单进行post请求,enctype属性一般设置为“application/x-www-form-urlencoded”,如果设置成multipart/form-data,则多用于文件上传,如下: 9 | 10 | ```html 11 |
12 |
13 | ``` 14 | 15 | 2设置解析body中间件 16 | 17 | ``` 18 | app.use(express.urlencoded()) 19 | ``` 20 | 21 | 3获取body数据 22 | 23 | ``` 24 | req.body.username 25 | ``` 26 | 27 | 28 | 29 | 登陆案例: 30 | 31 | HTML: 32 | 33 | ``` 34 | 35 | 36 | 37 | 38 | 39 | 40 | Document 41 | 42 | 43 |

登陆

44 |
45 |
46 | 用户名: 47 |
48 |
49 | 密码: 50 |
51 | 52 |
53 | 54 | 55 | 56 | ``` 57 | 58 | APP.JS 59 | 60 | ``` 61 | var express = require('express'); 62 | var path = require('path') 63 | var app = express(); 64 | var sqlQuery = require('./lcMysql') 65 | 66 | // view engine setup 67 | app.set('views', path.join(__dirname, 'views')); 68 | app.set('view engine', 'ejs'); 69 | app.use(express.static(path.join(__dirname, 'public'))); 70 | //解析post提交的数据 71 | app.use(express.urlencoded()) 72 | 73 | //搜索首页 74 | app.get('/',(req,res)=>{ 75 | res.render('index.ejs') 76 | }) 77 | 78 | //登陆页 79 | app.get('/login',(req,res)=>{ 80 | res.render('login') 81 | }) 82 | //处理登陆请求 83 | app.post('/login',async (req,res)=>{ 84 | //获取用户名和密码 85 | let username = req.body.username 86 | let password = req.body.password 87 | //查询数据库是否由此用户名和密码 88 | let sqlStr = 'select * from user where username = ? and password = ?'; 89 | let arr = [username,password]; 90 | let result = await sqlQuery(sqlStr,arr) 91 | if(result.length == 0 ){ 92 | res.send("登陆失败") 93 | }else{ 94 | res.send("登陆成功") 95 | } 96 | 97 | }) 98 | 99 | 100 | 101 | module.exports = app; 102 | 103 | ``` 104 | 105 | -------------------------------------------------------------------------------- /Express/day4/文档/5-中间件.md: -------------------------------------------------------------------------------- 1 | # 中间件 2 | 3 | 从字面意思,我们可以了解到它大概就是做中间代理操作,事实也是如此;大多数情况下,中间件就是在做接收到请求和发送响应中间的一系列操作。事实上,express是一个路由和中间件的web框架,Express 应用程序基本上是一系列中间件函数的调用。 4 | 5 | 6 | 7 | 1.浏览器发送请求 8 | 9 | 2.express接受请求 10 | 11 | 中间处理的过程 12 | 13 | 3.路由函数处理渲染(req,res) 14 | 15 | 4.res.render渲染 16 | 17 | 18 | 19 | 中间件函数可以执行以下任务: 20 | 21 | * 执行任何代码。 22 | * 对请求和响应对象进行更改。 23 | * 结束请求/响应循环。 24 | * 调用堆栈中的下一个中间件函数。 25 | 26 | 中间件也分为应用层中间件、路由中间件、内置中间件、错误处理中间件和第三方中间件。下面分别对以下进行说明: 27 | 28 | ### 1.应用层中间件 29 | 30 | 应用级中间键绑定到app对象使用app.use和app.METHOD()-需要处理http请求的方法,例如GET、PUT、POST,将之前的get或者post替换为use就行。 31 | 例如下面实例: 32 | 33 | ``` 34 | const express=require("express"); 35 | 36 | var app=express(); 37 | 38 | //匹配路由之前的操作 39 | app.use(function(req,res,next()){ 40 | console.log("访问之前"); 41 | }); 42 | 43 | app.get("/",function(req,res){ 44 | res.send("主页"); 45 | }); 46 | 47 | app.listen(8080); 48 | ``` 49 | 50 | 51 | 这时我们会发现http://localhost:8080/地址一直在加载,但命令行里显示了“访问之前”,说明程序并不会同步执行,如果使用next来是路由继续向下匹配,那么就能又得到主页数据了: 52 | 53 | ``` 54 | const express=require("express"); 55 | 56 | var app=express(); 57 | 58 | //匹配路由之前的操作 59 | app.use(function(req,res,next){ 60 | console.log("访问之前"); 61 | next(); 62 | }); 63 | 64 | app.get("/",function(req,res){ 65 | res.send("主页"); 66 | }); 67 | 68 | app.listen(8080); 69 | ``` 70 | 71 | 72 | 当然也可以简化写法: 73 | 74 | ``` 75 | const express=require("express"); 76 | 77 | var app=express(); 78 | 79 | app.use(function(req,res,next){ 80 | console.log("访问之前"); 81 | next(); 82 | },function(req,res){ 83 | res.send("主页"); 84 | }); 85 | 86 | app.listen(8080); 87 | ``` 88 | 89 | 因此,在进行路由匹配之前或再录又要继续向下执行时想做个操作,那么应用层中间件无疑是好的选择。 90 | 91 | ### 2.路由中间件 92 | 93 | 路由级中间件和应用级中间件类似,只不过他需要绑定express.Router(); 94 | 95 | 96 | 97 | ``` 98 | var router = express.Router() 99 | ``` 100 | 101 | 102 | 在匹配路由时,我们使用 router.use() 或 router.VERB() ,路由中间件结合多次callback可用于用户登录及用户状态检测。 103 | 104 | ``` 105 | const express = require("express"); 106 | var app = express(); 107 | var router=express.Router(); 108 | 109 | router.use("/",function(req,res,next){ 110 | console.log("匹配前"); 111 | next(); 112 | }); 113 | 114 | router.use("/user",function(req,res,next){ 115 | console.log("匹配地址:",req.originalUrl); 116 | next(); 117 | },function(req,res){ 118 | res.send("用户登录"); 119 | }); 120 | 121 | app.use("/",router); 122 | 123 | app.listen(8080); 124 | ``` 125 | 126 | 127 | 总之在检测用户登录和引导用户应该访问哪个页面是,路由中间件绝对好用。 128 | 129 | ### 3.错误处理中间件 130 | 131 | 顾名思义,它是指当我们匹配不到路由时所执行的操作。错误处理中间件和其他中间件基本一样,只不过其需要开发者提供4个自变量参数。 132 | 133 | ``` 134 | app.use((err, req, res, next) => { 135 | res.sendStatus(err.httpStatusCode).json(err); 136 | }); 137 | ``` 138 | 139 | 140 | 一般情况下,我们把错误处理放在最下面,这样我们即可对错误进行集中处理。 141 | 142 | ``` 143 | const express=require("express"); 144 | 145 | var app=express(); 146 | 147 | app.get("/",function(req,res,next){ 148 | const err=new Error('Not Found'); 149 | res.send("主页"); 150 | next(err); 151 | }); 152 | 153 | app.use("/user",function(err,req,res,next){ 154 | console.log("用户登录"); 155 | next(err); 156 | },function(req,res,next){ 157 | res.send("用户登录"); 158 | next(); 159 | }); 160 | 161 | app.use(function(req,res){ 162 | res.status(404).send("未找到指定页面"); 163 | }); 164 | 165 | app.listen(8080); 166 | ``` 167 | 168 | 169 | 170 | ### 4.内置中间件 171 | 172 | 从版本4.x开始,Express不再依赖Content,也就是说Express以前的内置中间件作为单独模块,express.static是Express的唯一内置中间件。 173 | 174 | ``` 175 | express.static(root, [options]); 176 | ``` 177 | 178 | 通过express.static我们可以指定要加载的静态资源。 179 | 180 | ### 5.第三方中间件 181 | 182 | 形如之前我们的body-parser,采用引入外部模块的方式来获得更多的应用操作。如后期的cookie和session。 183 | 184 | ``` 185 | var express = require('express'); 186 | var app = express(); 187 | var cookieParser = require('cookie-parser'); 188 | ``` 189 | 190 | 191 | 以上就是关于express中间件类型,在实际项目中,中间件都是必不可少的,因此熟悉使用各种中间件会加快项目的开发效率。 192 | 193 | -------------------------------------------------------------------------------- /Express/day4/文档/6-使用cookie.md: -------------------------------------------------------------------------------- 1 | ### 一、关于Cookie 2 | 3 | 在我们关闭一个登录过的网址并重新打开它后,我们的登录信息依然没有丢失;当我们浏览了商品后历史记录里出现了我们点击过的商品;当我们推回到首页后,推荐商品也为我们选出了相似物品;事实上当我们有过此类操作后,浏览器会将我们的操作信息保存到cookie上面。阿进而言之,cookie就是储存在用户本地终端上的数据。 4 | 5 | **Cookie的特点** 6 | 7 | 1. cookie保存在浏览器本地,只要不过期关闭浏览器也会存在。 8 | 2. 正常情况下cookie不加密,用户可轻松看到 9 | 3. 用户可以删除或者禁用cookie 10 | 4. cookie可以被篡改 11 | 5. cookie可用于攻击 12 | 6. cookie存储量很小,大小一般是4k 13 | 7. 发送请求自动带上登录信息 14 | 15 | ### 二、Cookie的安装及使用 16 | 17 | #### 1.安装 18 | 19 | ``` 20 | cnpm install cookie-parser --save 21 | ``` 22 | 23 | #### 2.引入 24 | 25 | ``` 26 | const cookieParser=require("cookie-parser"); 27 | ``` 28 | 29 | #### 3.设置中间件 30 | 31 | ``` 32 | app.use(cookieParser()); 33 | ``` 34 | 35 | #### 4.设置cookie 36 | 37 | ``` 38 | res.cookie("name",'zhangsan',{maxAge: 900000, httpOnly: true}); 39 | //res.cookie(名称,值,{配置信息}) 40 | ``` 41 | 42 | 关于设置cookie的参数说明: 43 | 44 | 1. domain: 域名 45 | 2. name=value:键值对,可以设置要保存的 Key/Value,注意这里的 name 不能和其他属性项的名字一样 46 | 3. Expires: 过期时间(秒),在设置的某个时间点后该 Cookie 就会失效,如 expires=Wednesday, 09-Nov-99 23:12:40 GMT。 47 | 4. maxAge: 最大失效时间(毫秒),设置在多少后失效 。 48 | 5. secure: 当 secure 值为 true 时,cookie 在 HTTP 中是无效,在 HTTPS 中才有效 。 49 | 6. Path: 表示 在那个路由下可以访问到cookie。 50 | 7. httpOnly:是微软对 COOKIE 做的扩展。如果在 COOKIE 中设置了“httpOnly”属性,则通过程序(JS 脚本、applet 等)将无法读取到COOKIE 信息,防止 XSS 攻击的产生 。 51 | 8. singed:表示是否签名cookie, 设为true 会对这个 cookie 签名,这样就需要用 res.signedCookies 而不是 res.cookies 访问它。被篡改的签名 cookie 会被服务器拒绝,并且 cookie 值会重置为它的原始值。 52 | 53 | #### 5.获取cookie 54 | 55 | ``` 56 | req.cookies.name; 57 | ``` 58 | 59 | 60 | 下面是一个基础实例: 61 | 62 | ``` 63 | const express=require("express"); 64 | const cookieParser=require("cookie-parser"); 65 | 66 | var app=express(); 67 | 68 | //设置中间件 69 | app.use(cookieParser()); 70 | 71 | app.get("/",function(req,res){ 72 | res.send("首页"); 73 | }); 74 | 75 | //设置cookie 76 | app.get("/set",function(req,res){ 77 | res.cookie("userName",'张三',{maxAge: 20000, httpOnly: true}); 78 | res.send("设置cookie成功"); 79 | }); 80 | 81 | //获取cookie 82 | app.get("/get",function(req,res){ 83 | console.log(req.cookies.userName); 84 | res.send("获取cookie成功,cookie为:"+ req.cookies.userName); 85 | }); 86 | 87 | app.listen(8080); 88 | ``` 89 | 90 | 91 | 当访问set路由后会设置cookie,当访问get路由后会获取到设置的cookie值。当然你也可以在其他页面继续获取当前cookie,以实现cookie共享。 92 | 93 | ### 三、多个二级域名共享cookie 94 | 95 | 只需要增加res.cookie中option对象的值,即可实现对相应路由下多个二级路由的cookie进行共享,代码如下: 96 | 97 | ``` 98 | const express=require("express"); 99 | const cookieParser=require("cookie-parser"); 100 | 101 | var app=express(); 102 | 103 | //设置中间件 104 | app.use(cookieParser()); 105 | 106 | app.get("/",function(req,res){ 107 | res.send("首页"); 108 | }); 109 | 110 | //设置cookie 111 | app.get("/set",function(req,res){ 112 | res.cookie("userName",'张三',{maxAge: 200000, httpOnly: true,domain: "ccc.com"}); 113 | res.send("设置cookie成功"); 114 | }); 115 | 116 | //获取cookie 117 | app.get("/get",function(req,res){ 118 | console.log(req.cookies.userName); 119 | res.send("获取cookie成功,cookie为:"+ req.cookies.userName); 120 | }); 121 | 122 | app.listen(8080); 123 | ``` 124 | 125 | 我们可以看到 126 | 127 | 不同的二级域名也能访问到相同的cookie,只要满足ccc.com这个顶级域名就行。 -------------------------------------------------------------------------------- /Express/day4/文档/7-Cookie加密.md: -------------------------------------------------------------------------------- 1 | ### 一、关于cookie加密 2 | 3 | cookie加密是让客户端用户无法的获取cookie明文信息,是数据安全的重要部分;一般的我们可以在保存cookie时对cookie信息进行加密,或者在res.cookie中对option对象的signed属性设置设置成true即可。 4 | 5 | ### 二、使用 signed 属性进行cookie加密 6 | 7 | 如下列代码: 8 | 9 | ``` 10 | const express = require("express"); 11 | const cookieParser = require("cookie-parser"); 12 | 13 | var app = express(); 14 | app.use(cookieParser('secret')); 15 | 16 | app.get("/",function(req,res){ 17 | res.send("主页"); 18 | }); 19 | 20 | //获取cookie 21 | app.use(function(req,res,next){ 22 | console.log(req.signedCookies.name); 23 | next(); 24 | }); 25 | 26 | //设置cookie 27 | app.use(function(req,res,next){ 28 | console.log(res.cookie("name","zhangsan",{httpOnly: true,maxAge: 200000,signed: true})); 29 | res.end("cookie为:"+req.signedCookies.name); 30 | }); 31 | 32 | app.listen(8080); 33 | ``` 34 | 35 | **签名原理** 36 | Express用于对cookie签名,而cookie-parser则是实现对签名的解析。实质是把cookie设置的值和cookieParser(‘secret’);中的secret进行hmac加密,之后和cookie值加“.”的方式拼接起来。 37 | 当option中signed设置为true后,底层会将cookie的值与“secret”进行hmac加密; 38 | 39 | **如何解析** 40 | cookie-parser中间件在解析签名cookie时做了两件事: 41 | 42 | 1. 将签名cookie对应的原始值提取出来 43 | 2. 验证签名cookie是否合法 44 | 45 | ### 3、直接对cookie值加密 46 | 47 | node为我们提供了一个核心安全模块“crypto”,它提供了很多安全相关的功能,如摘要运算、加密、电子签名等。 48 | 这是,我们便可很轻易的封装一个加密模块: 49 | 50 | ``` 51 | const crypto=require('crypto'); 52 | 53 | module.exports={ 54 | //MD5封装 55 | MD5_SUFFIX:'s5w84&&d4d473885s2025s5*4s2', 56 | md5:function(str){ 57 | var obj=crypto.createHash('md5'); 58 | obj.update(str); 59 | return obj.digest('hex'); 60 | } 61 | } 62 | ``` 63 | 64 | 65 | 之后只需要进行相应导入即可 66 | 67 | ``` 68 | const common=require('./MD5'); 69 | 70 | var str='123456'; 71 | var str=common.md5(str+'s5w84&&d4d473885s2025s5*4s2'); 72 | console.log(str); 73 | ``` 74 | 75 | 76 | 设置cookie代码如下: 77 | 78 | ``` 79 | const express=require("express"); 80 | const cookieParser=require("cookie-parser"); 81 | var cry = require('./md5'); 82 | 83 | var app=express(); 84 | 85 | var str='hello-123'; 86 | var str=cry.md5(str+'s5w84&&d4d473885s2025s5*4s2'); 87 | 88 | //设置中间件 89 | app.use(cookieParser()); 90 | 91 | //获取加密cookie 92 | app.use(function(req,res,next){ 93 | console.log(req.cookies.userName); 94 | next(); 95 | }); 96 | 97 | //设置并加密cookie 98 | app.use(function(req,res,next){ 99 | res.cookie("userName", str, {maxAge: 5*60*1000, httpOnly: true}); 100 | res.end("set ok"); 101 | }); 102 | 103 | app.listen(8080); 104 | ``` 105 | 106 | 如果是在判断登录时,只需将用户输入的账号进行同样加密操作在进行比较即可知道账户是否正确。 107 | crypto所涉及的加密方式有很多,推荐大家都写模块引用,这样更方便后期的维护。 108 | 109 | -------------------------------------------------------------------------------- /Express/day4/文档/8-session应用.md: -------------------------------------------------------------------------------- 1 | ### 一、关于session 2 | 3 | session是另一种记录客户状态的机制,与cookie保存在客户端浏览器不同,session保存在服务器当中; 4 | 当客户端访问服务器时,服务器会生成一个session对象,对象中保存的是key:value值,同时服务器会将key传回给客户端的cookie当中;当用户第二次访问服务器时,就会把cookie当中的key传回到服务器中,最后服务器会吧value值返回给客户端。 5 | 因此上面的key则是全局唯一的标识,客户端和服务端依靠这个全局唯一的标识来访问会话信息数据。 6 | 7 | ### 二、设置session 8 | 9 | 我们使用express-session模块来设置session 10 | 11 | ##### 1.安装express-session 12 | 13 | ``` 14 | cnpm install express-session --save 15 | ``` 16 | 17 | ##### 2.引入express-session模块 18 | 19 | ``` 20 | const session=require("express-session"); 21 | ``` 22 | 23 | ##### 3.设置session 24 | 25 | ``` 26 | session(options); 27 | ``` 28 | 29 | 30 | 如下列代码: 31 | 32 | ``` 33 | const express=require("express"); 34 | const session=require("express-session"); 35 | 36 | var app=express(); 37 | 38 | //配置中间件 39 | app.use(session({ 40 | secret: "keyboard cat", 41 | resave: false, 42 | saveUninitialized: true, 43 | cookie: ('name', 'value',{maxAge: 5*60*1000,secure: false}) 44 | })); 45 | 46 | app.use('/login',function(req,res){ 47 | //设置session 48 | req.session.userinfo='张三'; 49 | res.send("登陆成功!"); 50 | }); 51 | 52 | app.use('/',function(req,res){ 53 | //获取session 54 | if(req.session.userinfo){ 55 | res.send("hello "+req.session.userinfo+",welcome"); 56 | }else{ 57 | res.send("未登陆"); 58 | } 59 | }); 60 | 61 | app.listen(8080); 62 | ``` 63 | 64 | 在session(option)中对session进行设置 65 | 66 | ### 三、session的常用方法 67 | 68 | ``` 69 | //设置session 70 | req.session.username="张三" 71 | 72 | //获取session 73 | req.session.username 74 | 75 | //重新设置cookie的过期时间 76 | req.session.cookie.maxAge=1000; 77 | 78 | //销毁session 79 | req.session.destroy(function(err){ 80 | 81 | }) 82 | ``` 83 | 84 | 85 | 以下演示通过销毁session的方式来退出登录 86 | 87 | ``` 88 | const express=require("express"); 89 | const session=require("express-session"); 90 | 91 | var app=express(); 92 | 93 | //配置中间件 94 | app.use(session({ 95 | secret: "keyboard cat", 96 | resave: false, 97 | saveUninitialized: true, 98 | cookie: ('name', 'value',{ maxAge: 5*60*1000, 99 | secure: false, 100 | name: "seName", 101 | resave: false}) 102 | })); 103 | 104 | app.use('/login',function(req,res){ 105 | //设置session 106 | req.session.userinfo='张三'; 107 | res.send("登陆成功!"); 108 | }); 109 | 110 | app.use('/loginOut',function(req,res){ 111 | //注销session 112 | req.session.destroy(function(err){ 113 | res.send("退出登录!"+err); 114 | }); 115 | }); 116 | 117 | app.use('/',function(req,res){ 118 | //获取session 119 | if(req.session.userinfo){ 120 | res.send("hello "+req.session.userinfo+",welcome to index"); 121 | }else{ 122 | res.send("未登陆"); 123 | } 124 | }); 125 | 126 | app.listen(8080); 127 | ``` 128 | 129 | 当我们进入到主页时,未显示任何信息,进入login路由后,自动设置session,这是回到主页则显示session信息,之后进入loginOut路由已注销session信息,再回到首页显示为登陆。 130 | 131 | -------------------------------------------------------------------------------- /Express/day4/文档/sobooks登陆功能.md: -------------------------------------------------------------------------------- 1 | # sobooks登陆功能 2 | 3 | ### 1-引入session和cookie相关模块 4 | 5 | ``` 6 | var cookieParser = require('cookie-parser'); 7 | //引入session模块 8 | let session = require('express-session'); 9 | ``` 10 | 11 | ### 2-引入session 12 | 13 | ``` 14 | app.use(session({ 15 | secret: "xzsagjasoigjasoi", 16 | resave:true,//强制保存session 17 | cookie:{ 18 | maxAge:7*24*60*60*1000,//设置session的有效期为1周 19 | }, 20 | saveUninitialized:true//是否保存初始化的session 21 | })) 22 | ``` 23 | 24 | ### 3-引入cookie中间件 25 | 26 | ``` 27 | app.use(cookieParser('secret')); 28 | ``` 29 | 30 | ### 4-写判断是否登陆的中间件 31 | 32 | ``` 33 | function isLoginMid(req,res,next){ 34 | if(req.session.username==undefined){ 35 | res.render('info',{ 36 | title:"未登录", 37 | content:"尚未登陆,请进入登陆页面登陆", 38 | href:"/login", 39 | hrefTxt:"登录页" 40 | }) 41 | }else{ 42 | //一登录进入正常页面 43 | next() 44 | } 45 | } 46 | ``` 47 | 48 | ### 5-引入一个跳转的模板信息页面 49 | 50 | 可以显示登陆成功或者失败的信息内容,并且可以在一定时间内进行跳转。 51 | 52 | ```HTML 53 | 54 | 55 | 56 | 57 | 58 | 59 | <%-title%> 60 | 61 | 62 |

<%-title%>

63 |

<%-content%>

64 |

5秒后跳转至:<%-hrefTxt%>

65 | 76 | 77 | 78 | ``` 79 | 80 | ### 6-登陆页面 81 | 82 | ```javascript 83 |
84 |
85 | 86 |
87 |
88 | 89 |
90 |

忘记密码?

91 | 92 |
93 | ``` 94 | 95 | ### 7-处理POST方式提交的请求 96 | 97 | 1-获取表单提交的数据 98 | 99 | 2-查询表单提交的账号密码是否正确 100 | 101 | 3-如果正确,设置session,req.session.username = user.username; 102 | 103 | 4-显示登陆是否成功信息 104 | 105 | ``` 106 | router.post('/',async function(req,res){ 107 | console.log(req.body) 108 | //根据提交的邮箱和密码判断是否是正确的账号密码 109 | let strSql = "select * from user where mail=? and password = ?" 110 | let arr = [req.body.mail,req.body.password] 111 | let result = await sqlQuery(strSql,arr) 112 | if(result.length!=0){ 113 | //登陆成功 114 | user = result[0]; 115 | req.session.username = user.username; 116 | res.render('info',{ 117 | title:"登陆成功", 118 | content:"账号密码正确,即将进入首页", 119 | href:"/", 120 | hrefTxt:"首页" 121 | }) 122 | }else{ 123 | res.render('info',{ 124 | title:"登陆失败", 125 | content:"账号或密码不正确,即将进入登录页", 126 | href:"/login", 127 | hrefTxt:"登录页" 128 | }) 129 | } 130 | }) 131 | ``` 132 | 133 | -------------------------------------------------------------------------------- /Express/day5/文档/11-文件上传.md: -------------------------------------------------------------------------------- 1 | ### 一、multer中间件 2 | 3 | 再上传文件时,我们通常会使用到他。Multer用于处理multipart/form-data 类型的表单数据。首先我们先安装它: 4 | 5 | ``` 6 | cnpm install multer --save 7 | ``` 8 | 9 | ### 二、使用 10 | 11 | 首先在form表单中我们需要设置enctype为:multipart/form-data表单类型。同时我们也需要用到fs模块对文件重命名。下面是单文件上传实例: 12 | 13 | ``` 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 |

24 | 25 |
26 | 27 | 28 | ``` 29 | 30 | NODE代码: 31 | 32 | ``` 33 | const express=require("express"); 34 | const multer=require('multer'); 35 | //初始化上传对象 36 | var upload=multer({dest:'./upload/'}); 37 | var fs = require('fs'); 38 | 39 | 40 | var app=express(); 41 | 42 | app.use("/",upload.single("files"),function(req,res){ //files为input type="file"的name值 43 | var oldFile=req.file.destination+req.file.filename; //指定旧文件 44 | var newFile=req.file.destination + req.file.originalname; //指定新文件 45 | fs.rename(oldFile,newFile,function(err){ 46 | if(err){ 47 | res.send('上传失败!'); 48 | }else{ 49 | res.send('上传成功!'); 50 | } 51 | }); 52 | }); 53 | 54 | app.listen(8080); 55 | ``` 56 | 57 | 58 | 在这里我们可以指定单个文件上传到根目录的upload文件夹里。这里值得注意的是req.file会返回文件的基本信息: 59 | 60 | ``` 61 | fieldname: ***, //input type="file"的name值 62 | originalname: ***, //用户计算机上的文件的名称 63 | encoding: '***', //文件编码 64 | mimetype: ***', //文件的 MIME 类型 65 | destination: './***/', //保存路径 66 | filename: ***, //保存在 destination 中的文件名 67 | path: ***, //已上传文件的完整路径 68 | size: ** //文件大小(字节单位) 69 | ``` 70 | 71 | 72 | 73 | ### 三、上传多个文件 74 | 75 | 在HTML找中input type="file"需要加上multiple来实现过滤,multiple不写参数则可以读取·所有文件。而在服务端上,我们需要将single()改为array(“name”,num);的形式来接收多个文件的上传请求。最后对他们全部进行重命名。在这之前我们首先看看multer支持哪些文件上传方式: 76 | 77 | ``` 78 | .single(fieldname) //接受一个以 fieldname 命名的文件。.fields(fields) 79 | .array(fieldname[, maxCount]) //接受一个以 fieldname 命名的文件数组。可以配置 maxCount 来限制上传的最大数量。 80 | .fields(fields) //接受指定 fields 的混合文件。fields是一个拥有name和maxCount的数组对象。 81 | .none() //只接受文本域。如果任何文件上传到这个模式,将发生 "LIMIT_UNEXPECTED_FILE" 错误。 82 | .any() //接受一切上传的文件。 83 | ``` 84 | 85 | 下面我们将会演示如何上传多个文件: 86 | html: 87 | 88 | ``` 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 |
97 | 98 |

99 | 100 |
101 | 102 | 103 | ``` 104 | 105 | node代码: 106 | 107 | ``` 108 | const express=require("express"); 109 | const multer=require('multer'); 110 | var upload=multer({dest:'./upload/'}); 111 | var fs = require('fs'); 112 | 113 | 114 | var app=express(); 115 | 116 | app.use("/",upload.array("files",5),function(req,res,next){ 117 | req.files.forEach(function(ele,index){ 118 | console.log(ele); 119 | var oldFile=ele.destination+ele.filename; //指定旧文件 120 | var newFile=ele.destination+ele.originalname; //指定新文件 121 | fs.rename(oldFile,newFile,function(err){ 122 | err?console.log('上传失败!'):console.log('上传成功!'); 123 | }); 124 | }); 125 | res.send("成功上传"); 126 | }); 127 | 128 | app.listen(8080); 129 | ``` 130 | 131 | 132 | 这里,我们获取文件信息是通过req.files来获取,他是由数组构成的对象,之后用foreach循环对其进行重命名即可。 133 | 134 | ### 四、通过limits来限制上传文件 135 | 136 | Multer通过使用limits这个对象来对数据进行限制,它允许使用以下参数: 137 | 138 | ``` 139 | Key Description Default 140 | fieldNameSize field 名字最大长度 100 bytes 141 | fieldSize field 值的最大长度 1MB 142 | fields 非文件 field 的最大数量 无限 143 | fileSize 在 multipart 表单中,文件最大长度 (字节单位) 无限 144 | files 在 multipart 表单中,文件最大数量 无限 145 | parts 在 multipart 表单中,part 传输的最大数量(fields + files) 无限 146 | headerPairs 在 multipart 表单中,键值对最大组数 2000 147 | 如果你上传的文件超出这些设定,MulterError模块将会启用,该模块在node_modules/multer/lib/multer-error.js上: 148 | ``` 149 | 150 | 我们可以使用err.code定位到该错误,他有7种code方式,不同设置会返回不同code; 151 | 152 | ``` 153 | LIMIT_PART_COUNT 154 | LIMIT_FILE_SIZE 155 | LIMIT_FILE_COUNT 156 | LIMIT_FIELD_KEY 157 | LIMIT_FIELD_VALUE 158 | LIMIT_FIELD_COUNT 159 | LIMIT_FIELD_COUNT 160 | ``` 161 | 162 | 163 | 下面就给大家做个简单实例: 164 | html依然不变,js代码如下: 165 | 166 | ``` 167 | const express=require("express"); 168 | const multer=require('multer'); 169 | var upload=multer({dest:'./upload/',limits:{fileSize: 1024 * 1024 * 20,files: 5}}); 170 | var fs = require('fs'); 171 | 172 | var app=express(); 173 | 174 | app.use("/",upload.array("files",5),function(req,res,next){ 175 | req.files.forEach(function(ele,index){ 176 | var oldFile=ele.destination+ele.filename; //指定旧文件 177 | var newFile=ele.destination+ele.originalname; //指定新文件 178 | fs.rename(oldFile,newFile,function(err){ 179 | err?console.log('上传失败!'):console.log('上传成功!'); 180 | }); 181 | }); 182 | next(); 183 | res.send("上传成功!"); 184 | }); 185 | 186 | app.use(function(err,req,res,next){ 187 | if (err.code==='LIMIT_FILE_SIZE'){ 188 | res.send('File is too large'); 189 | }else if(err.code==='LIMIT_FILE_COUNT'){ 190 | res.send('Too many files'); 191 | } 192 | }) 193 | 194 | app.listen(8080); 195 | ``` 196 | 197 | 198 | 199 | -------------------------------------------------------------------------------- /Express/day5/文档/12-文件下载.md: -------------------------------------------------------------------------------- 1 | ### 文件下载 2 | 3 | 文件下载非常简单,仅需通过res.download()执行即可,他可以写为3种形式: 4 | 5 | ``` 6 | res.download('/report-12345.pdf'); 7 | ``` 8 | 9 | 以下是一个对选择对应文件进行下载的实例: 10 | html: 11 | 12 | ``` 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |

22 | 23 |
24 | 25 | 26 | ``` 27 | 28 | 29 | js: 30 | 31 | ``` 32 | const express=require("express"); 33 | const bodyParser=require("body-parser"); 34 | 35 | var app=express(); 36 | 37 | var jsonParser = bodyParser.json(); 38 | var urlencodedParser = bodyParser.urlencoded({ extended: false }); 39 | 40 | app.post('/',urlencodedParser,function(req,res){ 41 | res.download("./public/"+req.body.files,err=>{ 42 | if(err){ 43 | res.send("下载失败!"); 44 | }else{ 45 | console.log("下载成功!"); 46 | } 47 | }); 48 | }); 49 | 50 | app.listen(8080); 51 | ``` 52 | 53 | 我们可以选择根目录public下的文件对其进行下载。 54 | 55 | -------------------------------------------------------------------------------- /Express/day5/文档/13-AJAX上传图片.md: -------------------------------------------------------------------------------- 1 | # AJAX上传图片 2 | 3 | ### 图片上传实现步骤 4 | 5 | #### 图片上传 6 | 7 | 通过jquery监听input change事件,这样我们可以获取到上传的图片流信息,从而可以获取到图片的地址、大小、格式以及名称等信息 8 | 9 | 这里创建3个数组,imgName、imgSrc、imgFile分别用于存放上传图片的名称、url地址以及图片流信息 10 | 11 | ``` 12 | var fileList = this.files; 13 | for(var i = 0; i < fileList.length; i++) { 14 | var imgSrcI = getObjectURL(fileList[i]); 15 | imgName.push(fileList[i].name); 16 | imgSrc.push(imgSrcI); 17 | imgFile.push(fileList[i]); 18 | } 19 | ``` 20 | 21 | getObjectURL方法是一个用于获取本地图片的地址,使用该url可以显示图片 22 | 23 | ``` 24 | function getObjectURL(file) { 25 | var url = null ; 26 | if (window.createObjectURL!=undefined) { // basic 27 | url = window.createObjectURL(file) ; 28 | } else if (window.URL!=undefined) { // mozilla(firefox) 29 | url = window.URL.createObjectURL(file) ; 30 | } else if (window.webkitURL!=undefined) { // webkit or chrome 31 | url = window.webkitURL.createObjectURL(file) ; 32 | } 33 | return url ; 34 | } 35 | ``` 36 | 37 | #### 控制上传图片大小、格式以及上传数量 38 | 39 | ``` 40 | $('#upload').on('change',function(){ 41 | if(imgSrc.length==4){ 42 | return alert("最多只能上传4张图片"); 43 | } 44 | var imgSize = this.files[0].size; //b 45 | if(imgSize>1024*1024*1){//1M 46 | return alert("上传图片不能超过1M"); 47 | } 48 | if(this.files[0].type != 'image/png' && this.files[0].type != 'image/jpeg' && this.files[0].type != 'image/gif'){ 49 | return alert("图片上传格式不正确"); 50 | } 51 | }) 52 | ``` 53 | 54 | #### 图片预览 55 | 56 | 创建一个addNewContent方法用于动态展示添加的图片实现图片预览,在每次上传图片的时候调用该方法 57 | 58 | ``` 59 | function addNewContent(obj) { 60 | $(obj).html(""); 61 | for(var a = 0; a < imgSrc.length; a++) { 62 | var oldBox = $(obj).html(); 63 | $(obj).html(oldBox + '
  • '); 64 | } 65 | } 66 | ``` 67 | 68 | #### 图片删除 69 | 70 | 1.通过监听鼠标的mouseover事件,显示图片删除按钮 71 | 72 | ``` 73 | $('.content-img-list').on('mouseover','.content-img-list-item',function(){ 74 | $(this).children('a').removeClass('hide'); 75 | }); 76 | ``` 77 | 78 | 2.监听鼠标的mouseleave事件,隐藏图片删除按钮 79 | 80 | ``` 81 | $('.content-img-list').on('mouseleave','.content-img-list-item',function(){ 82 | $(this).children('a').addClass('hide'); 83 | }); 84 | ``` 85 | 86 | 3.获取图片index下标属性,通过js的splice方法删除数组元素,重新调用addNewContent方法遍历图片数组显示预览图片 87 | 88 | ``` 89 | $(".content-img-list").on("click",'.content-img-list-item a',function(){ 90 | var index = $(this).attr("index"); 91 | imgSrc.splice(index, 1); 92 | imgFile.splice(index, 1); 93 | imgName.splice(index, 1); 94 | var boxId = ".content-img-list"; 95 | addNewContent(boxId); 96 | if(imgSrc.length<4){//显示上传按钮 97 | $('.content-img .file').show(); 98 | } 99 | }); 100 | ``` 101 | 102 | #### 图片上传提交 103 | 104 | 这里主要使用FormData来拼装好数据参数,提交到后台 105 | 106 | ``` 107 | var formFile = new FormData(); 108 | ``` 109 | 110 | 遍历imgFile图片流数组拼装到FormData中 111 | 112 | ``` 113 | $.each(imgFile, function(i, file){ 114 | formFile.append('myFile[]', file); 115 | }); 116 | ``` 117 | 118 | 添加其他参数 119 | 120 | ``` 121 | formFile.append("type", type); 122 | formFile.append("content", content); 123 | formFile.append("mobile", mobile); 124 | ``` 125 | 126 | 最后使用ajax提交内容 127 | 128 | ``` 129 | $.ajax({ 130 | url: 'http://zhangykwww.yind123.com/webapi/feedback', 131 | type: 'POST', 132 | data: formFile, 133 | async: true, 134 | cache: false, 135 | contentType: false, 136 | processData: false, 137 | // traditional:true, 138 | dataType:'json', 139 | success: function(res) { 140 | console.log(res); 141 | } 142 | }) 143 | ``` 144 | 145 | 以上就实现了图片上传、图片预览和图片删除的功能 146 | 147 | jquery设置 ajax属性 148 | 149 | ``` 150 | processData : false, // 告诉jQuery不要去处理发送的数据 151 | contentType : false,// 告诉jQuery不要去设置Content-Type请求头 152 | ``` -------------------------------------------------------------------------------- /Express/day5/文档/sobook注册.md: -------------------------------------------------------------------------------- 1 | # sobook注册 2 | 3 | ### 1-注册页 4 | 5 | 1-设置表单 6 | 7 | ```HTML 8 |
    9 |
    10 | 11 |
    12 |
    13 | 14 |
    15 |
    16 | 17 |
    18 |
    19 | 20 |
    21 | 22 |

    登陆

    23 | 24 |
    25 | ``` 26 | 27 | 2-前端校验表单数据 28 | 29 | ```javascript 30 | var formDiv = document.querySelector('form'); 31 | var inputs = document.querySelectorAll('form input'); 32 | var btn = document.querySelector('#registerBtn'); 33 | formDiv.oninput = function(){ 34 | //判断是否有内容为空 35 | isAble = true; 36 | inputs.forEach((item,i)=>{ 37 | if(item.value==""){ 38 | isAble = false; 39 | } 40 | }) 41 | //正则匹配邮箱地址 42 | let reg = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/ 43 | //密码与再次输入的密码是否匹配 44 | if(inputs[2].value==inputs[3].value&&isAble&®.test(inputs[0].value)){ 45 | btn.disabled = false; 46 | btn.style.cursor = 'pointer' 47 | }else{ 48 | btn.disabled = true; 49 | btn.style.cursor = 'not-allow' 50 | } 51 | } 52 | ``` 53 | 54 | 55 | 56 | ### 2-注册页的路由 57 | 58 | 1-正常浏览器GET请求的路由 59 | 60 | ``` 61 | router.get('/', function(req, res, next) { 62 | res.render('register') 63 | }); 64 | ``` 65 | 66 | 2-表单提交的POST请求路由,先判断是否已注册,没有注册即将数据插入到数据库 67 | 68 | ```javascript 69 | router.post('/',async function(req,res){ 70 | //获取表单提交的邮箱,密码,用户名 71 | console.log(req.body) 72 | let mail = req.body.mail; 73 | let password = jiami(req.body.password); 74 | let username = req.body.username; 75 | //判断邮箱是否已注册,如果已注册,将不在注册; 76 | let strSql = "select * from user where mail=?" 77 | let result = await sqlQuery(strSql,[mail]) 78 | if(result.length!=0){ 79 | //邮箱已注册 80 | res.render('info',{ 81 | title:"注册失败", 82 | content:"此邮箱已注册过,可直接登陆,或找寻密码", 83 | href:"/register", 84 | hrefTxt:"注册页" 85 | }) 86 | 87 | }else{ 88 | //此邮箱尚未注册,可注册 89 | strSql = "insert into user (mail,username,password) values (?,?,?)" 90 | await sqlQuery(strSql,[mail,username,password]) 91 | res.render('info',{ 92 | title:"注册成功", 93 | content:"注册成功请登陆,即将进入登陆页面", 94 | href:"/login", 95 | hrefTxt:"登录页" 96 | }) 97 | } 98 | }) 99 | ``` 100 | 101 | 102 | 103 | ### 3-加密密码并保存至数据库 104 | 105 | 加密: 106 | 107 | ``` 108 | function jiami(str){ 109 | let salt = "fjdsoigijasoigjasdiodgjasdiogjoasid" 110 | let obj = crypto.createHash('md5') 111 | str = salt+str; 112 | obj.update(str) 113 | return obj.digest('hex') 114 | } 115 | ``` 116 | 117 | ### 4-修改登陆也为加密操作 118 | 119 | ```javascript 120 | function jiami(str){ 121 | let salt = "fjdsoigijasoigjasdiodgjasdiogjoasid" 122 | let obj = crypto.createHash('md5') 123 | str = salt+str; 124 | obj.update(str) 125 | return obj.digest('hex') 126 | } 127 | 128 | router.post('/',async function(req,res){ 129 | console.log(req.body) 130 | //根据提交的邮箱和密码判断是否是正确的账号密码 131 | let strSql = "select * from user where mail=? and password = ?" 132 | let arr = [req.body.mail,jiami(req.body.password)] 133 | let result = await sqlQuery(strSql,arr) 134 | if(result.length!=0){ 135 | //登陆成功 136 | user = result[0]; 137 | req.session.username = user.username; 138 | res.render('info',{ 139 | title:"登陆成功", 140 | content:"账号密码正确,即将进入首页", 141 | href:"/", 142 | hrefTxt:"首页" 143 | }) 144 | }else{ 145 | res.render('info',{ 146 | title:"登陆失败", 147 | content:"账号或密码不正确,即将进入登录页", 148 | href:"/login", 149 | hrefTxt:"登录页" 150 | }) 151 | } 152 | 153 | }) 154 | ``` 155 | 156 | -------------------------------------------------------------------------------- /Node/day1/文档/01.学习Node能够为我们带来什么.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 01.学习Node能够为我们带来什么 226 | 227 | 228 |

    Node在一线企业中的运用

    作为中间层

    我们通常说前端和后端,前端负责用户界面,而后端负责提供数据和业务接口。现在我们在两者间加入一层,前端并不是直接去请求后端业务接口,而是请求到中间层。再由中间层去请求业务接口,看一下示意图:

    团片

    整个流程可以描述为:客户端直接请求到中间层的Node服务,Node服务分析请求,看需要哪个页面,再去请求对应数据,拿到数据后和模版结合成用户看到页面,再给到客户端。

    那么有的人可能会觉得,这种模式不是更麻烦了吗?其实不然,我们来看看 中间层的优点

    1. 减轻客户端内存,项目用户体验好。不会像mvvm模式的项目把页面渲染和数据请求都压在客户端,而是在服务端完成。
    2. SEO性好,不像mvvm模式页面由js生成,而是在服务器渲染好html 字符,有利于网页被搜索到。
    3. 保持了前后端分离的优点和目的,即解放后端,后端可以继续以接口的形式写业务代码。
    4. 前端可以操控的范围增多,甚至可以做服务器,数据库层面的优化,比如中间层中常常用nginx,redis来优化项目,应对高并发。

    中间层模式是一种开发模式上的进步,为什么这么好的模式我从来没有听说过呢?因为这种模式成本过高,如果没有一定量级的项目没必要去采用。

    做项目构建工具

    这里说的项目构建工具,我相信大家都用过,我们的webpack,vue-cli都是输入项目构建工具。那么大家觉得这一类工具神奇好用方便的同时,有没有想过这些工具是拿什么语言写的?其实它们并不难,这些工具都是用Node来写的。

    很多公司都会开发自己公司的项目构建工具,帮助公司项目做的更标准更方便,一个好的项目构建工具,会极大的加快整个公司的项目开发效率。

    这一类的项目构建工具一般都要很多的文件操作,Node对于i/o流的操作,在目前的主流后端语言中数一数二。所以越来越多的公司选择用Node来做项目构建工具。

    做一些小型网站后端

    用Node做后端,可能是大多数人认为的Node作用。其实真正在企业之中,很少会让你去用Node去做后端。 所以一般来说都是做一些小型或者个人站的后端。

    229 | 230 | -------------------------------------------------------------------------------- /Node/day1/文档/02.Node.js简介.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 02.Node.js简介 226 | 227 | 228 |
    1. Node.js是什么?

    Node.js 诞生于 2009 年,由 Joyent 的员工 Ryan Dahl 开发而成, 目前官网最新版本已经更新到 12.0.0版本,最新稳定的是10.15.3。Node.js 不是一门语言也不是框架,它只是基于 Google V8 引擎的 JavaScript 运行时环境,同时结合 Libuv 扩展了 JavaScript 功能,使之支持 io、fs 等只有语言才有的特性,使得 JavaScript 能够同时具有 DOM 操作(浏览器)和 I/O、文件读写、操作数据库(服务器端)等能力,是目前最简单的全栈式语言。

    这里我们可以简单理解Node.js是一个内置有chrome V8引擎的JavaScript运行环境,他可以使原本在浏览器中运行的JavaScript有能力跑后端,从而操作我们数据库,进行文件读写等。

    目前市面上高密集的I/O模型,比如 Web 开发,微服务,前端构建等都有做Node.js的身影。不少大型网站都是使用 Node.js 作为后台开发语言的,比如 淘宝 双十一、去哪儿网 的 PC 端核心业务等。另外我们一些前端工具譬如VSCode,webpack等也是有Node.js开发。

    Node.js的包管理工具,npm已经成为世界开源包管理中最大的生态,功能强大,目前单月使用者接近1000万。

    1. Node.js特点(记住三句话)
    • 事件驱动
    • 非阻塞IO模型(异步)
    • 轻量和高效
    229 | 230 | -------------------------------------------------------------------------------- /Node/day1/文档/04.使用Nodemon自动重启项目.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 04.使用Nodemon自动重启项目 226 | 227 | 228 |

    我们在开发的过程中,每次改完代码之后都必须重启服务器,显然这样的操作效率是比较低,这里给大家推荐个工具,nodemon,nodemon可以帮我们实时监听项目中代码的变化,并且自动重启服务,而且配置简单。

    1. 安装:npm install -g nodemon
    2. 使用nodemon运行项目,取代之前的node app.js

    nodemon [your app.js]

    项目运行之后,nodemon会自动监听代码的改动,并且重新启动服务,大大增加我们开发效率。

    1. nodemon常见配置
    • 在命令行指定应用的端口号:nodemon ./server.js localhost 8080
    • 查看帮助,帮助里面有很多选项都是一目了然:nodemon -h 或者 nodemon --help
    • 运行 debug 模式:nodemon --debug ./server.js 80
    • 手动重启项目: Nodemon 命令运行的终端 窗口中输入 rs 两个字符,然后再按下回车键,就能手动重启 Nodemon了。

     

    229 | 230 | -------------------------------------------------------------------------------- /Node/day1/文档/08.npm常见命令.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 08.npm常见命令 226 | 227 | 228 |

    npm英文全称:node package manager,npm 为你和你的团队打开了连接整个 JavaScript 天才世界的一扇大门。它是世界上最大的软件注册表,每星期大约有 30 亿次的下载量,包含超过 600000 个 包(package) (即,代码模块)。来自各大洲的开源软件开发者使用 npm 互相分享和借鉴。包的结构使您能够轻松跟踪依赖项和版本。 我们平时开发项目都是需要使用npm下载依赖,常见的npm命令总结如下:

    1. npm -v:查看npm版本。
    2. npm init:初始化后会出现一个package.json配置文件。可以在后面加上-y ,快速跳过问答式界面。
    3. npm install:会根据项目中的package.json文件自动下载项目所需的全部依赖。
    4. npm install 包名 --save-dev(npm install 包名 -D):安装的包只用于开发环境,不用于生产环境,会出现在package.json文件中的devDependencies属性中。
    5. npm install 包名 --save(npm install 包名 -S):安装的包需要发布到生产环境的,会出现在package.json文件中的dependencies属性中。
    6. npm list:查看当前目录下已安装的node包。
    7. npm list -g:查看全局已经安装过的node包。
    8. npm --help:查看npm帮助命令。
    9. npm update 包名:更新指定包。
    10. npm uninstall 包名:卸载指定包。
    11. npm config list:查看配置信息。
    12. npm 指定命令 --help:查看指定命令的帮助。
    13. npm info 指定包名:查看远程npm上指定包的所有版本信息。
    14. npm config set registry https://registry.npm.taobao.org: 修改包下载源,此例修改为了淘宝镜像。
    15. npm root:查看当前包的安装路径。
    16. npm root -g:查看全局的包的安装路径。
    17. npm ls 包名:查看本地安装的指定包及版本信息,没有显示empty。
    18. npm ls 包名 -g:查看全局安装的指定包及版本信息,没有显示empty。

     

    229 | 230 | -------------------------------------------------------------------------------- /Node/day7/1-根据数据和模板动态生成页面.md: -------------------------------------------------------------------------------- 1 | # 根据数据和模板动态生成页面 2 | 3 | 1. 根据规则去解析链接,并且获取ID或者时索引值 4 | 5 | ``` 6 | //请求路径:http://127.0.0.1/movies/0 7 | let index = req.pathObj.base; 8 | ``` 9 | 10 | 2. 根据索引获取数据 11 | 12 | ``` 13 | let movies = [ 14 | { 15 | name:"雪暴", 16 | brief:"电影《雪暴》讲述了在一座极北的边陲小镇,一伙穷凶极恶、作案手法老到的悍匪为抢夺黄金,打劫运金车,并借助大雪掩盖了所有犯罪痕迹。为了探求真相,警察王康浩暗地里搜集证据,熟悉地形,终于在一场灾难级的暴雪降临时,与谋财害命的悍匪发生了惊心动魄的正面对决……", 17 | author:"张震" 18 | },{ 19 | name:"少年的你", 20 | brief:"陈念(周冬雨 饰)是一名即将参加高考的高三学生,同校女生胡晓蝶(张艺凡 饰)的跳楼自杀让她的生活陷入了困顿之中。胡晓蝶死后,陈念遭到了以魏莱(周也 饰)为首的三人组的霸凌,魏莱虽然表面上看来是乖巧的优等生,实际上却心思毒辣,胡晓蝶的死和她有着千丝万缕的联系。", 21 | author:"周冬雨 " 22 | } 23 | ] 24 | let pageData = movies[index] 25 | ``` 26 | 27 | 3. 根据模板渲染页面 28 | 29 | ``` 30 | res.render(movies[index],'./template/index.html') 31 | ``` 32 | 33 | 4. 底层需要实现渲染函数,通过正则匹配,找到需要修改的地方进行一一的修改。 34 | 35 | ```javascript 36 | function render(options,path){ 37 | fs.readFile(path,{encoding:"utf-8",flag:"r"},(err,data)=>{ 38 | if(err){ 39 | console.log(err) 40 | }else{ 41 | console.log(data) 42 | let reg = /\{\{(.*?)\}\}/igs 43 | let result; 44 | while(result = reg.exec(data)){ 45 | //去除2边的空白 46 | let strKey = result[1].trim() 47 | let strValue = options[strKey] 48 | data = data.replace(result[0],strValue) 49 | } 50 | 51 | this.end(data) 52 | } 53 | }) 54 | } 55 | ``` 56 | 57 | -------------------------------------------------------------------------------- /Node/day7/2-列表的动态渲染.md: -------------------------------------------------------------------------------- 1 | # 列表的动态渲染 2 | 3 | ### 1. 定义了列表循环的标记 4 | 5 | ``` 6 | {%for {stars} %} 7 |
  • 8 |

    姓名:{{item}}

    9 |
  • 10 | {%endfor%} 11 | ``` 12 | 13 | ### 2. 正则匹配标记 14 | 15 | ``` 16 | let reg = /\{\%for \{(.*?)\} \%\}(.*?)\{\%endfor\%\}/igs 17 | ``` 18 | 19 | 匹配到2个组 20 | 21 | 1. 第一个组时匹配出变量的key值 22 | 2. 第二个组匹配出需要生成的每一项的内容 23 | 24 | ### 3. 匹配替换每一项的内容 25 | 26 | ```javascript 27 | while(result = reg.exec(data)){ 28 | let strKey = result[1].trim(); 29 | //通过KEY值获取数组内容 30 | let strValueArr = options[strKey] 31 | let listStr = "" 32 | strValueArr.forEach((item,i)=>{ 33 | //替换每一项内容里的变量 34 | listStr = listStr + replaceVar(result[2],{"item":item}) 35 | }) 36 | data = data.replace(result[0],listStr) 37 | } 38 | ``` 39 | 40 | ### 4. 通过eval函数,将字符串的表达式计算出来 41 | 42 | ``` 43 | let strValue = eval('options.'+strKey); 44 | ``` 45 | 46 | -------------------------------------------------------------------------------- /Node/day7/3-正则路由的设定.md: -------------------------------------------------------------------------------- 1 | # 正则路由的设定 2 | 3 | 要求:可以根据自己设定的正则匹配路径来执行相对应的函数来响应用户的内容。 4 | 5 | ### 1.设定正则的匹配路径和响应的执行函数 6 | 7 | ``` 8 | app.on('^/$',(req,res)=>{ 9 | res.setHeader("content-type","text/html;charset=utf-8"); 10 | res.end("

    这是首页

    ") 11 | }) 12 | ``` 13 | 14 | ### 2. 获取正则路径创建正则对象 15 | 16 | ``` 17 | let reg = new RegExp(regStr,'igs'); 18 | ``` 19 | 20 | ### 3.匹配路径,并调用相对应的函数 21 | 22 | ``` 23 | if(reg.test(req.url)){ 24 | this.reqEvent[key](req,res) 25 | resState = true 26 | break; 27 | } 28 | ``` 29 | 30 | ### 4.判断是否正则路径响应过,如果响应过,将不再响应,不能重复响应,会报错 31 | 32 | ```javascript 33 | if(!resState){ 34 | if(pathObj.dir==this.staticDir){ 35 | res.setHeader("content-type",this.getContentType(pathObj.ext)) 36 | let rs = fs.createReadStream('./static/'+pathObj.base) 37 | rs.pipe(res) 38 | }else{ 39 | res.setHeader("content-type","text/html;charset=utf-8") 40 | res.end("

    404!页面找不到

    ") 41 | } 42 | } 43 | ``` 44 | 45 | -------------------------------------------------------------------------------- /Node/day7/4-梳理框架流程.md: -------------------------------------------------------------------------------- 1 | # 4-梳理框架流程 2 | 3 | ### 1浏览器发送请求 4 | 5 | 1. 用户输入网址地址 6 | 7 | ``` 8 | http://127.0.0.1/ 9 | ``` 10 | 11 | 2. 浏览器根据请求转变成HTTP的请求包 12 | 13 | ``` 14 | GET / HTTP/1.1 15 | Host: 127.0.0.1 16 | Connection: keep-alive 17 | Pragma: no-cache 18 | Cache-Control: no-cache 19 | Upgrade-Insecure-Requests: 1 20 | User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36 21 | Sec-Fetch-Mode: navigate 22 | Sec-Fetch-User: ?1 23 | Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3 24 | Sec-Fetch-Site: none 25 | Accept-Encoding: gzip, deflate, br 26 | Accept-Language: zh-CN,zh;q=0.9 27 | ``` 28 | 29 | ### 2服务器接受到请求 30 | 31 | ​ 1. http模块里中实例化的server对象,server对象监听每一次浏览器发送过来的请求,每次的请求都会触发`request`事件 32 | 33 | ``` 34 | this.server.on('request',(req,res)=>{}) 35 | ``` 36 | 37 | 2. 将HTTP的请求包转化成req的请求对象,并且传入到请求事件触发的函数中。 38 | 3. 会创建生成1个res响应对象,这个对象可以帮助我们快速的实现HTTP的响应 39 | 40 | ### 3解析请求路径,调用不同的页面渲染函数 41 | 42 | 1. 正则匹配方式进行对路径的匹配 43 | 2. 以匹配的正则字符串作为KEY,找到需要调用执行的渲染函数 44 | 45 | ```javascript 46 | //循环匹配正则路径 47 | for(let key in this.reqEvent){ 48 | 49 | let regStr = key 50 | let reg = new RegExp(regStr,'igs'); 51 | console.log(regStr,reg) 52 | if(reg.test(req.url)){ 53 | this.reqEvent[key](req,res) 54 | resState = true 55 | break; 56 | } 57 | } 58 | ``` 59 | 60 | 3. 调用页面的执行函数 61 | 62 | ``` 63 | app.on('/movies/[01]',(req,res)=>{})//这里的箭头函数即为真正匹配到的页面时执行的函数 64 | ``` 65 | 66 | 4. 调用模板的渲染函数 67 | 68 | ``` 69 | res.render(movies[index],'./template/index0.html') 70 | ``` 71 | 72 | 5. 执行渲染函数 73 | 74 | ```javascript 75 | function render(options,path){ 76 | fs.readFile(path,{encoding:"utf-8",flag:"r"},(err,data)=>{ 77 | if(err){ 78 | console.log(err) 79 | }else{ 80 | //数组变量的替换 81 | data = replaceArr(data,options) 82 | //单个变量的替换 83 | data = replaceVar(data,options) 84 | //最终输出渲染出来的HTML 85 | this.end(data) 86 | } 87 | }) 88 | } 89 | ``` 90 | 91 | 6. 数组变量的替换 92 | 93 | ```javascript 94 | function replaceArr(data,options){ 95 | //匹配循环的变量,并且替换循环的内容 96 | let reg = /\{\%for \{(.*?)\} \%\}(.*?)\{\%endfor\%\}/igs 97 | while(result = reg.exec(data)){ 98 | let strKey = result[1].trim();//提取变量时,去掉左右两边的空格 99 | //通过KEY值获取数组内容 100 | let strValueArr = options[strKey] 101 | let listStr = "" 102 | strValueArr.forEach((item,i)=>{ 103 | //替换每一项内容里的变量 104 | listStr = listStr + replaceVar(result[2],{"item":item}) 105 | }) 106 | data = data.replace(result[0],listStr) 107 | } 108 | return data; 109 | } 110 | ``` 111 | 112 | 7. 单个变量的替换 113 | 114 | ```javascript 115 | function replaceVar(data,options){ 116 | let reg = /\{\{(.*?)\}\}/igs 117 | let result; 118 | console.log(options) 119 | while(result = reg.exec(data)){ 120 | //去除2边的空白 121 | let strKey = result[1].trim() 122 | 123 | console.log(strKey)// item,item.abc 124 | let strValue = eval('options.'+strKey);//执行字符串作为JS表达式,并将计算出来的结果返回 125 | data = data.replace(result[0],strValue) 126 | } 127 | return data 128 | } 129 | ``` 130 | 131 | 132 | 133 | ### 4如果是请求静态文件,那么就按照静态文件的形式输出 134 | 135 | 1. 首先判断是否响应过,如果没有响应过,可以判断是否为静态文件,如果是静态文件就正常的输出 136 | 2. 否则,就输出404 137 | 138 | ```javascript 139 | if(!resState){ 140 | if(pathObj.dir==this.staticDir){ 141 | res.setHeader("content-type",this.getContentType(pathObj.ext)) 142 | let rs = fs.createReadStream('./static/'+pathObj.base) 143 | rs.pipe(res) 144 | }else{ 145 | res.setHeader("content-type","text/html;charset=utf-8") 146 | res.end("

    404!页面找不到

    ") 147 | } 148 | } 149 | ``` 150 | 151 | -------------------------------------------------------------------------------- /Node/day8/文档/1node总结.md: -------------------------------------------------------------------------------- 1 | Node:一门后端语言(服务器端的程序语言),能够连接数据库存取数据,能够接受和处理网络请求(服务器的响应,发送请求去获取数据),单线程事件驱动,异步执行,不等待,提高IO(input和ouput)的处理速度和效率。 2 | 3 | 服务器:本质上是一台PC主机(linux系统,windows系统),部署了后端语言的执行环境,并且能够长时间提供网络服务。 4 | 5 | ### 事件驱动 6 | 7 | node本身提供了事件对象,帮助我们快速订阅者模式,或者观察者模式,或者事件模式。 8 | 9 | ``` 10 | //事件的订阅 11 | event.on(‘林俊杰演唱会’,()=>{订阅门票}) 12 | //事件的触发 13 | event.emit(‘林俊杰演唱会’) 14 | ``` 15 | 16 | ### 读写事件 17 | 18 | ``` 19 | fs.readfile('path',读取配置,(err,data)=>{}) 20 | fs.writeFile('path',写入数据,写入配置,()=>{}) 21 | ``` 22 | 23 | ### 读写的promise封装 24 | 25 | ```javascript 26 | let fs = require('fs') 27 | function fsRead(path){ 28 | return new Promise(function(resolve,reject){ 29 | fs.readFile(path,{flag:'r',encoding:"utf-8"},function(err,data){ 30 | if(err){ 31 | //console.log(err) 32 | //失败执行的内容 33 | reject(err) 34 | 35 | }else{ 36 | //console.log(data) 37 | //成功执行的内容 38 | resolve(data) 39 | } 40 | //console.log(456) 41 | }) 42 | }) 43 | } 44 | 45 | 46 | function fsWrite(path,content){ 47 | return new Promise(function(resolve,reject){ 48 | fs.writeFile(path,content,{flag:"a",encoding:"utf-8"},function(err){ 49 | if(err){ 50 | //console.log("写入内容出错") 51 | reject(err) 52 | }else{ 53 | resolve(err) 54 | //console.log("写入内容成功") 55 | } 56 | }) 57 | }) 58 | } 59 | 60 | function fsDir(path){ 61 | return new Promise(function(resolve,reject){ 62 | fs.mkdir(path,function(err){ 63 | if(err){ 64 | reject(err) 65 | }else{ 66 | resolve("成功创建目录") 67 | } 68 | }) 69 | }) 70 | } 71 | 72 | module.exports = {fsRead,fsWrite,fsDir} 73 | ``` 74 | 75 | #### 使用方式 76 | 77 | ``` 78 | (async function(){ 79 | let data = await fsRead('path') 80 | })() 81 | ``` 82 | 83 | ### 网络请求数据 84 | 85 | request,axios:效率比较高,单局限性比较大 86 | 87 | puppeteer:效率低,局限性比较小 88 | 89 | 重点掌握的是:页面的分析,数据存放的位置,以及响应内容。 90 | 91 | ### 网络响应数据 92 | 93 | http.createServer:就可以创建1个服务器去监听某个端口,并且通过请求事件来处理每个发送过来的请求。 94 | 95 | server.on('request',(req,res)=>{ 96 | 97 | ​ req:请求数据都会放在请求对象里 98 | 99 | ​ res:能够做出响应对象 100 | 101 | }) 102 | 103 | ### 路由 104 | 105 | 根据不同的路径去响应不同的内容 106 | 107 | ```javascript 108 | //循环匹配正则路径 109 | for(let key in this.reqEvent){ 110 | res.setHeader("content-type","text/html;charset=utf-8") 111 | let regStr = key 112 | let reg = new RegExp(regStr,'igs'); 113 | //console.log(regStr,reg) 114 | if(reg.test(req.url)){ 115 | this.reqEvent[key](req,res) 116 | resState = true 117 | break; 118 | } 119 | } 120 | ``` 121 | 122 | ### 模板 123 | 124 | 会有个固定样式和结构的HTML模板,根据请求的数据不同,显示页面内容。例如新闻网站 125 | 126 | ```javascript 127 | function render(options,path){ 128 | fs.readFile(path,{encoding:"utf-8",flag:"r"},(err,data)=>{ 129 | if(err){ 130 | console.log(err) 131 | }else{ 132 | try { 133 | data = replaceArr(data,options) 134 | data = replaceVar(data,options) 135 | } catch (error) { 136 | console.log(error) 137 | } 138 | 139 | this.end(data) 140 | } 141 | }) 142 | } 143 | ``` 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /Node/day8/文档/2npm上传包.md: -------------------------------------------------------------------------------- 1 | # 2. NPM上传包 2 | 3 | ### 1. 创建文件夹 4 | 5 | ### 2. npm包的初始化 6 | 7 | ``` 8 | npm init 9 | ``` 10 | 11 | ### 3. npm包信息的设置 12 | 13 | ```json 14 | { 15 | "name": "lcfs", 16 | "version": "0.1.0", 17 | "description": "将原生的fs模块进行promise封装,可以快速的使用async_await模式", 18 | "main": "lcfs.js", 19 | "scripts": { 20 | "test": "echo \"Error: no test specified\" && exit 1" 21 | }, 22 | "keywords": [ 23 | "fs", 24 | "promise封装" 25 | ], 26 | "author": "陈鹏", 27 | "license": "ISC" 28 | } 29 | ``` 30 | 31 | ### 4. 注册NPM官网账号 32 | 33 | ### 5. NPM官网账号需要邮箱验证 34 | 35 | ### 6. 本机登陆NPM 36 | 37 | ``` 38 | npm login 39 | ``` 40 | 41 | ### 7. 发布NPM包 42 | 43 | ``` 44 | npm publish 45 | ``` 46 | 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # -front_end_notebook 2 | -------------------------------------------------------------------------------- /mysql/day1/Node连接MySQL.md: -------------------------------------------------------------------------------- 1 | # Node.js 连接 MySQL 2 | 3 | 介绍如何使用 Node.js 来连接 MySQL,并对数据库进行操作。 4 | 5 | ### 安装驱动 6 | 7 | 本教程使用了[淘宝定制的 cnpm 命令](https://www.runoob.com/nodejs/nodejs-npm.html#taobaonpm)进行安装: 8 | 9 | ``` 10 | $ cnpm install mysql 11 | ``` 12 | 13 | ### 连接数据库 14 | 15 | 在以下实例中根据你的实际配置修改数据库用户名、及密码及数据库名: 16 | 17 | ## test.js 文件代码: 18 | 19 | ```javascript 20 | var mysql = require('mysql'); 21 | var connection = mysql.createConnection({ 22 | host : 'localhost', 23 | user : 'root', 24 | password : '123456', 25 | database : 'test' 26 | }); 27 | 28 | connection.connect(); 29 | 30 | connection.query('SELECT 1 + 1 AS solution', function (error, results, fields) { 31 | if (error) throw error; 32 | console.log('The solution is: ', results[0].solution); 33 | }); 34 | ``` 35 | 36 | 执行以下命令输出结果为: 37 | 38 | $ node test.js 39 | The solution is: 2 40 | 41 | 42 | 43 | ## 数据库操作( CURD ) 44 | 45 | 在进行数据库操作前,你需要将本站提供的 Websites 表 SQL 文件[websites.sql](https://static.runoob.com/download/websites.sql) 导入到你的 MySQL 数据库中。 46 | 47 | 本教程测试的 MySQL 用户名为 root,密码为 123456,数据库为 test,你需要根据自己配置情况修改。 48 | 49 | ### 查询数据 50 | 51 | 将上面我们提供的 SQL 文件导入数据库后,执行以下代码即可查询出数据: 52 | 53 | ## 查询数据 54 | 55 | ```javascript 56 | var mysql = require('mysql'); 57 | 58 | var connection = mysql.createConnection({ 59 | host : 'localhost', 60 | user : 'root', 61 | password : '123456', 62 | port: '3306', 63 | database: 'test' 64 | }); 65 | 66 | connection.connect(); 67 | 68 | var sql = 'SELECT * FROM websites'; 69 | //查 70 | connection.query(sql,function (err, result) { 71 | if(err){ 72 | console.log('[SELECT ERROR] - ',err.message); 73 | return; 74 | } 75 | 76 | console.log('------SELECT------'); 77 | console.log(result); 78 | console.log('------------------'); 79 | }); 80 | 81 | connection.end(); 82 | ``` 83 | 84 | 执行以下命令输出就结果为: 85 | 86 | ``` 87 | $ node test.js 88 | --------------------------SELECT---------------------------- 89 | [ RowDataPacket { 90 | id: 1, 91 | name: 'Google', 92 | url: 'https://www.google.cm/', 93 | alexa: 1, 94 | country: 'USA' }, 95 | RowDataPacket { 96 | id: 2, 97 | name: '淘宝', 98 | url: 'https://www.taobao.com/', 99 | alexa: 13, 100 | country: 'CN' } 101 | ] 102 | ------------------------------------------------------------ 103 | ``` 104 | 105 | ### 插入数据 106 | 107 | 我们可以向数据表 websties 插入数据: 108 | 109 | ## 插入数据 110 | 111 | ```javascript 112 | var mysql = require('mysql'); 113 | 114 | var connection = mysql.createConnection({ 115 | host : 'localhost', 116 | user : 'root', 117 | password : '123456', 118 | port: '3306', 119 | database: 'test' 120 | }); 121 | 122 | connection.connect(); 123 | 124 | var addSql = 'INSERT INTO websites(Id,name,url,alexa,country) VALUES(0,?,?,?,?)'; 125 | var addSqlParams = ['工具', 'https://c.sxb.com','23453', 'CN']; 126 | //增 127 | connection.query(addSql,addSqlParams,function (err, result) { 128 | if(err){ 129 | console.log('[INSERT ERROR] - ',err.message); 130 | return; 131 | } 132 | 133 | console.log('------INSERT------'); 134 | //console.log('INSERT ID:',result.insertId); 135 | console.log('INSERT ID:',result); 136 | console.log('--------------\n\n'); 137 | }); 138 | 139 | connection.end(); 140 | ``` 141 | 142 | 执行以下命令输出就结果为: 143 | 144 | ``` 145 | $ node test.js 146 | --------------------------INSERT---------------------------- 147 | INSERT ID: OkPacket { 148 | fieldCount: 0, 149 | affectedRows: 1, 150 | insertId: 6, 151 | serverStatus: 2, 152 | warningCount: 0, 153 | message: '', 154 | protocol41: true, 155 | changedRows: 0 } 156 | ----------------------------------------------------------------- 157 | ``` 158 | 159 | 执行成功后,查看数据表,即可以看到添加的数据 160 | 161 | ### 更新数据 162 | 163 | 我们也可以对数据库的数据进行修改: 164 | 165 | ## 更新数据 166 | 167 | ```javascript 168 | var mysql = require('mysql'); 169 | 170 | var connection = mysql.createConnection({ 171 | host : 'localhost', 172 | user : 'root', 173 | password : '123456', 174 | port: '3306', 175 | database: 'test' 176 | }); 177 | 178 | connection.connect(); 179 | 180 | var modSql = 'UPDATE websites SET name = ?,url = ? WHERE Id = ?'; 181 | var modSqlParams = ['移动站', 'https://m.sxt.com',6]; 182 | //改 183 | connection.query(modSql,modSqlParams,function (err, result) { 184 | if(err){ 185 | console.log('[UPDATE ERROR] - ',err.message); 186 | return; 187 | } 188 | console.log('--------UPDATE--------'); 189 | console.log('UPDATE affectedRows',result.affectedRows); 190 | console.log('----------\n\n'); 191 | }); 192 | 193 | connection.end(); 194 | ``` 195 | 196 | 执行以下命令输出就结果为: 197 | 198 | ``` 199 | --------------------------UPDATE---------------------------- 200 | UPDATE affectedRows 1 201 | ----------------------------------------------------------------- 202 | ``` 203 | 204 | 执行成功后,查看数据表 205 | 206 | ### 删除数据 207 | 208 | 我们可以使用以下代码来删除 id 为 6 的数据: 209 | 210 | ## 删除数据 211 | 212 | ```javascript 213 | var mysql = require('mysql'); 214 | 215 | var connection = mysql.createConnection({ 216 | host : 'localhost', 217 | user : 'root', 218 | password : '123456', 219 | port: '3306', 220 | database: 'test' 221 | }); 222 | 223 | connection.connect(); 224 | 225 | var delSql = 'DELETE FROM websites where id=6'; 226 | //删 227 | connection.query(delSql,function (err, result) { 228 | if(err){ 229 | console.log('[DELETE ERROR] - ',err.message); 230 | return; 231 | } 232 | 233 | console.log('-----DELETE-------'); 234 | console.log('DELETE affectedRows',result.affectedRows); 235 | console.log('----------------\n\n'); 236 | }); 237 | 238 | connection.end(); 239 | ``` 240 | 241 | 执行以下命令输出就结果为: 242 | 243 | ``` 244 | --------------------------DELETE---------------------------- 245 | DELETE affectedRows 1 246 | ----------------------------------------------------------------- 247 | ``` 248 | 249 | 执行成功后,查看数据表 -------------------------------------------------------------------------------- /mysql/day1/数据库.md: -------------------------------------------------------------------------------- 1 | 数据库:专门用于存放数据地方。sqlServer,mysql,sqlite 2 | 3 | 数据库分类:关系型数据库(mysql),非关系型数据库(nosql,mongodb),图谱数据库(大数据建立知识图谱) 4 | 5 | 6 | 7 | ### 1. Mysql下载 8 | 9 | ``` 10 | https://dev.mysql.com/downloads/ 11 | ``` 12 | 13 | ### 2. 选择 [MySQL Community Server](https://dev.mysql.com/downloads/mysql/) 14 | 15 | ### 3. 下载页面 16 | 17 | ``` 18 | https://dev.mysql.com/downloads/windows/installer/8.0.html 19 | ``` 20 | 21 | 22 | 23 | ### 4. 安装MySQL 24 | 25 | 注意: 26 | 27 | 1. 仅安装server-only 28 | 2. 选择mysql.5x密码验证 29 | 30 | ### 5. 测试是否安装成功 31 | 32 | 1. 打开mysql8.0 cline client 33 | 2. 输入账号密码能够进入数据库 34 | 35 | ### 6. 安装navicat 36 | 37 | ### 7. 破解navicat 38 | 39 | ​ 安装好后,点击打开PatchNavicat.exe 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /mysql/day3/文档/3-子查询.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 13-子查询 226 | 227 | 228 |

    子查询

    • 查询支持嵌套使用
    • 查询各学生的语文、数学、英语的成绩

    什么是子查询

    当一个查询是另一个查询的条件时,这个查询称之为子查询(内层查询)

    什么时候用?

    当查询需求比较复杂,一次性查询无法得到结果,需要多次查询时,

    例如:给出一个部门名称,需要获得该部门所有的员工信息

    分析:

    1.需要先确定部门的id

    2.然后才能通过id确定员工

    解决问题的方式是把一个复杂的问题拆分为若干个简单的问题

    2. 如何使用?

    首先明确子查询就是一个普通的查询,当一个查询需要作为子查询使用时,用括号包裹即可

    3. 需要注意

    in中的子查询只能包含一个列

    例如:查询财务部有哪些人

    正确的写法:select name from emp where dept_id in (select id from dept where name = "财务");

    错误的写法:select name from emp where dept_id in (select * from dept where name = "财务");

    关键字:exists

    exists后跟子查询,子查询有结果是为True,没有结果时为False。为True时外层执行,为False外层不执行

    如何使用?

    select from emp where exists (select from emp where salary > 1000);

    前面 exists 后面 229 | 如果 后面 查询有结果时,前面 才会执行

    230 | 231 | -------------------------------------------------------------------------------- /mysql/day4/文档/1猫眼爬虫.md: -------------------------------------------------------------------------------- 1 | ## 猫眼反扒措施: 2 | 3 | ### 1-验证码 4 | 5 | 解决方案 6 | 7 | * 先手动通过验证码,获取请求头的信息(cookie,Referer,等),将此信息放置到请求对象里。 8 | 9 | * puppeteer自动模拟验证码,获取请求头,在将请求信息放置到对象中 10 | 11 | 12 | 13 | ### 2-限制请求网站次数 14 | 15 | 解决方案 16 | 17 | * 用动态无数个代理去访问,就可以降低每一个IP地址访问次数 18 | 19 | 20 | 21 | ### 3-字体文件动态加密 22 | 23 | 解决方案 24 | 25 | * 解析加密的字体问价,将字体的编码与图像进行比对,将图像转为SVG字符串编码后,比对字符串相似性。 26 | * 解析加密的字体问价,将字体的编码与图像进行比对,用卷积神经网络算法,去识别字体。 27 | 28 | 29 | 30 | 31 | 32 | ## 分析爬取过程 33 | 34 | ### 0-初识数据 35 | 36 | **1. 将验证后的请求头信息解析到请求对象里** 37 | 38 | ``` 39 | let options2 = { 40 | headers:{ 41 | "User-Agent":" Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36", 42 | "Cookie": "__mta=222114471.1574126937575.1575561742585.1575562035163.57; uuid_n_v=v1; uuid=F3A5F6E00A6B11EA866F9F7B8B645BB609807B37AECE45E69BBA4F3E5FC94038; _lxsdk_cuid=16e8146e542c8-04aa42a59aa0e4-5373e62-1fa400-16e8146e542c8; _lxsdk=F3A5F6E00A6B11EA866F9F7B8B645BB609807B37AECE45E69BBA4F3E5FC94038; _csrf=e7d0bd2ca9710f6294df933a1ea8d7546b48f8ed7a44f83a25177dd5f96233d0; Hm_lvt_703e94591e87be68cc8da0da7cbd0be2=1575507926,1575509335,1575525980,1575537558; _lx_utm=utm_source%3DBaidu%26utm_medium%3Dorganic; __mta=222114471.1574126937575.1575537635171.1575537840771.39; Hm_lpvt_703e94591e87be68cc8da0da7cbd0be2=1575562035; _lxsdk_s=16ed6c2213d-710-b40-6ad%7C%7C12", 43 | "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3", 44 | "Accept-Encoding": "gzip, deflate, br", 45 | "Accept-Language": "zh-CN,zh;q=0.9", 46 | "Cache-Control": "no-cache", 47 | "Connection":"keep-alive", 48 | "Host": "maoyan.com", 49 | "Pragma": "no-cache", 50 | "Sec-Fetch-Mode": "navigate", 51 | "Sec-Fetch-Site": "none", 52 | "Sec-Fetch-User": "?1", 53 | "Upgrade-Insecure-Requests": "1", 54 | "Referer": "https://maoyan.com/films?showType=3" 55 | } 56 | } 57 | ``` 58 | 59 | **2.数据库初始化** 60 | 61 | ``` 62 | //创建与数据库的连接的对象 63 | let con = mysql.createConnection(options); 64 | 65 | //建立连接 66 | con.connect((err)=>{ 67 | //如果建立连接失败 68 | if(err){ 69 | console.log(err) 70 | }else{ 71 | console.log('数据库连接成功') 72 | } 73 | }) 74 | ``` 75 | 76 | **3.设置爬取的页面和条数变量** 77 | 78 | ``` 79 | //index代表第多少页 80 | let index = 0; 81 | //代表第n页第k条数据 82 | let k = 0; 83 | //放置每个页面详情链接地址的数组 84 | let hrefList = [] 85 | ``` 86 | 87 | 88 | 89 | ### 1-获取列表页的详情页链接地址 90 | 91 | ```javascript 92 | async function getPageList(index){ 93 | let httpUrl = "https://maoyan.com/films?showType=3&offset="+ index*30; 94 | let response = await axios.get(httpUrl,options2) 95 | //用cheerio解析页面html 96 | let $ = cheerio.load(response.data); 97 | 98 | $(".movie-list .movie-item>a").each(function(i,element){ 99 | let href = "https://maoyan.com"+$(element).attr('href') 100 | hrefList.push(href); 101 | }) 102 | //将当前页面的所有详情页链接地址获取 103 | //console.log(hrefList) 104 | //获取当前页面第k条详情链接地址 105 | getMovieInfo(hrefList[k]) 106 | 107 | } 108 | ``` 109 | 110 | 111 | 112 | ### 2-通过详情页地址获取详细信息 113 | 114 | **1.申明获取详情页信息的函数** 115 | 116 | ``` 117 | async function getMovieInfo(href){} 118 | ``` 119 | 120 | **2.获取详情页,并用cheerio进行解析** 121 | 122 | ``` 123 | let response = await axios.get(href,options2) 124 | let $ = cheerio.load(response.data) 125 | ``` 126 | 127 | **3.获取电影名称/电影图片/电影分类/电影地区和发布时间** 128 | 129 | ``` 130 | let moviename = $('.movie-brief-container .name').text(); 131 | let movieimg = $('.avatar-shadow>.avatar').attr('src'); 132 | let cataory = $('body > div.banner > div > div.celeInfo-right.clearfix > div.movie-brief-container > ul > li:nth-child(1)').text(); 133 | let areaTime = $("body > div.banner > div > div.celeInfo-right.clearfix > div.movie-brief-container > ul > li:nth-child(2)").text(); 134 | let area = areaTime.split('/')[0].trim(); 135 | let pubtime = $("body > div.banner > div > div.celeInfo-right.clearfix > div.movie-brief-container > ul > li:nth-child(3)").text().substring(0,10) 136 | ``` 137 | 138 | **4.由于有些电影未上映,没有时间,所有获取不到duration** 139 | 140 | ```javascript 141 | try { 142 | var duration = parseInt(areaTime.split('/')[1].trim()); 143 | } catch (error) { 144 | var duration = 0 145 | } 146 | ``` 147 | 148 | **5.根据是否有评分,采取获取评分的操作或者直接设置未0,获取操作如下** 149 | 150 | ``` 151 | score = $(".index-right .star-on").css("width").substring(0,2); 152 | ``` 153 | 154 | **6.评分人的数量和票房获取** 155 | 156 | 挑战: 157 | 158 | * 每一次请求获取到的编码都是不一样的 159 | * 每一次通过页面获取的解析编码的字体文件的链接地址也是不一样的 160 | * 字体文件里的编码和数字图像每一次都不样。 161 | 162 | 1.获取评分数量和票房的编码 163 | 164 | ``` 165 | let aa = $("body > div.banner > div > div.celeInfo-right.clearfix > div.movie-stats-container > div:nth-child(1) > div > div > span > span").text() 166 | let bb = $("body > div.banner > div > div.celeInfo-right.clearfix > div.movie-stats-container > div:nth-child(2) > div > span.stonefont").text() 167 | ``` 168 | 169 | 2.获取随机字体文件的链接地址 170 | 171 | ```javascript 172 | let fontUrl = $("head > style").html() 173 | let reg = /format.*?url\('(.*?woff)'\)/igs; 174 | //console.log(fontUrl) 175 | let result = reg.exec(fontUrl) 176 | let fontPath = result[1] 177 | ``` 178 | 179 | 3.将随机的字体文件下载并进行解析 180 | 181 | ``` 182 | let ws = fs.createWriteStream('a.woff') 183 | res.data.pipe(ws); 184 | ``` 185 | 186 | 4.将评分乱码字符编码分割成数组,因为解析编码是一个异步的操纵,设置1个变量b来判断是否以及所有编码解析完毕。将解析完毕的编码放置到新的数组arr11; 187 | 188 | ``` 189 | let arr1 = aa.split("") 190 | let b = 0; 191 | let arr11 = [] 192 | ``` 193 | 194 | 5.循环解析字符串编码 195 | 196 | ``` 197 | arr1.forEach(async (item,i)=>{}) 198 | ``` 199 | 200 | 6.解析乱码字符串 201 | 202 | ``` 203 | let result = await parseNum(item); 204 | ``` 205 | 206 | 7.解析字符串的过程,opentype.js可以帮助我们快速的解析字体文件 207 | 208 | ```javascript 209 | async function parseNum(str){ 210 | return new Promise(function(resolve,reject){ 211 | //载入字体文件,并解析出字体对象 212 | opentype.load("a.woff", function(err, font) { 213 | if (err) { 214 | reject('Font could not be loaded: ' + err) 215 | } else { 216 | //通过字体对象,并在getPath方法中传入乱码的编码,获取出绘制该字体图像路径,0,100,72分别表示绘制的x坐标为0,y坐标为100,字体大小为72 217 | var path = font.getPath(str,0,100,72); 218 | //通过绘制的图像路径,得出SVG矢量图的字符串 219 | let a = path.toSVG(); 220 | let max = 0; 221 | let key = 0; 222 | let arr = [] 223 | for(let i=0;i<10;i++){ 224 | let temp = similar(a,svgPaths[i]) 225 | if(max0.5?result.key:item; 266 | ``` 267 | 268 | 10.最终将评分的数量获取 269 | 270 | ```javascript 271 | scorenum = arr11.join("") 272 | scorenum = parseChar(scorenum); 273 | scorenum = isNaN(scorenum)?0:scorenum; 274 | ``` 275 | 276 | 11.同理可得票数数据 277 | 278 | 12.跳转至下一页获取获取下个链接数据 279 | 280 | ```javascript 281 | k++; 282 | if(k==hrefList.length){ 283 | hrefList = []; 284 | k=0; 285 | console.log(index,"页-"+k+"数据已爬取完毕!-----") 286 | index++; 287 | getPageList(index) 288 | 289 | }else{ 290 | getMovieInfo(hrefList[k]) 291 | } 292 | ``` 293 | 294 | 295 | 296 | ### 3-存放到数据库 297 | 298 | ```javascript 299 | let arr = [moviename,movieimg,cataory,area,duration,pubtime,score,scorenum,boxoffice,brief,director] 300 | let strSql = 'insert into movie (moviename,movieimg,cataory,area,duration,pubtime,score,scorenum,boxoffice,brief,director) values (?,?,?,?,?,?,?,?,?,?,?)' 301 | await sqlQuery(strSql,arr) 302 | ``` 303 | 304 | -------------------------------------------------------------------------------- /mysql/day4/文档/3-子查询.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 13-子查询 226 | 227 | 228 |

    子查询

    • 查询支持嵌套使用
    • 查询各学生的语文、数学、英语的成绩

    什么是子查询

    当一个查询是另一个查询的条件时,这个查询称之为子查询(内层查询)

    什么时候用?

    当查询需求比较复杂,一次性查询无法得到结果,需要多次查询时,

    例如:给出一个部门名称,需要获得该部门所有的员工信息

    分析:

    1.需要先确定部门的id

    2.然后才能通过id确定员工

    解决问题的方式是把一个复杂的问题拆分为若干个简单的问题

    2. 如何使用?

    首先明确子查询就是一个普通的查询,当一个查询需要作为子查询使用时,用括号包裹即可

    3. 需要注意

    in中的子查询只能包含一个列

    例如:查询财务部有哪些人

    正确的写法:select name from emp where dept_id in (select id from dept where name = "财务");

    错误的写法:select name from emp where dept_id in (select * from dept where name = "财务");

    关键字:exists

    exists后跟子查询,子查询有结果是为True,没有结果时为False。为True时外层执行,为False外层不执行

    如何使用?

    select from emp where exists (select from emp where salary > 1000);

    前面 exists 后面 229 | 如果 后面 查询有结果时,前面 才会执行

    230 | 231 | -------------------------------------------------------------------------------- /项目/day1/文档/1项目需求.md: -------------------------------------------------------------------------------- 1 | # 医院管理系统 2 | 3 | ### 1-前台 4 | 5 | 1. 首页 6 | 2. 医院介绍 7 | 3. 新闻中心 8 | 4. 预约挂号 9 | 10 | ### 2-后台 11 | 12 | 1. 用户管理,添加删除修改用户资料 13 | 2. 权限管理,用户有哪些的权限,有些页面只有某部分特殊用户可以访问。 14 | 3. 登陆注册 15 | 4. 新闻信息管理 16 | 5. 新闻分类管理 17 | 6. 医生管理 18 | 7. 患者管理 19 | 8. 挂号管理 20 | 9. 药品管理 21 | 10. 财务统计 -------------------------------------------------------------------------------- /项目/day2/文档/2-1个人信息页.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a415432669/-front_end_notebook/34f5976ee52efd1cfc17e5087f484c489ad12a5a/项目/day2/文档/2-1个人信息页.md -------------------------------------------------------------------------------- /项目/day4/文档/1权限管理.md: -------------------------------------------------------------------------------- 1 | # 权限管理 2 | 3 | 权限管理需要的数据表 4 | 5 | 1. 用户表 6 | 2. 角色表 7 | 3. 权限表 8 | 9 | 举例: 10 | 11 | 张三用户是1个管理员,那么管理员就有(编辑用户,编辑新闻,编辑挂号,查看统计数据) 12 | 13 | 李四用户是1个医生,那么就拥有编辑挂号的权限。 14 | 15 | 王五是1个新闻编辑者,那么就拥有新闻编辑的权限。 16 | 17 | 设置权限表 18 | 19 | 1添加/删除 /需改权限 20 | 21 | 2字段字段: 22 | 23 | 权限编码,id 24 | 25 | 编辑用户权限,权限名称 26 | 27 | /admin/users/userlist2,访问路径 28 | 29 | -------------------------------------------------------------------------------- /项目/day4/文档/文档/1权限管理.md: -------------------------------------------------------------------------------- 1 | # 权限管理 2 | 3 | 权限管理需要的数据表 4 | 5 | 1. 用户表 6 | 2. 角色表 7 | 3. 权限表 8 | 9 | 举例: 10 | 11 | 张三用户是1个管理员,那么管理员就有(编辑用户,编辑新闻,编辑挂号,查看统计数据) 12 | 13 | 李四用户是1个医生,那么就拥有编辑挂号的权限。 14 | 15 | 王五是1个新闻编辑者,那么就拥有新闻编辑的权限。 16 | 17 | 设置权限表 18 | 19 | 1添加/删除 /需改权限 20 | 21 | 2字段字段: 22 | 23 | 权限编码,id 24 | 25 | 编辑用户权限,权限名称 26 | 27 | /admin/users/userlist2,访问路径 28 | 29 | -------------------------------------------------------------------------------- /项目/day5/1-SVN.md: -------------------------------------------------------------------------------- 1 | SVN是subversion的缩写,是一个开放源代码的版本控制系统,通过采用分支管理系统的高效管理,简而言之就是用于多个人共同开发同一个项目,实现共享资源,实现最终集中式的管理。 2 | 3 | 4 | 5 | 1. 工作场景 6 | 7 | 进入公司需要做的关于开发的第一件事, 就是向项目经理索要SVN服务器地址+用户名+密码 8 | 2. 角色解释 9 | 10 | > 11 | > 服务器: 用于存放所有版本的代码,供客户端上传下载更新 12 | > 13 | > 客户端: 通过客户端下载上传本地代码 14 | > 15 | > 用户名/密码: 验证人员身份,判断是否有操作权限; 16 | ### 项目开发中的基本操作 17 | 18 | 1. 项目经理初始化项目结构并上传到服务器 19 | 2. 苦逼程序猿根据地址+用户名+密码下载一份完整代码到本地 20 | 3. 开始开发,任务完成后,提交任务代码到服务器 21 | 4. 从服务器上更新其他同事的代码到本地 22 | 23 | 24 | 25 | ### SVN操作体现 26 | 27 | 1. svn import (项目经理做的) 28 | 2. svn checkout : 将服务器对应项目的所有代码下载到本地 29 | 3. svn commit : 提交本地最新代码到服务器 30 | 4. svn update : 从服务器更新最新被修改的代码 31 | 5. update to revision:更新版本到什么版本 32 | 33 | ### 服务器搭建 34 | 35 | 1. 打开visual svn server manager 36 | 37 | 2. 创建代码库 38 | 39 | 3. 点击create new resporitory 40 | 41 | 1-regular fsfs resportory 42 | 43 | 2-设置代码库名称 44 | 45 | 3-empty resporitory 46 | 47 | 4-默认所有用户可读写 48 | 49 | 4. 导入代码 50 | 51 | 1-进入想要导入的文件夹,点击右键,选择SVN,选择import 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /项目/day5/1项目需求.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a415432669/-front_end_notebook/34f5976ee52efd1cfc17e5087f484c489ad12a5a/项目/day5/1项目需求.docx --------------------------------------------------------------------------------