-
Notifications
You must be signed in to change notification settings - Fork 1
/
search.xml
168 lines (168 loc) · 165 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[canvas]]></title>
<url>%2F2018%2F02%2F15%2Fcanvas%2F</url>
<content type="text"><![CDATA[canvascanvas画线条基本步骤 编写canvas标签 1<canvas>/<canvas> 获取canvas的dom元素 1var cas=document.querySelector("canvas"); 获取canvas的上下文对象(画笔) 1var ctx=cas.getContext("2d"); 设置起点 1ctx.moveTo(x,y); 设置终点 1ctx.lineTo(x,y); 描边(连线) 1ctx.stroke(); 设置canvas的宽度和高度 在css中直接设置宽度和高度 其实是拉伸 在标签中通过属性的方式写宽度和高度(不叫做行内!!! ) 注意点 如果不写moveTo的话 那么lineTo 既是终点也是起点 首尾相连: 手动写lineTo ctx.closePath(); h5新标签 color标签 一般结合 onchange一起使用 1<input type="color" /> number标签 min 最小值 max 最大值 value 当前的值 step 步长 1<input type="number" min=0 max=10 value=0 step=1 > 操作数组的4个方法 原数组 arr=[a,b,c,d,e]; push 往屁股添加一个值 返回数组的长度 如: 6= arr.push(f) arr= [a, b, c, d,e,f]; pop 从屁股移除一个值 返回被删除的值 如: e= arr.pop() arr=[a,b,c,d]; unshift 从头部添加一个值 返回数组的长度如: 6= arr.unshift(f) arr=[f,a,b,c,d,e]; shift 从头部删除一个值 返回被删除的值 如: a= arr.shift() arr=[b,c,d,e]; eval 方法 可以把字符串变成代码执行 不要在工作里面出现 stroke和fill stroke 描边-对在同一条路径上的图形进行描边(封闭和不封闭) fill 填充 对同一条路径上的用线条画(不能填充strokeRect()的矩形)的封闭图形进行填充 路径 路径也是图形 判断两个图形是否在同一条路径上 不能够靠视觉上的首尾相连来判断 而是看有没有调用 ctx.beginPath(); 一般在画新图形之前,最好要 开启一条新的路径 线条属性 颜色 ctx.strokeStyle=”red”; ctx.fillStyle=”red”; 宽度 ctx.lineWidth=10; 线段末端类型 miter 默认 round 突出圆角 square 突出矩形 ctx.lineCap=”miter”; 线段相交点的类型 butt 屁股 默认值 round 圆角 bevel 平切 ctx.lineJoin=”butt”; 虚线 setLineDash([实线的长度,实线的间隙的长度……]) 设置虚线 getLineDash() 获取虚线 lineDashOffset=1 设置虚线的偏移值 内置画矩形和擦除 画矩形 stokeRect(x,y,width.height) 填充矩形 fillRect(x,y,width,height) 擦除 clearRect(x,y,width,height) 内置画圆弧 360 度=PI*2 return Math.PI/180* num ctx.arc(圆心x,圆心y,半径,开始的弧度,结束的弧度,?是否反向) 内置 画图片步骤:12345var img=new Image();img.src="路径";img.onload=function(){ ctx.drawImage()......} 方法解释 3个参数 drawImage(图片对象,画在画布的x,画在画布的y); 5个参数 drawImage(图片对象,画在画布的x,画在画布的y,画多宽,画多高); 9个参数 drawImage(图片对象, 原图的x,原图的y,原图的宽,原图的高, 画在画布的x,画在画布的y,画多宽,画多高); 渐变决定渐变图案的因素 颜色 方向 长度 代码: 123456var g=ctx.creatLinearGradient(开始的坐标x,y,结束的坐标x,y);g.addColorStop(0,"red");g.addColorStop(1,"black");ctx.strokeStyle=g;ctx.fillStyle=g;ctx.fillRect(0,0,50,50); 文字ctx.strokeText(“文本”,x,y); ctx.fillText(“文本”,x,y); ctx.font=”50px 宋体” font的设置和在css中的设置一样 阴影ctx.shadowBlur 模糊值 ctx.shadowColor 颜色 ctx.shadowOffsetX x 偏移 ctx.shadowOffsetY y 偏移 1234ctx.shadowBlur=10;ctx.shadowColor="red";ctx.shadowOffsetX=10;ctx.shadowOffsetY=10; canvas中的转换移动css3中的移动和canvas中的移动的区别css3中的移动 移动的是自身的dom元素 移动的值 是覆盖的 先 translateX(100px) 再 tranlateX(200px) 最终的值 是 ranlateX(200px) 移动的单位 一个 百分比 % 一个是像素 px canvas中的移动 移动的是坐标系 移动的值是叠加的 先 translateX(100px) 再 tranlateX(200px) 最终的值 是 ranlateX(300px) 单位 不用写单位 1ctx.translate(1,1) 旋转css3中的旋转和canvas中的旋转的区别css3中的旋转 旋转的自身的dom元素 旋转的值 是覆盖的 单位 是 角度 旋转的中心点 是元素的中心 center center canvas中的旋转 旋转的是坐标系 旋转的值 是叠加的 单位 是 弧度!!!! 旋转的中心点是坐标系的圆点 0 0 1ctx.rotate(degToArc(10)); 环境环境 值 坐标系和线条属性 保存和还原保存的是 坐标系 和 线条属性 还原的是 坐标系 和 线条属性 保存了几次 就可以还原几次 12ctx.save()ctx.restore(); 擦除和重置擦除ctx.clearRect(0,0,100,100) 有时候 坐标系改变了的话 再使用擦除这个代码 坐标不一定是 0 0 不能擦除坐标系 不能擦除线条属性(颜色 大小 虚线 末端 相交🍌) 重置cas.width=cas.width 可以擦除画图的图案 可以重置坐标系 可以重置线条属性 下载标签实现1<a href="资源路径" download="资源名字(可以随便写)" ></a> 代码实现12345678// 1 创建a标签var aDom=document.createElement("a");// 2 指定 hrefaDom.href="资源路径";// 3 设置download属性aDom.download="五年高考";// 4 手动触发 a标签的点击行为aDom.click();//它是个方法 不是 事件 画布保存和下载获取canvas画布的路径 cas.toDataURL()cas.ToDataURL(图片类型,质量)图片类型 image/png 默认值 即 cas.toDataURL() 既可以 不能压缩 image/jpeg 可以压缩 默认会把透明背景填充成黑色 建议在使用它的时候 先手动fill 画布 成白色 质量(0-1) 0 最低 1 最高画视频将video标签传入 ctx.drawImage(video,0,0) 即可 参数和 之前画图片的一样 在线视频截图下载报错: Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported. 解决:以服务器的形式打开即可 和canvas相关的技术 echartjs 百度 数据展示 文档详细 基于配置 konvajs 国外 自定义图形 类jq和js的关系 three.js 国外 3d模拟 门槛高 电脑性能要求高 d3.js 国外 大数据 门槛中 数据分析 移动web失真原因图片的清晰度和设备的清晰度不一致 解决方案: 不用解决 忍受 模糊 全部高清图 最常用 srcset 设备像素比的方式 devicePixelRatio 1<img src="./images/科比.png" srcset="./images/赵丽颖.png 2x,./images/高圆圆.png 3x" alt=""> 基本概念 逻辑分辨率:屏幕的宽和高 单位 是px 设备分辨率:屏幕里面一共拥有的物理像素点的个数!!! 对角线:一般说的手机屏幕尺寸都是指对角线的长度 单位 是英寸 PPI (Pixels Per Inch)也叫像素密度,所表示的是每英寸所拥有的像素数量 值越高,越清晰 视口布局视口被手机厂商设置宽度为980px的视口 理想视口视口宽度和屏幕等宽并且使用绝对长度单位写元素大小 是固定 在代码里面如何写处理 meta:vp+tab 不标准的1<meta name="viewport" content="width=device-width, initial-scale=1.0"> 标准的写法1<meta name="viewport" content="width=device-width, initial-scale=1.0,maximum-scale=1,minimum-scale=1,user-scalable=no"> less编译工具考拉注意事项 需要将 整个 css文件夹 都拖放到考拉当中 存放less文件的文件夹的名字不能够包含 less 字段 新创建less文件的时候 需要手动点击一下 考拉 里面的刷新按钮 reflash vs code 中的插件 easyless直接在vs code中下载 启用即可 语法:变量1234@myColor:red;body{ color:@myColor;} 函数12345678910111213141516171819202122// 不带参数的函数.changeColor1(){ color:red;}body{ .changeColor1();}// 带参数的函数.changeColor2(@c){ color:@c;}body{ .changeColor2(red);}// 带默认参数的函数.changeColor3(@c:black){ color:@c;}body{ .changeColor3();// color:black; .changeColor3(red);// color:red;} 嵌套12345div{ &:after{} p{} >section{}} 导入1@import "base.less"; 注释12// 这种注释不会被编译到css中/* 这种注释会被编译到css中 */ 真机调试ghostlab 云真机 工作中如何对待: 1 公司里面有测试 开发者的只要确保在 模拟器上 没有bug 就可以了. 2 大公司里 有第三方的测试团队去测试 3 小小的公司 公司人少 只能够自己去测试 (再去研究真机调试的步骤) 5 在学习的时候 可以先不用管真机调试 .以后用到的时候再去搭建环境和步骤就可以 了 !!! 触屏事件触屏事件的种类touchstart 手指按下屏幕 touchemove 手指在屏幕上滑动 touchend 手指离开屏幕 三个触摸点数组touches 屏幕上所有的触摸点的集合 targetTouches 目标元素上的触摸点的集合 changedTouches 在目标元素上发生了状态改变(进入 离开 移动)的触摸点的集合 三对坐标信息clientX/Y 相对于视口的坐标 pageX/Y 相对于页面的坐标 screenX/Y 相对屏幕的坐标 触屏事件的注意点需求: 鼠标事件不能模拟多指触控 click点击事件在移动端存在延迟 原因 手机厂商为了用户更方便的放大页面 延迟机制: 第一次点击后,先等待一段时间 这一段时间过后 判断有没有第二次的点击发生 有 触发双击放大 没有的再触发单击 不管有没有第二次的点击 都需要等待 - 延迟 触屏事件不能在pc端上触发 建议绑定事件的时候 用 addEventListener 来绑定 手势封装tap判断依据: 手指个数不能超过1 按下时间不能超过300ms 滑动距离不能超过5px 手势封装swipe判断依据: 手指个数不能超过1 按下时间不能超过800ms 滑动距离不能小于15px 京东轮播图过渡结束事件transitionend 每一次过渡结束的时候 都会触发一次 移动端轮播图插件 swiper.js我们使用的版本是3.x版本 使用教程看官网即可 网站 vs code 插件1 安装 ease Server 插件 (同步刷新功能) 2 在当前的html页面中 输入 ctrl+shift+enter 自己构建zepto1 先安装好 nodejs 在cmd中输入 node -v 如果出现了版本号 证明安装成功了 2 在zepto文件夹内 按下 shift+鼠标右键 弹出命令行窗口 3 输入 npm install 4 执行编译 npm run-script dist 5 自己设置要编译的模块 SET MODULES=zepto event data touch 6 执行编译 npm run-script dist 响应式布局概念:用一套代码 可以做一个适应不同宽度的设备,还可以提供比较友好的用户体验 核心原理:媒体查询 媒体查询一种css的语法,可以根据设备的不同(主要是宽度),去加载对应的css代码 媒体查询的知识点媒体类型 all 包括一下两个类型 screen 带正常屏幕的设备 print 打印机 媒体特性 宽度 高度 视口的宽高比 123456// 2/1 不能改为 2@media screen and (aspect-ratio:2/1){ body{ }} 媒体关键字 and or 代码中写逗号 来体现 not 取反 only 用来做兼容 媒体查询的引入方式 在css(和一般的样式代码是同层级)中直接写媒体查询-用得最多 在style标签上 通过属性的方式 在link标签上 通过属性的方式 bootstrap框架栅格系统是把所有屏幕(4种屏幕) 分成了12份 每一列占一份 4种宽度不同的屏幕 极小屏幕 xs < 768px 小屏幕 sm 768-992 中等-普通屏幕 md 992-1200 大屏幕 lg > 1200 步骤: 先写容器 .container(版心的宽度) .container-fluid (全屏) 写 .row 再去写栅格(要注意标明屏幕的种类) 工具提示用法: 直接粘贴标签的代码 必须要写上一段初始化的js代码 微金所知识点a标签不能嵌套a标签nth-child和nth-of-type的区别 nth-child 计算子元素索引的时候 会计算其他类型的标签 nth-of-type 计算子元素的索引的时候 不会计算其他类型的标签 列嵌套-有时候代码怎么简单怎么写在做大的布局的时候容器的时候 建议使用 块级元素 (千万不要写行内嵌套块级元素)rem+媒体查询 布局rem和px和em的区别 px是绝对长度单位 em是相对长度单位 相对于自身的fontsize 谷歌浏览器默认字体是16px 谷歌浏览器默认最小字体是12px rem 是相对长度单位 相对于html标签的fontsize 实现屏幕适配的步骤 使用rem+媒体查询还原设计稿 使用rem+媒体查询去做屏幕适配 适配的注意事项 基础值 和设计稿等宽的媒体查询里面的html标签的fontsize 为100px 写100px 是为了方便计算 公式: fontsize=基础值*要适配的屏幕/设计稿的宽度 不建议使用js来代替媒体查询的功能 任何宽度的设计稿都可以用这套解决方案 pc端的滚动条占宽度 移动端的滚动条不占宽度 在线工具标你妹 px转rem工具 开发技巧less和souce mapsouce map存放了less文件和css文件的映射关系 作用可以从页面调试工具里面 直接映射会对应的less文件 步骤: 使用工具创建souce map (考拉 vs code中插件 easyless) 考拉 easyless 插件 需要在vs code 的设置中 加入以下代码 就可以了 123"less.compile": { "sourceMap": true // true => generate source maps (.css.map files) }, 需要在谷歌浏览器中开启 ]]></content>
<categories>
<category>web</category>
</categories>
<tags>
<tag>canvas</tag>
</tags>
</entry>
<entry>
<title><![CDATA[浅析node(二)]]></title>
<url>%2F2018%2F02%2F13%2F%E6%B5%85%E6%9E%90nodejs-%E4%BA%8C%2F</url>
<content type="text"><![CDATA[关于node的常见知识点札记 同步和异步的对比【非阻塞I/O和阻塞I/O】 异步读取文件 异步嵌套读取文件 同步读取文件 异步和同步操作中,捕获错误的方式 Node 中的模块化 什么是程序开发中的模块及好处 什么是程序开发中的模块化:把一些功能类似的代码,封装到一个单独的文件中去,这些单独抽离出来的代码文件,就能够提供各种各样好用的功能;这种通过代码功能分割文件的方式,叫做程序中的模块化; 好处:保证了每个文件的功能(职能)单一;需要什么特定的功能,直接调用某一个特定的模块;对将来程序开发和维护都有好处! Node 中如何实现模块化 在Node中通过三个东西实现模块化: module:表示一个模块,在Node中,最常见的模块,就是一个个的JS文件! require:作用是加载其他模块用的;在一个JS文件中,如何引用其他JS文件中的成员呢?就是用require exports:在Node的模块中,如果这个模块需要向外暴露一些成员,供其他JS模块使用,那么,需要使用exports向外暴露这些成员! Node 中的模块化,主要解决的JS文件之间的相互依赖关系! Node 中的模块的分类 node由三部分组成:ECMAScript + 核心API + 第三方等API 核心模块 什么是核心模块:官方,发现一些功能模块使用非常频繁,然后,官方把这些模块,编译成了二进制可执行文件,然后打包到了Node的安装包中;所以,这些核心模块就已经随着安装Node时候,被安装到了本地; 如何使用核心模块使用require(‘核心模块的名称’); 第三方模块 什么是第三方模块:出了官方提供的好用的核心模块之外,我们程序员发现,还有一些使用也很频繁的代码和方法,一些牛逼的团体、个人、公司,开发出了好用的模块,通过NPM官网,托管出去,供其他人下载使用的这些模块;统称为第三方模块; 如何使用第三方模块- 通过moment这个第三方模块,来介绍如何使用一个第三方模块; 第三方模块的使用方式: 先使用npm下载这个模块!【注意:在安装第三方模块的时候,安装的名字,就是你在require时候导入的名字】 使用require导入这个第三方模块! 通过官方文档,试着去使用这个第三方模块! 注意:无论是核心模块、还是第三方模块,都是通过 标识符名称来引用这个模块的! 用户模块 什么是用户模块:程序员自己定义的JS文件,统统数据用户模块! 用户模块向外导出成员的两种方式: 第一种方式:使用global对象,相当于浏览器中的window对象 全局变量污染, 不知道成员是从哪个模块中暴露 出去的 推荐Node提供的exports exports 和 module.exports 的区别 通过 module.exports 可以使用 . 的形式追加属性,也可以使用 等号 直接赋值的形式导出成员; exports 只能通过 . 的形式追加属性;不能使用 等号 直接赋值的形式! 注意: 在一个 module 中,最终向外暴露的成员,以 module.exports 指向的对象为准! 在一个模块中,不要混合使用 module.exports 和 exports 模块加载规则 优先从缓存中加载 加载核心模块:优先从缓存中加载;如果缓存中没有的话,再去执行加载核心模块! 自己的模块:优先从缓存中加载;如果缓存中没有的话,再去执行加载用户模块! 用户模块的查找规则: 用户模块的查找规则:如果不写后缀名,则先严格按照给定的文件名去查找模块并加载执行;index -> index.js -> index.json -> index.node 第三方模块查找规则: node首先,查看项目根目录中有没有 node_modules 文件夹 查找 node_modules 文件夹中,有没有和第三方模块名称一致的文件夹 在模块对应的文件夹中,查找有没有 package.json 这个文件 在 package.json 文件中,查找有没有 main 属性 如果有 main 属性,并且 main 属性指向的路径存在,那么就尝试加载这个路径指定的文件! 如果 package.json 文件中,没有 main 属性,或者 main 属性指向的路径不存在,或者没有package.json 文件, 那么,Node尝试加载 模块根目录中 index 相关文件:index.js -> index.json -> index.node 如果在node_modules文件夹中,找不到对应的模块文件夹,或者在项目根目录中根本没有node_modules文件夹,则向上一层文件夹中去查找,查找规则同上! 如果上一层目录中也没有查找到,则再向上翻一层去查找,直到找到当前项目所在的盘符根目录为止! 如果找到了盘符根目录还找不到,则报错:cannot find module * 只安装部署依赖项不安装开发工具 npm i –production 模块化 node中的模块化基于commonJS:最显著的特点所有依赖项都是同步加载的!!! commonJS由于是一个同步加载规范,所以不适合浏览器端使用;于是,浏览器端需要使用异步的模块加载机制 AMD、CMD 结合art-template模板引擎在服务器端渲染页面 art-template官方文档 服务端渲染和客户端渲染的对比 博客园官网 服务器端渲染 特点:Node服务器先读取模板页面,再获取要渲染的数据,然后直接在服务器端把完整的页面渲染拼接好,然后通过网络一次性的把结果发送给浏览器去显示!【注意:通过服务器端渲染,在网络中传输的是一个真正页面】 优点:请求次数少,速度快,对SEO友好 缺点:服务器渲染压力大【在后端可以通过缓存机制去解决】 客户端渲染 特点:先访问服务器获取到模板页面,然后再通过Ajax获取要渲染的数据,拿到这两者之后,再客户端调用模板引擎,渲染得到模板字符串,然后通过JS的方式,把结果展示到页面上!【真正的页面是在客户端浏览器中怼出来的,在进行网络传输的时候,第一次传输的是模板页面,第二次传输的是数据】 优点:减轻了服务器端的渲染压力 缺点:请求次数多,用户体验可能没有服务器端渲染好(白屏效果);对SEO不友好 使用nodemon来自动重启Node服务 运行npm i nodemon -g全局安装模块 使用方式 和 node 完全一样: node 要执行文件的路径 nodemon 要执行文件的路径 文件列表小案例 获取文件的属性 在优化前, 先封装一个根据指定路径读取文件内容的方法【重点】 将读取文件夹的方法抽离出来,同时对error进行处理【重点】 相关文章 art-template 官方文档 ES6 - 模板字符串 js中的数组对象排序 i5ting/tocmd.npm 将MD转换为HTML]]></content>
<categories>
<category>node</category>
</categories>
<tags>
<tag>node</tag>
</tags>
</entry>
<entry>
<title><![CDATA[浅析node]]></title>
<url>%2F2018%2F02%2F13%2F%E6%B5%85%E6%9E%90nodejs%2F</url>
<content type="text"><![CDATA[学习Node.js时候的建议 前端和后端的知识体系不同!不要使用前端的思维方式去学习后端开发! 学习Node.js阶段,把自己当作后端开发人员! 课堂上对重点的知识做笔记,利用一切时间练习课堂代码;晚上抽半个小时梳理总结今天所学知识点;整理今天的笔记! 每天吃饭睡觉走大街上,都要在脑海中闪现今天学习的知识点【×××知识点是什么,怎么用】 什么是前端和后端 前端主要工作:写页面(HTML)、美化页面(CSS)、调用后台提供的API接口去请求或提交数据(JS);Ajax (XHR、Jquery .ajax .get $.post),Jquery更多的是进行DOM方面的操作!H5C3,高级、Angular(单页面应用程序) 前端主要在浏览器端进行开发;前端主要和页面打交道,用户看到的基本上就是我们前端写出来的(前端就是颜值) 后端主要工作:主要进行业务逻辑的操作、比如数据的增删改查、操作数据库、对外暴露API接口(后台是骨骼、灵魂) 前后端协作流程:前端写页面,前端调用后台的接口;后台接受前端发过来的请求,然后进行相关的业务处理,把处理的结果返回给前端(提供API接口) Javascript 的起源 诞生:JS的诞生和一个公司有关,有一个网景公司;当时网速只有20~30KB(电话线),当时,就是为了进行客户端验证,为了减少不必要的网络请求,提高网络效率,网景公司决定研究一门语言,来解决这个问题;JS作者在10之内,就把这门语言写出来了,叫做LiveScript,为了搭上Java这般顺风车,就把LiveScript改名为Javascript; 浏览器中的一战:微软发现浏览器市场很火,然后就自己开发了一款浏览器【IE】;为了推广自己的IE浏览器,把IE捆绑到了Windows操作系统中;JScript 一战胜利的果实:ECMAScript标准的确立;就是一个语言规范;只规定了如何进行分支判断、如何定义变量、如何定义方法….. 短暂的和平期:IE6【微软发现自己已经天下无敌了,然后就把IE6的开发团队给解散了!】在和平期间,JS这门语言主要做什么:表单验证、简单的特效、狗皮膏药(当时被称为脚本语言) 二战:【主角:谷歌、IE、火(浴火重生)狐】谷歌在2008年,借助于AJax,开始火起来了!一方面提升渲染引擎的效率、另一方面,提升JS解析引擎的效率,最终,二战以谷歌的胜利结束;谷歌有了一款世界上效率最快的JS解析引擎,叫做chrome V8引擎; Node.js 的起源 nodejs的历史由来 作者,之前主要是做高性能服务器维护的;高性能的服务器有哪些必要的条件;必须要是异步非阻塞的、基于事件驱动的; 作者在分析了上面两条基本条件之后,就开始研发高性能服务器! C,C++,Java,C#,Javascript 对于程序员来说,如果同时提供了同步和异步两种编程方式,程序员肯定会选择同步; 作者选择Javascript最主要的原因,就是这门语言是单线程的 多线程:程序员能主动开启子线程的语言,就叫做多线程的语言;Thread td = new Thread(); 作者最终,选择的语言是Javascript、解析引擎是V8; 刚开始,作者写出来的这个东西叫做web.js;后来,随着项目扩大和功能的完善,作者发现这款产品,不仅能做高性能的服务器,还能做许多事情,比如可以用这个东西去做前端包管理;于是就改名叫做Node.js; 像前端需要好用的工具,都是使用node.js开发出来的! ECMAScript、浏览器中的Javascript、Node中的Javascript ECMAScript:语言规范;不能使用ECMAScript进行实际的编程,因为它只是一个语言标准而已; 浏览器中的Javascript:是ECMAScript这个规范的具体实现;我们可以使用浏览器中的JS进行实际的编程! 浏览器中的Javascript组成部分:DOM + BOM + ECMAScript;其中,DOM和BOM和规范无关,是浏览器根据自己的需要,后期扩展的; Node中的Javascript:是ECMAScript这个规范的具体实现;也能进行实际的编程! Node中的Javascript组成部分:ECMAScript + 核心API + 其他第三方API,由于Node不需要操作浏览器对象和文档对象,所以把BOM和DOM给剔除了!核心API中,提供了 操作 文件的API 和 操作网络的 API等。。。。 什么是Node.js Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。 我们使用JS写的Node代码,说白了,只是单纯的字符串而已; 如果想要执行这些写好的代码,必须要有一个解析执行环境,这个环境就是chrome 的 V8 引擎; Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。 事件驱动:当触发某个事件的时候,执行事件中指定的代码! 非阻塞式 I/O 的模型:采用非阻塞的操作,能够提高处理的效率! Node.js 的包管理器 npm,是全球最大的开源库生态系统。 先使用npm init -y初始化一下包管理文件package.json,将来所有安装的包,都会记录到这个文件中 使用npm install 包名 –save/–save-dev去安装包;其中,install可以简写成i; –save表示把包安装到部署依赖中(在开发和部署上线都需要使用的包); –save-dev表示安装到开发依赖(只在项目开发阶段需要用到的包); –save可以简写成-S;–save-dev可以简写成-D; npm uninstall 包名 –save/–save-dev或者npm remove 包名 –save/–save-dev npm i webpack -g,其中,-g表示全局安装某些包,通过-g安装的包都在C:\Users\用户名\AppData\Roaming\npm下面 NPM 和 (Github、码云) 和 Git之间的关系 NPM是包管理平台(里面托管了各种各样的包) Github、码云 代码托管平台(托管了各种各样的代码仓库) Git是用来对代码进行分支版本管理 补充:yarn yarn和npm的作用完全一样,都是用来管理项目中的第三方依赖包的! yarn是Facebook公司推出来的一套包管理工具!【后期咱们学习React,尤其是ReactNative的时候,默认就是用Yarn去装包的】 yarn会缓存曾经安装过的包;但是npm也会缓存,但是效果不如yarn明显; yarn与npm的命令行小结 补充:如何设置npm和yarn的安装镜像: 什么是NPM淘宝镜像呢:由于每次安装包需要走国外的网络,速度很慢,所以,淘宝帮我们在国内创建了一个NPM包托管网站,能够提升使用NPM装包时候的速度! 配置npm的国内淘宝镜像: npm config set registry https://registry.npm.taobao.org –global npm config set disturl https://npm.taobao.org/dist –global 配置yarn的国内淘宝镜像: yarn config set registry https://registry.npm.taobao.org –global yarn config set disturl https://npm.taobao.org/dist –global 总结:Node.js 就是一个Javascript的服务端运行环境(依赖于Chrome 的V8引擎),为Javascript提供了服务器端编程的能力! Node.js 可以做哪些事情 【★】Node可以作为前端的一个工具去使用,通过NPM安装好用的工具;Node可以开发一些前端的工具【i5ting_toc】 【★】Node能做服务器 【★】Node还可以操作数据库并对外提供数据接口 Node能做即时聊天 【★★】为什么要学习Node.js 从企业角度分析: 企业对一专多能人才的需求:怎么才能最大限度的压榨员工;省钱 多高性能服务器的需求:怎么才能最大限度的压榨服务器; Node适合做快速的网络请求,但是不适合做大数据的业务逻辑的处理; 从自身角度分析: 挣钱; 对将来职业生涯有好处:学了Node之后,为我们构建了后端的知识体系,无论将来你会不会做后端的Node开发,但是,起码你已经构建了完整的前后端知识体系! 后面学到的任何新技术:Vue.js,React,React-Native都是基于Node的!包括后面要学的一个构建工具webpack! 在Node中执行相关的JS代码的两种方式 直接在命令行中输入node,进入Node的REPL运行环境: R:Read:读取用户输入的字符串内容 E:Evaluate:把用户输入的字符串,当作JS代码去解析执行 P:Print:打印输出Evaluate解析的结果 L:Loop:进入下一次循环 将Node代码写入到一个js文件中,然后通过node 要执行的JS文件路径去运行Node代码 环境变量 为什么可以通过命令行形式,直接启动node或者notepad呢??? 注意:每当修改了环境变量之后,需要重新启动命令窗口! 什么是环境变量 Path环境变量的作用:能够让我们通过命令行的形式快速启动一个应用程序; 用户变量和系统变量的区别、 用户变量:用户变量是属于每一个用户的,用户之间的用户变量是私有的,不共享的; 系统变量:系统变量是共享的,只能用户能够登录这台电脑,那么就能访问系统变量中的任何东西!【将来咋在配置环境变量的时候,推荐大家配置到系统变量中!】 配置环境变量的两种方式 第一种方式:直接把路径添加到path环境变量中; 第二种方式:先在环境变量中创建一个新变量,名字类似于:ZOOM_HOME这样,值是我们程序的根目录;然后,再把这个变量名,引用到 Path环境变量中,类似于:%ZOOM_HOME% Path变量的查找规则 先从当前的目录中查找有没有这个应用程序,如果有,则直接运行,如果没有,则去系统环境变量中,path环境变量下挨个查找对应的文件夹,如果在对应文件夹中能找到,则直接运行,如果找不到,最终报错! 【★★】使用fs模块来操作文件 fs.readFile fs.writeFile fs.appendFile 【★★★】fs模块中路径问题 箭头函数 Node中的使用http模块创建最基本的web服务器 之前有没有接触过Web服务器??? 其实,PHPStudy中集成了一个叫做Apache的软件;这个软件就是一个服务器软件,能够托管一个网站; 其实在Node中,并没有类似于Apache这样软件;我们可以通过简单的几行代码,写出一个Web服务器! 【★★★】HTTP协议中的 请求 - 处理 - 响应 模型 【★★】step by step 构建HTTP服务器 升级1:根据不同的URL地址响应不同HTML消息 升级2:根据不同URL地址 - 响应不同的页面 升级3:在响应不同页面的同时响应静态资源(css、图片、js) 【★★★】【难点】升级4:根据不同URL地址 - 响应不同文件的改造 【★★】【难点】网页中资源请求路径问题 相关文章 JavaScript——历史与简介 深入浅出Node.js(一):什么是Node.js I/O事件概述 理解Node.js事件驱动编程 Node.js 中文网 Node.js 英文官网 Node.js 菜鸟教程 CNode:Node.js专业中文社区 深入浅出Node.js(朴灵)完整扫描版.pdf HTTP Keep-Alive是什么?如何工作? 浏览器User-agent String里的历史故事 art-template API yarn与npm的命令行小结]]></content>
<categories>
<category>node</category>
</categories>
<tags>
<tag>node</tag>
</tags>
</entry>
<entry>
<title><![CDATA[2018年度小目标]]></title>
<url>%2F2018%2F01%2F01%2F2018%E5%B9%B4%E5%B9%B4%E5%BA%A6%E5%B0%8F%E7%9B%AE%E6%A0%87%2F</url>
<content type="text"><![CDATA[2018做什么?每一年都会给自己设置一个主题和主线,去尝试新的东西,当然大多数时候没有实现目标,一开始会因为总是没有实现而失望,渐渐也不再执着于结果,而是着重于在过程中更多的了解自己。 真的,认知自我是一个漫长的过程,作为一个内心敏感的人,尤其如此。 所以,在新的一年里,我想做以下的事情或尝试: -阅读:关于阅读,是一个我一直以来坚持的事情,持续的信息的输入,不至于囿于一个逻辑或者角度,新的一年打算阅读以下书籍: 《文明的冲突与世界秩序的重建》、《完全政变手册》、《暗店街》、《逻辑学导论 : (第11版)》、《大银行家》、《激荡三十年:中国企业1978~2008. 上》、《简单逻辑学》,如果你也喜欢阅读可以在豆瓣查看我的阅读历史的计划。 –摄影:单反买了小一年,也没有去系统的学习,好羞愧😫,本来想学好了以后拍妹子,然后找个漂亮的女票友,好像暴露了目的,ε=ε=ε=┏(゜ロ゜;)┛ -针织:别笑,我前几天突然很感兴趣,觉得好有意思,以前怎么没有留意这块?所以以后会留意这方面的信息。 -坚持长跑:特别怀念大学时候绕着内环跑步的日子。毕业后很久没有跑步,我没有别的特别喜欢的运动,跑步算是喜欢的一个。 -提升代码水平:每天坚持提交github,账号从2015年注册到现在,活跃度很低,向阮一峰学习。 -做几个我喜欢的demo,以前很多很好的想法没有去实现,现在慢慢有了将想法实现的能力,可以将之前的idea实现。 -努力攒钱,争取在后年或者大后年,攒够去阿根廷的旅游费用,当然最好是两人份的,很喜欢伊瓜苏瀑布、乌斯怀亚。我一直都认为,在伊瓜苏瀑布下的看瀑布的应该是一对。 暂时想到这里,就先写到这里吧!]]></content>
<categories>
<category>规划</category>
</categories>
<tags>
<tag>life</tag>
</tags>
</entry>
<entry>
<title><![CDATA[PHP基础语法]]></title>
<url>%2F2017%2F12%2F20%2FPHP%E5%9F%BA%E7%A1%80%E8%AF%AD%E6%B3%95%2F</url>
<content type="text"><![CDATA[目标 简单了解后端开发的流程,了解后端工作人员如何对数据进行处理,以便将来在工作中能更好的跟后端开发人员交流。 了解B/S架构开发的基本流程,能开发动态网站,并体会动态网站和静态网站的不同。 多了解一门编程语言,体会不同语言之间的异同。 前瞻知识点 B/S结构的处理流程 网络基本概念 IP 域名 DNS 网络DNS 本地DNS 端口 安装WAMP环境 W – web, A – Apache , M – MySql ,P – PHP hello world 所有PHP代码都放在标记: <?php …… ?> PHP输出方式:echo 1echo 'hello world' 注释 / 多行注释 / 多行注释不要嵌套 // 单行注释 语句分隔符 “;” PHP文件访问方式 PHP简介 PHP Hypertext Preprocessor 超文本预处理器,是嵌入到HTML文件中的服务器端的脚本语言; 一个PHP文件中,可以包含多种代码:HTML、CSS、JS、Jquery、PHP、MySQL等 PHP的语法,与C、Java、Python、JS语法比较相似; PHP是服务器端的脚本语言,脚本语言相对编程语言来说,要简单的多。 PHP程序只能运行在服务器端,在客户端看不见PHP任何代码; PHP文件的扩展名是以.php为后缀的。 语言基础 PHP与ASP、JSP一样,都是嵌入到HTML文件中的服务器端的脚本语言; PHP文件的后缀是.php PHP代码的标记:<?php …… ?> PHP中每行程序代码,必须以英文下的分号结束; 输出时间1echo date('Y-m-d H:i:s'); 默认情况下PHP输出的时间是格林威治时间,比中国时区晚8个小时 解决方案: 1.0 找到PHP的配置文件 php.ini 2.0 定位到 date.timezone 3.0 设置中国时区 12; PRC为中华人民共和国date.timezone = PRC 变量特点 变量是临时存储数据的容器; 变量是存储在内存当中; 网页执行完毕后,网页中相关的变量全部消失。 变量命名规则 变量名称只能包含:字母、数字、下划线 PHP中的变量名必须以 “\$” 符号开头,但是”\$”符号不属于变量的一部分,将来不管是赋值还是取值,都必须加上”\$” 变量名区分大小写 PHP中的变量使用不需要声明,直接赋值使用 常用的两种命名方法: 驼峰命名法 第一个单词的首字母小写,其余单词的首字母全部大写 如:userName,userId 帕斯卡命名法: 与驼峰命名法很像 唯一区别是它的首字母是大写 如:UserName,UserId 下划线命名法 所以的单词首字都小写,由下划线连接 如:user_name,user_id 变量的声明与赋值 声明 PHP中变量不需要声明,但是每个变量必须以$开 赋值: 直接给带有$的变量赋值 使用“=”,赋值运算符给变量赋值,“=”读作赋值运算符 赋值运算符的左边只能是一个变量名 如:$userName = “张三”; 变量值的读取 变量的数据类型由其中的内容决定 直接以 “$+变量名”的方式 取得可以取得变量的值 1$userName = "admin"; 可以使用“.”来连接将变量与字符串一起输出 1echo "我的名字叫".$userName 也可以在字符串的内部直接使用变量 在PHP中“.”点表示相连 在PHP中“+”加表示相加(是一种算术运算符) 123456 echo "我的名字叫做$userName"但是,如果用引号时,变量后面还有其它的有文字,就不能解析了如 echo "我的名字叫做$userName很高兴见到大家"这里就无法解析,需要在变量后面添加一个非空特殊字符,如: echo "我的名字叫做{$userNmae}很高兴见到大家"如果输出字符是单引号,则无法解析字符串 变量相关函数 header():设置服务器返回到浏览器的数据的类型 1header('content-type:text/html;charset=utf-8'); isset() 判断变量是否存在 可以用来判断变量是否定义,或者是否为null empty() 判断变量是否为空 如果 var 是非空或非零的值,则 empty() 返回 FALSE “”、0、“0”、NULL、FALSE、array()、var $var; 以及没有任何属性的对象都将被认为是空的,也就意味着,如果上述的数据用empty来判断返回的结果都为true unset() 删除变量 可以通过这个函数删除一个已经存在的变量,删除之后变量为null 可以同步删除多个变量 如果在函数中 unset() 一个全局变量,则只是局部变量被销毁,而在调用环境中的变量将保持调用 unset() 之前一样的值。(这个当我们学了全局变量与局部变量以后再说) 1unset($a,$b,$c) var_dump() 显示变量的类型和值 可以同时打印多个变量 print_r() 显示数组的元素信息 如果是字符串,数字,boolean值,会原样输出 如果是数组,会详细打印数组信息 可变变量 指一个变量的名称,由另一个变量的值来充当 1234$a = "b";$b = "c";echo $b;//输出 cecho $$a;//输出 c 如果使用可变变量,一定要加上给可变变量的变量名加上{}作为定界符 变量的值传递和引用传递 JS中的基本数据类型(只有一个值):字符串型、数值型、布尔型、undefined、null JS中的复合数据类型(多个值):数组(Array)、对象(Object)、函数(Function) 值传递: 传递的是变量中保存的值 引用传递 传递的是变量保存的引用地址 在PHP中可以使用 & 符号将值传递的变量改变为引用传递:如 1234$a = "abc";$b = &$a;$a = "123";echo "$a<br/>$b" 常量概念 常量就是值永远不变的量,即:一旦赋值无法删除,修改。 由于变量在使用时还需要去它存储的地址中去找到对应的数据,相比变量来说,常量的速度要快。(存储地址?) 命名规则 与变量基本一致 变量名称只能包含:字母、数字、下划线,常量也是 PHP中的常量不需要使用$符号开头 常量名区分大小写,一般建议使用时尽量用大写,为了与变量区分开 常用的两种命名方法: 驼峰命名法 第一个单词的首字母小写,其余单词的首字母全部大写 如:uerName,userId 下划线命名法 所以的单词首字都小写,由下划线连接 如:user_name,user_id 常量定义define() 语法: bool define ( string $name , mixed $value [, bool $case_insensitive = false ] ) 案子:通过define关键来定义 123define('TITLE',"中华人民共和国");//定义常量TITLEdefine('TITLE',"中华人民共和国",true);//加上true,使用时既可以使用TITLE,又可以使用小写title,来使用常量define('TITLE',"中华人民共和国",false);//加上false,使用时必须使用TITLE 判断是否是常量:defined() 语法: bool defined ( string $name ) defined()可以判断常量 1defined(“TITLE”);//通过defined判断常量时,常量一定要加上引号 魔术常量 量的值在程序运行过程中不会改变。但是,魔术常量的值,会随着所在位置的不同而改变。 魔术常量都由系统定义好的,我们直接使用就好了,常用的有: __LINE__:得到当前代码的行号 __FILE__:得到当前文件的路径 __DIR__:得到当前文件所在目录 __FUNCTION__:得到所在函数 数据类型 数据类型分类 12345js中的数据类型: 简单数据类型: Number,string,bool,undefined 复杂数据类型 数组,对象,null 基本数据类型:字符串型(string)、整型(int)、浮点型(float:小数)、布尔型(bool) 复合数据类型:数组、对象 特殊数据类型:资源、NULL 数据类型判断 is_bool(),判断变量是不是布尔型 is_int(),判断变量是不是整型 is_string() 判断变量是不是字符串型 is_float() 判断变量是不是浮点型 is_numeric() 判断变量不是数值型或数字字符串。 is_array() 判断变量是不是数组 is_null() 判断变量是否为NULL is_resource() 判断变量是不是资源型 123456789//判断用户是否大于18岁,如果大于18岁,显示网页内容,如果没有,则不显示$age = 20;if(isset($age) && is_numeric($age)) { if($age >18) { echo('显示网页'); } else { echo ('不显示'); }} 整型 关键字:int 在js中所有的数字都用number来表示,但是在PHP,数字有不同的划分,其中整数(正整数,0,负整数)用整型来表示 整型包含正整数、负整数、零0 可以用10进制、八进制、16进制表示 123echo 100;//十进制echo 011;//八进制,以0开头的整数,结果为9echo 0x9a;//十六进制,以0x开头,结果为154 PHP中最大整数:PHP_INT_MAX,(2^31)-1 如果数字超出来了整数据范围,则类型会变为float 浮点型 关键字:float PHP中除了整数还有浮点型,一般浮点数会包含小数,比整型更加精确 浮点型的范围:1.8E-308~1.8E+308 注意点: 如果要对浮点数进行操作,就好先将浮点数转为整数再处理 1234567891011121314//以下做法不可取if(2.1/0.7 == 3) { echo '相等';} else { echo '不相等';}//以下是正确做法$a = 2.1 * 10;$b = 0.7 * 10;if($a/$b == 3) { echo '相等';} else { echo '不相等';} 字符串 字符串定义的四种方式 用单引号可以定义一个字符串;如:$a = ‘abc’; 单引号内无法解析变量 转义符号有:\’ 用双引号来定义一个字符串;如:$a = “abc”; 双引号内可以解析变量 转义符号有:\\、\’、\”、\$、\r、\n、\t 定义长字符串:heredoc 12345678910<?phpheader('content-type:text/html;charset=utf-8');$a = 'abc';$aa = <<<heredoc<html>123$a</html>heredoc;echo $aa;?> 使用注意: 1)字符串放在两个heredoc标签之间 2)以“<<<heredoc”作为开始,不用带分号 3)以“heredoc;”作为结束,要带分号 4)标签名称可以自定义 5)结束标签必须放单独一行 定义长字符串:nowdoc 12345678910<?phpheader('content-type:text/html;charset=utf-8');$a = 'abc';$aa = <<<'nowdoc'<html>123$a</html>nowdoc;echo $aa;?> 使用注意: 与上述一样,但是开始的标签名必须加单引 不能解析变量 布尔型 关键字:bool 概念 true表示真,flase表示假 转换: 0,””,NULL,0.0,”0”,arry() 空数组会转为false 其它的数据都会转为true 空型 用null表示,表示没有 以下情况会出现NULL 可以直接给一个变量,赋一个NULL值; 未定义的变量的值,也是NULL; unset()删除一个变量后,该变量的值为NULL。 数据类型转换 概念 数据有不同的类型,有时间操作数据时需要转换类型以后才能完成操作,比如计算 类型的转换分为两种:自动转换(隐式转换)、手动转换(强制转换)在js中则被称为显示转换 大部分情况下数据是自动转换,少数情况下强制转换。 强制转换 通过关键字进行转换 12345678(bool)$a //强制转换为bool //除了0,0.0,”“,”0“,NULL,array(),转为false,其余都为true(int)$a //强制转换为int //转换会将小数去尾(string)$a //强制转换为string //null也可以转为‘’;(array)$a //强制转换为array(object)$a //强制转换为object 进制转换 进制就进位制,X进制,就是逢X进一。同理:十进制就是逢十进一,二进制逢二进一等等。 8进制:有8个基本数,分别为0、1、2、3、4、5、6、7,运算规则”逢8进1”; 16进制:有16个基本数,分别为0、1、2、3、4、5、6、7、8、9、A、B、C、D、E、F,运算规则”逢16进1”。 2进制:有2个基本数,分别是0、1,运算规则”逢2进1”。 转换方法: decbin()将十进制转换为2进制 bindec()将2进制转换为十进制 decoct()将十进制转换为8进制 octdec()将八进制转换为10进制 dechex()将十进制转换为16进制 hexdec()将16进制转换为十进制 运算符 算数运算符+、-、*、/、%、++、– 赋值运算符=、+=、-=、*=、/=、%= 字符串运算符. 和 .= 案例:隔行变色 1234567891011121314151617181920212223<?phpheader('content-type:text/html;charset=utf-8');$str = "<table width='500' border='1' align='center'>";$str .= "<tr bgColor='#f0f0f'>"; $str .= " <th>编号</th>"; $str .= " <th>新闻编号</th>"; $str .= " <th>发布时间</th>"; $str .= "</tr>";for($i = 0 ; $i < 10 ; $i ++) { if($i % 2 == 0) { $str .= "<tr bgColor='#f0f0f'>"; } else { $str .= "<tr>"; } $str .= "<td>$i</td>"; $str .= "<td>&nbsp;</td>"; $str .= "<td>&nbsp;</td>"; $str .= "</tr>";}$str .= "</table>";echo $str;?> 比较运算符>、<、>=、<=、\=\=、!=、\=\=\=、!== 案例:字符串比较 ASCII码表:如果两个字符进行比较,比较的本质是它们对应的ASCII码 1echo ("a" > 120); 逻辑运算符&&、||、! 实例:闰年判断 123456789//条件://1)能够被4整除,且不能被100整除//2)能被400整除$year = 2000;if ($year%4 == 0 && $year % 100 !=0 || $year % 400 ==0) { echo "{$year}是闰年";} else { echo "{$year}不是闰年";} 运算符优先级 三元运算符 案例:比较两个数的大小 123$a = 123;$b = 456;$max = $a > $b ? $a : $b; 错误控制运算符(@) 作用:主要用来屏蔽表达式的错误: 可以用在变量、常量、函数调用之前,但是不能用在函数定义、类定义之前。 123$a = 100;$b = 0;echo @($a / $b); 流程控制 顺序结构 赋值语句 1$a = 123; 分支结构 分支结构(if) 多条件判断(if else if else) 案例:根据分数给评语。 多分支结构(switch) 案例:完成今天星期几 循环结构 while do while for break和continue 案例: 1)得到1~100的所有整数相加之和 2)输出100~1000以内所有水仙花数 3)输出九九乘法表 数组概念 数组是一组数的集合。如:$arr = array(1,2,3,4,5,6); PHP中数组的分类 枚举数组 数组元素的下标(索引),是从0开始的正整数,如:$arr = array(1,2,3,4,5,6); 关联数组: 数组元素的下标,是一个字符串。如:$arr[‘edu’] = ‘大专’ 关联数组无法通过数字下标取得对应的元素 混合数组: 两种下标都有的数组,就是混合数组。如:$arr = array(100, ‘edu’=>’大专’ , ‘tel’ => ‘130110’) 多维数组: 数组元素的值,是一个数组,还可以理解为:数组嵌套数组。如:$arr[5][2] = 100 数组的创建 使用 array() 函数来创建数组 创建枚举数组: 下标是从0 开始的正整数 1$arr = array($value1,$value2,$value3,....); 创建关联数组 $key是重新给数组元素指定一个下标 下标和元素的值之间连接号是”=>”,又称”重载元素下标” 1$arr = array([$key=>]$value,[$key=>]$value,[$key=>]$value,....) 创建混合数组 有字符串下标的元素,就没有整型下标 如果$key省略了,就用默认下标 下标只能是整型以及字符串 如果元素没有指定下标,会在最大整数下标的基础上加1. 1$arr[] = $value 如果$arr数组不存在的话,会先创建数组,并添加第1个数组元素,第1个元素的下标为0 如果$arr已经存在 的话,添加新的数组元素,新的数组元素的下标,是最大整数下标+1 创建混合数组: 由语法1,2混合创建数组 123456$arr[] = 12;$arr[] = 13;$arr["a"] =14;$arr[] = 15;$arr[20] = 16;$arr[] = 19; 多维数组 多维数组,即数组中的元素又是一个数组 123456$arr = array( array(1,2,3,4), array(10,20,30,40), array('a','b','c'), "abc"); 数组中的函数 count() 计算数组中的单元数目或对象中的属性个数 语法:int count ( mixed $var [,\$mode = COUNT_NORMAL ] ) 参数: $var 是对象或者数组 $model: 如果可选的 mode 参数设为 COUNT_RECURSIVE(或 1),count() 将递归地对数组计数。对计算多维数组的所有单元尤其有用。mode 的默认值是 0。count()识别不了无限递归 unset() 删除变量或数组元素 语法:void unset ( mixed $var [, mixed $… ] ) 删除元素后,仅仅只是相当于将数据从数组中移除掉了,对数组的下标没有影响 数组的遍历 使用for加next()遍历数组 此方法比较麻烦 1234for($i = 0; $i < count($arr); $i++) { echo key($arr).current($arr)."<br/>" next($arr);} key():当前指针处元素的键名(下标),不会移动指针 current():返回当前指针处元素的值,不会移动指针 next(): 返回下一个元素的值,指针移动到下一个元素的开头 PHP中的数组传值时为值传递1234$arr = array(1,2,3,4);$arr1 = $arr;$arr1[0] = 100;echo $arr[0];//1 函数概念 函数是一段命名的代码段 封装一代码,将来要执行时可以直接调用 定义函数的方式1234567891011/* function 定义函数的关键字 functionName 函数的名称 arguments 形参列表 retValue 返回值 fnCode 功能代码*/function functionName(arguments){ // fnCode return retValue;} function: 定义函数的关键字,不区分大小写 functionName:函数名称,命名规则与变量一样,只是不需要加$符号 小括号():主要用来接受传递过来的参数 arguments:定义函数时的参数,形参 return 函数的返回值。 函数的调用: 实参:调用函数时,传递的参数就是实参,含有真正数据的 形参:定义函数时的参数 案例:得到数组中所有数组的和 函数参数的传递: 值传递 函数参数的值传递,就是将参数的值,复制一份,传到函数中 值传递速度比较慢,但修改其中一个不会影响到另一个; 引用传递 引用传递速度比较快,但修改其中一个,另一个也会改变。 引用传递,是将一个变量的地址,复制一份,传到函数中 默认参数 函数中有些参数可能是固定不变的,为了操作方便,可以用默认参数来代替 默认参数必须放在非默认参数的右边 默认参数的值,只能是:字符串、整型、浮点型、布尔型、NULL、数组 123function showSelf($name,$age=19,$gender="男") { echo "我叫{$name},今天{$age},性别{$ender}";} 函数可变数量参数 func_get_args( void ):返回一个参数列表的枚举数组,参数数组的下标是从0开始的整数 func_get_arg( $index ):返回参数数组中,指定下标的参数的值; func_num_args( void ):返回实参的个数。 1234567891011121314function getSum() { $arr = func_get_args(); $len = func_num_args(); $sum = 0; print_r($arr); echo "<br/>"; print_r($len); echo "<br/>"; for($i = 0; $i < $len; $i++) { $sum += func_get_arg($i); } echo "$sum"; } getSum(1,2,3,4,5); 函数的返回值 函数的返回值,通过return语句来实现; 将函数的执行结果,返给了函数调用者; return语句一旦执行,函数立即结束,函数剩余的代码不再执行了。 return语句有”中断”函数、”退出”函数; return不能同时返回多个值,只能返回一个值。如果想返回多个值,可以放入数组 常用系统函数字符串 定义字符串基本语法 单引号字符串 双引号字符串 nowdoc字符串 heredoc字符串 字符串常用api strlen() 获取字符串长度 substr() 返回字符串的子串 strtoupper() 将字符串转化为大写 strtolower() 将字符串转化为小写 ucfirst() 将字符串的首字母转换为大写 trim() 去除字符串首尾处的空白字符 ltrim() 删除字符串开头的空白字符 rtrim() 删除字符串末端的空白字符 strrev() 反转字符串 strpos() 查找字符串首次出现的位置,从左往右查找 strrpos() 计算指定字符串在目标字符串中最后一次出现的位置,从右往左查找 strchr()和strstr() 两者一样,用于查找字符串的首次出现 str_replace() 子字符串替换 str_repeat() 重复一个字符串 数学 常用函数 max() 求最大值 min() 求最小值 rand() 产生一个随机整数 mt_rand() 更高效的随机数 ceil() 进一法取整 floor() 舍去法取整 round() 对浮点数进行四舍五入 pow() 次方运算 abs() 求绝对值 sqrt() 开方运算 日期 常用函数 time() 返回自从 Unix 纪元(格林威治时间 1970 年 1 月 1 日 00:00:00)到当前时间的秒数 date() 格式化一个本地时间/日期 microtime() 返回当前 Unix 时间戳和微秒数 strtotime() 将任何英文文本的日期时间描述解析为 Unix 时间戳]]></content>
<categories>
<category>PHP</category>
</categories>
<tags>
<tag>PHP</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Vue事件修饰符]]></title>
<url>%2F2017%2F10%2F15%2Fvue%E9%98%BB%E6%AD%A2%E4%BF%AE%E9%A5%B0%E7%AC%A6%2F</url>
<content type="text"><![CDATA[事件修饰符: 在事件处理程序中调用 event.preventDefault() 或 event.stopPropagation()是非常常见的需求。尽管我们可以在methods 中轻松实现这点,但更好的方式是:methods 只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。为了解决这个问题, Vue.js 为 v-on 提供了 事件修饰符。通过由点(.)表示的指令后缀来调用修饰符。 .stop .prevent .capture .self .once 1234567891011121314151617<!-- 阻止单击事件冒泡 --><a v-on:click.stop="doThis"></a> <!-- 提交事件不再重载页面 --><form v-on:submit.prevent="onSubmit"></form> <!-- 修饰符可以串联 --><a v-on:click.stop.prevent="doThat"></a> <!-- 只有修饰符 --><form v-on:submit.prevent></form> <!-- 添加事件侦听器时使用事件捕获模式 --><div v-on:click.capture="doThis">...</div> <!-- 只当事件在该元素本身(而不是子元素)触发时触发回调 --><div v-on:click.self="doThat">...</div> 按键修饰符: 在监听键盘事件时,我们经常需要监测常见的键值。 Vue 允许为 v-on 在监听键盘事件时添加按键修饰符: 12<!-- 只有在 keyCode 是 13 时调用 vm.submit() --><input v-on:keyup.13="submit"> 12345<!-- 同上 --><input v-on:keyup.enter="submit"> <!-- 缩写语法 --><input @keyup.enter="submit"> 全部的按键别名:记住所有的 keyCode 比较困难,所以 Vue 为最常用的按键提供了别名: .enter .tab .delete (捕获 “删除” 和 “退格” 键) .esc .space .up .down .left .right 可以通过全局 config.keyCodes 对象自定义按键修饰符别名: 12// 可以使用 v-on:keyup.f1Vue.config.keyCodes.f1 = 112 同样的在v-model里面修饰符也有很多应用。]]></content>
<categories>
<category>框架</category>
</categories>
<tags>
<tag>Vue</tag>
</tags>
</entry>
<entry>
<title><![CDATA[图表库ECharts介绍]]></title>
<url>%2F2017%2F08%2F26%2F%E5%9B%BE%E8%A1%A8%E5%BA%93ECharts%E4%BB%8B%E7%BB%8D%2F</url>
<content type="text"><![CDATA[简介 ECharts是百度公司出品的一款非常强大的数据可视化工具,补充说明,目前已经推出微信小程序,那么就让我们一起来了解如何使用入门吧。 ECharts,一个纯 Javascript 的图表库,可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器(IE8/9/10/11,Chrome,Firefox,Safari等),提供直观,生动,可交互,可高度个性化定制的数据可视化图表。ECharts 提供了常规的折线图,柱状图,散点图,饼图,K线图,用于统计的盒形图,用于地理数据可视化的地图。 官网github: https://github.com/ecomfe/echarts ECharts官网: http://echarts.baidu.com/ star:23k+ 步骤引入js文件12345678<!DOCTYPE html><html><head> <meta charset="utf-8"> <!-- 引入 ECharts 文件 --> <script src="echarts.min.js"></script></head></html> 准备一个dom容器1<div id="main" style="width: 600px;height:400px;"></div> 初始化一个实例1234<script type="text/javascript"> // 基于准备好的dom,初始化echarts实例 var myChart = echarts.init(document.getElementById('main'));</script> 设置图表的配置项和数据12345678910111213141516171819// 指定图表的配置项和数据 var option = { title: { text: 'ECharts 入门示例' }, tooltip: {}, legend: { data:['销量'] }, xAxis: { data: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"] }, yAxis: {}, series: [{ name: '销量', type: 'bar', data: [5, 20, 36, 10, 10, 20] }] }; 最后,显示图表1myChart.setOption(option); APIecharts echarts.init(dom,theme,opts) 实例一个图表对象 dom 实例容器,一般是一个具有高宽的div元素。 theme 应用的主题。 opts 可选参数 width 可显式指定实例宽度,单位为像素。如果传入值为 null/undefined/auto,则表示自动取 dom(实例容器)的宽度。 height 可显式指定实例高度,单位为像素。如果传入值为 null/undefined/'auto',则表示自动取 dom(实例容器)的高度。 renderer 渲染器 echart.connect(group) 多个图表实例实现联动 group 图表实例的 id,或者图表实例的数组。 echarts.getInstanceByDom 获取dom容器上的实例 图表配置项 title 标题组件 text string 主标题文本 link string 主标题文本超链接 target string 超链接打开方式 self 当前窗口 blank 新窗口 subtext string 副标题文本 left/right/top/bottom 距离容器的距离 legend 图例组件 type 图例类型 plain 普通图例 scroll 可滚动翻页的图例 orient 图例列表的布局朝向 horizontal vertical left/right/top/bottom data formatter 格式化图例文本 xAxis / yAxis name string 坐标轴名称 type string 坐标轴类型 value 竖直轴 适用于连续数据 category 类目轴,适用于离散的类目数据,为这个类型的时候必须使用data来设置类目数据 time 时间轴 适用于连续的时序数据 log 对数轴 适用于对数数据 min 坐标轴刻度最小值 max 坐标轴刻度最大值 inverse 是否反向坐标轴 默认false ECharts3新添加 tooltip 提示框组件 trigger 触发类型 item 主要在散点图,饼图等无类目轴的图表中使用 axis 坐标轴触发,主要在柱状图,折线图等会使用类目轴的图表中使用 none 什么都不触发 triggerOn 提示框触发条件 mouseover click mouseover|click none confine 是否将提示框限制在图标都区域内 default:false series 系列列表 type 图表类型 line 折线图 bar 柱状图/条形图 pie 饼图 scatter 散点图 map 地图 … name 系列名称 用于tooltip的显示 data 系列中的数据内容数组。数组项通常为具体的数据项。 markPoint 图标标记 markLine 图表标记线 案例(浏览器使用率):12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title> <script src="js/echarts.common.min.js"></script> <style> #main { width: 600px; height: 400px; margin: 100px auto; } </style></head><body><div id="main"></div><script> var chart = echarts.init(document.querySelector("#main")); var option = { title: { text: "各浏览器使用量", subtext: "纯属虚构", left: "center" }, tooltip: { trigger: "item", formatter: "{a} <br/>{b} : {c} ({d}%)" },// legend: {// orient: "vertical",// left: "left",// data: ["张三","王二","找钱","孙俪","周哈","李四"]// }, legend: { orient: 'vertical', left: 'left', data: ['Chrome', 'Firefox', 'IE', 'Opera', 'Safari'] }, series: [ { name: "浏览器使用比例", type: "pie", radius: "50%", //圆形半径 支持百分比 百分比的时候 是相对一容器宽高较小的那项的一半 center: ["50%", "50%"], data: [ {value: 50000, name: "Chrome"}, {value: 35000, name: "Firefox"}, {value: 10000, name: "IE"}, {value: 15000, name: "Opera"}, {value: 20000, name: "Safari"} ] } ] } chart.setOption(option);</script></body></html> 效果图: ]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>ECharts</tag>
</tags>
</entry>
<entry>
<title><![CDATA[console命令详解]]></title>
<url>%2F2017%2F06%2F03%2Fconsole%E5%91%BD%E4%BB%A4%E8%AF%A6%E8%A7%A3%2F</url>
<content type="text"><![CDATA[熟悉前端的不会对console和alert陌生,两者在调试的时候可谓是法宝级别的工具,但是关于console,其实远远不止于console.log这一个简单的命令,它能做的事情有很多,那么让我们慢慢了解一下,它有哪些功能吧。 一、显示信息的命令 最常用的就是console.log,查看效果的方法:如果是Chrome浏览器,就打开“开发者工具”,按快捷键“Ctrl+F12”,或者右键点击页面,选择“检查”菜单。然后在“Console”面板可以查看输出结果。 如果是火狐浏览器的话,按组合键“Ctrl+Shift+K”可以打开“网页控制台”。 示例代码: 12345678910111213141516171819202122<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1" /><title>常用console命令之显示信息_何问起</title><base target="_blank" /><meta charset="utf-8" /><style>a{color:blue;}</style></head><body><div>常用console命令之显示信息,请查看浏览器的console面板。<a href="http://hovertree.com/h/bjaf/gk6698g3.htm">说明</a><a href="http://hovertree.com">首页</a></div><script type="text/javascript">console.log('hello hovertree');console.info('信息 何问起');console.error('错误');console.warn('警告');</script></body></html> 二:占位符 console上述的集中度支持printf的占位符格式,支持的占位符有:字符(%s)、整数(%d或%i)、浮点数(%f)和对象(%o) 代码如图: 1234567891011121314151617<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1" /><title>console命令之占位符何问起</title><base target="blank" /><meta charset="utf-8" /><style>a{color:blue;}</style></head><body><div>console命令之占位符 说明首页</div><script type="text/javascript">console.log("%d年%d月%d日",2016,11,11);</script></body></html> 效果如图: 三、信息分组 示例代码: 123456789101112131415161718192021222324<!DOCTYPE html><html><head><meta name="viewport" content="width=device-width, initial-scale=1" /><title>常用console命令_何问起</title><base target="_blank" /><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><style>a{color:blue;}</style></head><body><div>常用console命令之信息分组<a href="http://hovertree.com/h/bjaf/gk6698g3.htm">说明</a><a href="http://hovertree.com">首页</a></div><script type="text/javascript">console.group("第一组信息");console.log("第一组第一条:何问起(http://hovertree.com)");console.log("第一组第二条:柯乐义(http://keleyi.com)");console.groupEnd();console.group("第二组信息");console.log("第二组第一条:HoverClock 一个jQuery时钟插件");console.log("第二组第二条:欢迎使用");console.groupEnd();</script></body></html> 效果如图: 四、查看对象的信息 console.dir()可以显示一个对象所有的属性和方法。 示例代码: 123456789101112131415161718192021222324<!DOCTYPE html><html><head><meta name="viewport" content="width=device-width, initial-scale=1" /><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><title>用console.dir方法查看对象信息_何问起</title><base target="_blank" /><meta charset="utf-8" /><style>a{color:blue;}</style></head><body><div>用console.dir方法查看对象信息<a href="http://hovertree.com/h/bjaf/gk6698g3.htm">说明</a><a href="http://hovertree.com">首页</a></div><script type="text/javascript">var info = {blog:"http://hovertree.com",时钟插件:"HoverClock",message:"欢迎使用!"};console.dir(info);</script></body></html> 效果图: 五、显示某个节点的内容 console.dirxml()用来显示网页的某个节点(node)所包含的html/xml代码。 示例代码: 1234567891011121314151617181920212223<!DOCTYPE html><html><head><meta name="viewport" content="width=device-width, initial-scale=1" /><title>常用console命令_何问起</title><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><base target="_blank" /><style>a{color:blue;}</style></head><body><div>console.dirxml()用来显示网页的某个节点(node)所包含的html/xml代码<a href="http://hovertree.com/h/bjaf/gk6698g3.htm">说明</a><a href="http://hovertree.com">首页</a></div><div id="info"><h3>我的博客:hovertree.com</h3><p>HoverTreeImg插件,欢迎使用</p></div><script type="text/javascript">var info = document.getElementById('info');console.dirxml(info);</script></body></html> 效果图: 六、判断变量是否是真 console.assert()用来判断一个表达式或变量是否为真。如果结果为否,则在控制台输出一条相应信息,并且抛出一个异常。 示例代码: 1234567891011121314151617181920212223<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1" /><title>console.assert使用_何问起</title><base target="_blank" /><meta charset="utf-8" /><style>a{color:blue;}</style></head><body><div>console.assert方法的使用<a href="http://hovertree.com/h/bjaf/gk6698g3.htm">说明</a><a href="http://hovertree.com">首页</a></div><script type="text/javascript">var result = 1;console.assert( result );var year = 2014;console.assert(year == 2018 );</script></body></html> 效果图: 七、追踪函数的调用轨迹。 console.trace()用来追踪函数的调用轨迹。 示例代码: 1234567891011121314151617181920212223242526<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1" /><title>追踪函数的调用轨迹_何问起</title><base target="_blank" /><meta charset="utf-8" /><style>a{color:blue;}</style></head><body><div>javascript中console.trace()方法示例<a href="http://hovertree.com/h/bjaf/gk6698g3.htm">说明</a><a href="http://hovertree.com">首页</a></div><script type="text/javascript">/*函数是如何被调用的,在其中加入console.trace()方法就可以了 -- 何问起*/function add(a,b){console.trace();return a+b;}var x = add3(1,1);function add3(a,b){return add2(a,b);}function add2(a,b){return add1(a,b);}function add1(a,b){return add(a,b);}</script></body></html> 效果图: 八、计时功能 console.time()和console.timeEnd(),用来显示代码的运行时间。 示例代码: 123456789101112131415161718192021<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1" /><title>显示代码的运行时间_何问起</title><base target="_blank" /><meta charset="utf-8" /></head><body><div>console.time()和console.timeEnd(),用来显示代码的运行时间。<a href="http://hovertree.com/h/bjaf/gk6698g3.htm">说明</a><a href="http://hovertree.com">首页</a></div><script type="text/javascript">console.time("控制台计时器一");for(var i=0;i<10000;i++){for(var j=0;j<1000;j++){}}console.timeEnd("控制台计时器一");</script></body></html> 效果图: 本文转载自云栖社区]]></content>
<categories>
<category>工具</category>
</categories>
<tags>
<tag>console</tag>
</tags>
</entry>
<entry>
<title><![CDATA[提高js性能技巧]]></title>
<url>%2F2017%2F06%2F01%2F%E6%8F%90%E9%AB%98js%E6%80%A7%E8%83%BD%E6%8A%80%E5%B7%A7%2F</url>
<content type="text"><![CDATA[提高JS性能的技巧1评估局部变量主要针对IE而言,由于局部变量的查找是从最特定作用域到最大作用域,且可以通过多个域层级,所以这种查找会导致查询到通用的结果。在定义函数作用域的时候,如果一个局部变量在之前没有进行过var变量声明, 那么此处一定要在变量名前加上var关键字以定义其当前的作用域和防止查询,从而提高代码的速度。 2.除非必要,否则不要使用嵌套循环减少不必要的循环,例如 for 和 while 循环,以保持 JavaScript 的线性,并避免需要遍历数千个对象。无用的循环可能会导致浏览器处理代码时更困难,从而减缓速度。 3.缓存对象以提高性能很多时候,会重复使用脚本来访问某个对象。 将重复访问的对象存储在用户定义的变量中,并且之后在引用该对象时使用此变量,可以立刻实现性能提升。 4.使用.js文件来缓存脚本使用这种技术可以实现性能提升,因为它允许浏览器只加载脚本一次,当页面被重新加载或重新访问时只需要从缓存中调用脚本即可。 5.使用Gzip压缩文件使用 GZip 可以明显地降低 JavaScript 文件的大小,节省带宽,并加快响应时间。 有时 JavaScript 文件非常大,如果没有经过压缩,它可能会造成网站瘫痪。较小的文件能提供更快、更令人满意的网页体验。 6.尽量减少对HTTP的请求通过结合外部文件和直接在 XHTML 页面中嵌入 JavaScript 来尽量减少对 HTTP 返回页面的请求。 每一个特定的 HTTP 请求传输至服务器端都会导致大量的时延。 7.不要重复使用相同的脚本复的脚本对性能会造成显著的影响。重复的脚本会创建不需要的HTTP请求,尤其是在IE浏览器中。在HTML页面中使用脚本标签,可以帮助避免意外地生成重复的脚本。 8.缩短作用域链全局作用域通常较慢,因为每次执行函数的时候,它会引发创建一个临时的调用作用域,JavaScript 会在作用域链中搜索第一个对象,如果找不到该变量,则会遍历链直到搜索到全局对象。 9.使用函数内联函数内联有助于减少函数调用的成本,并用被调用的函数体替换函数调用。 在 JavaScript 中执行函数调用是一个高成本的操作,因为需要执行几个准备步骤:为参数分配空间,复制参数以及解析函数名称。 10.尽量不要使用全局变量由于脚本引擎需要搜索作用域,所以当从函数内部或其他作用域内引用全局变量时,如果局部作用域丢失,全局变量将被销毁。如果全局作用域中的变量并不一直存在于脚本的生命周期,则可以提升性能。 11.不要一直保留对其他文档的引用当脚本结束后不再保留对其他文档的引用可以实现更快的性能。因为对其他文档中的对象进行的任何引用不会被保存在整个 DOM 树中,并且脚本环境也不会一直保留在内存中。 因此文档本身不用再被加载。 12.不要误用 for-in由于“for-in”循环需要脚本引擎构建包含所有可枚举属性的列表,因此 for 循环内的代码是不会修改这个列表的。 在 for 循环内部,预先计算出列表的长度并赋给变量 len,然后进行迭代。]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>js</tag>
</tags>
</entry>
<entry>
<title><![CDATA[js知识点常记]]></title>
<url>%2F2017%2F05%2F13%2Fjs%E7%9F%A5%E8%AF%86%E7%82%B9%E5%B8%B8%E8%AE%B0%2F</url>
<content type="text"><![CDATA[1.hasOwnProperty相关为了判断一个对象是否包含自定义属性而不是原型链上的属性,我们需要使用继承自 Object.prototype 的 hasOwnProperty方法。hasOwnProperty 是 JavaScript 中唯一一个处理属性但是不查找原型链的函数。 123456789// 修改Object.prototypeObject.prototype.bar = 1; var foo = {goo: undefined};foo.bar; // 1'bar' in foo; // truefoo.hasOwnProperty('bar'); // falsefoo.hasOwnProperty('goo'); // true 注意: 通过判断一个属性是否 undefined 是不够的。 因为一个属性可能确实存在,只不过它的值被设置为 undefined。 hasOwnProperty 作为属性JavaScript 不会保护 hasOwnProperty 被非法占用,因此如果一个对象碰巧存在这个属性, 就需要使用外部的 hasOwnProperty 函数来获取正确的结果。 1234567891011var foo = { hasOwnProperty: function() { return false; }, bar: 'Here be dragons'};foo.hasOwnProperty('bar'); // 总是返回 false// 使用其它对象的 hasOwnProperty,并将其上下文设置为foo({}).hasOwnProperty.call(foo, 'bar'); // true 当检查对象上某个属性是否存在时,hasOwnProperty 是唯一可用的方法。 同时在使用 for in loop遍历对象时,推荐总是使用 hasOwnProperty 方法, 这将会避免原型对象扩展带来的干扰。 for in 循环和 in 操作符一样,for in 循环同样在查找对象属性时遍历原型链上的所有属性。 1234567// 修改 Object.prototypeObject.prototype.bar = 1;var foo = {moo: 2};for(var i in foo) { console.log(i); // 输出两个属性:bar 和 moo} 注意: 由于 for in 总是要遍历整个原型链,因此如果一个对象的继承层次太深的话会影响性能。 由于不可能改变 for in 自身的行为,因此有必要过滤出那些不希望出现在循环体中的属性, 这可以通过 Object.prototype 原型上的 hasOwnProperty 函数来完成。 使用 hasOwnProperty 过滤123456// foo 变量是上例中的for(var i in foo) { if (foo.hasOwnProperty(i)) { console.log(i); }} 推荐总是使用 hasOwnProperty。不要对代码运行的环境做任何假设,不要假设原生对象是否已经被扩展了。 2.命名函数的赋值表达式另外一个特殊的情况是将命名函数赋值给一个变量。 1234var foo = function bar() { bar(); // 正常运行}bar(); // 出错:ReferenceError bar 函数声明外是不可见的,这是因为我们已经把函数赋值给了 foo; 然而在 bar 内部依然可见。这是由于 JavaScript 的命名处理所致, 函数名在函数内总是可见的。 注意:在IE8及IE8以下版本浏览器bar在外部也是可见的,是因为浏览器对命名函数赋值表达式进行了错误的解析, 解析成两个函数 foo 和 bar 3.方法的赋值表达式另一个看起来奇怪的地方是函数别名,也就是将一个方法赋值给一个变量。 12var test = someObject.methodTest;test(); 上例中,test 就像一个普通的函数被调用;因此,函数内的 this 将不再被指向到 someObject 对象。而是指向了window。 4.循环中的闭包一个常见的错误出现在循环中使用闭包,假设我们需要在每次循环中调用循环序号 12345for(var i = 0; i < 10; i++) { setTimeout(function() { console.log(i); }, 1000);} 上面的代码不会输出数字 0到 9,而是会输出数字10 十次。 当 console.log 被调用的时候,匿名函数保持对外部变量i的引用,此时 for循环已经结束,i的值被修改成了10. 为了得到想要的结果,需要在每次循环中创建变量 i的拷贝。 为了避免引用错误,为了正确的获得循环序号,最好使用 匿名包装器(注:其实就是我们通常说的自执行匿名函数)。 1234567for(var i = 0; i < 10; i++) { (function(e) { setTimeout(function() { console.log(e); }, 1000); })(i);} 外部的匿名函数会立即执行,并把 i 作为它的参数,此时函数内 e 变量就拥有了 i 的一个拷贝。 当传递给 setTimeout 的匿名函数执行时,它就拥有了对 e 的引用,而这个值是不会被循环改变的。 有另一个方法完成同样的工作,那就是从匿名包装器中返回一个函数。这和上面的代码效果一样。 1234567for(var i = 0; i < 10; i++) { setTimeout((function(e) { return function() { console.log(e); } })(i), 1000)} 5.对象使用和属性JavaScript 中所有变量都可以当作对象使用,除了两个例外 null 和 undefined。 123456false.toString(); // 'false'[1, 2, 3].toString(); // '1,2,3'function Foo(){}Foo.bar = 1;Foo.bar; // 1 一个常见的误解是数字的字面值(literal)不能当作对象使用。这是因为 JavaScript 解析器的一个错误, 它试图将点操作符解析为浮点数字面值的一部分。 12.toString(); // 出错:SyntaxError 有很多变通方法可以让数字的字面值看起来像对象。 1232..toString(); // 第二个点号可以正常解析2 .toString(); // 注意点号前面的空格(2).toString(); // 2先被计算 删除属性的唯一方法是使用 delete 操作符;设置属性为 undefined 或者 null 并不能真正的删除属性, 而仅仅是移除了属性和值的关联。 1234567891011121314var obj = { bar: 1, foo: 2, baz: 3};obj.bar = undefined;obj.foo = null;delete obj.baz;for(var i in obj) { if (obj.hasOwnProperty(i)) { console.log(i, '' + obj[i]); }} 上面的输出结果有 bar undefined 和 foo null - 只有 baz 被真正的删除了,所以从输出结果中消失。 6.arguments 对象JavaScript 中每个函数内都能访问一个特别变量 arguments。这个变量维护着所有传递到这个函数中的参数列表。 arguments 变量不是一个数组(Array)。 尽管在语法上它有数组相关的属性 length,但它不从 Array.prototype继承,实际上它是一个对象(Object)。 因此,无法对 arguments 变量使用标准的数组方法,比如 push, pop 或者 slice。 虽然使用 for 循环遍历也是可以的,但是为了更好的使用数组方法,最好把它转化为一个真正的数组。 转化为数组下面的代码将会创建一个新的数组,包含所有 arguments 对象中的元素。 1Array.prototype.slice.call(arguments); arguments 对象为其内部属性以及函数形式参数创建 getter 和 setter 方法。 因此,改变形参的值会影响到 arguments 对象的值,反之亦然。 123456789101112function foo(a, b, c) { arguments[0] = 2; a; // 2 b = 4; arguments[1]; // 4 var d = c; d = 9; c; // 3}foo(1, 2, 3); 如下一个例子: 123456789function sidEffecting(ary) { ary[0] = ary[2];}function bar(a,b,c) { c = 10 sidEffecting(arguments); return a + b + c;}bar(1,1,1) 这里所有的更改都将生效,a和c的值都为10,a+b+c的值将为21。 7.类型相关测试为定义变量1typeof foo !== 'undefined' 上面代码会检测 foo 是否已经定义;如果没有定义而直接使用会导致 ReferenceError 的异常。 这是 typeof 唯一有用的地方。当然也能判断出来基本类型。 Object.prototype.toString检测一个对象的类型为了检测一个对象的类型,强烈推荐使用 Object.prototype.toString 方法 如下例子: 123Object.prototype.toString.call([]) // "[object Array]"Object.prototype.toString.call({}) // "[object Object]"Object.prototype.toString.call(2) // "[object Number]" 类型转换内置类型(比如 Number 和 String)的构造函数在被调用时,使用或者不使用 new 的结果完全不同。 123new Number(10) === 10; // False, 对象与数字的比较Number(10) === 10; // True, 数字与数字的比较new Number(10) + 0 === 10; // True, 由于隐式的类型转换 转换为字符串 1'' + 10 === '10'; // true 将一个值加上空字符串可以轻松转换为字符串类型。 转换为数字 1+'10' === 10; // true 使用一元的加号操作符,可以把字符串转换为数字。 转换为布尔型 通过使用 否 操作符两次,可以把一个值转换为布尔型。 1234567!!'foo'; // true!!''; // false!!'0'; // true!!'1'; // true!!'-1' // true!!{}; // true!!true; // true 8.为什么不要使用 evaleval 函数会在当前作用域中执行一段 JavaScript 代码字符串。 12345678var foo = 1;function test() { var foo = 2; eval('foo = 3'); return foo;}test(); // 3foo; // 1 但是 eval 只在被直接调用并且调用函数就是 eval 本身时,才在当前作用域中执行。 123456789var foo = 1;function test() { var foo = 2; var bar = eval; bar('foo = 3'); return foo;}test(); // 2foo; // 3 上面的代码等价于在全局作用域中调用 eval,和下面两种写法效果一样: 12345678910111213141516171819// 写法一:直接调用全局作用域下的 foo 变量var foo = 1;function test() { var foo = 2; window.foo = 3; return foo;}test(); // 2foo; // 3// 写法二:使用 call 函数修改 eval 执行的上下文为全局作用域var foo = 1;function test() { var foo = 2; eval.call(window, 'foo = 3'); return foo;}test(); // 2foo; // 3 在任何情况下我们都应该避免使用 eval 函数。99.9% 使用 eval 的场景都有不使用 eval 的解决方案。 eval 也存在安全问题,因为它会执行任意传给它的代码, 在代码字符串未知或者是来自一个不信任的源时,绝对不要使用 eval 函数。 9.定时器手工清空定时器12var id = setTimeout(foo, 1000);clearTimeout(id); 清除所有定时器由于没有内置的清除所有定时器的方法,可以采用一种暴力的方式来达到这一目的。 1234// 清空"所有"的定时器for(var i = 1; i < 1000; i++) { clearTimeout(i);} 可能还有些定时器不会在上面代码中被清除(注:如果定时器调用时返回的 ID 值大于 1000), 因此我们可以事先保存所有的定时器 ID,然后一把清除。 建议不要在调用定时器函数时,为了向回调函数传递参数而使用字符串的形式。 123456789function foo(a, b, c) {}// 不要这样做setTimeout('foo(1,2, 3)', 1000)// 可以使用匿名函数完成相同功能setTimeout(function() { foo(1, 2, 3);}, 1000) 绝对不要使用字符串作为 setTimeout 或者 setInterval 的第一个参数, 这么写的代码明显质量很差。当需要向回调函数传递参数时,可以创建一个匿名函数,在函数内执行真实的回调函数。 另外,应该避免使用 setInterval,因为它的定时执行不会被 JavaScript 阻塞。 后续逐渐添加 10,数组和字符串 split() join() 的区别 前者是切割成数组的形式,后者是将数组转换成字符串 数组方法pop() push() unshift() shift() Push( )尾部添加 pop ( )尾部删除 Unshift( )头部添加 shift( )头部删除 并返回删除项]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>js</tag>
</tags>
</entry>
<entry>
<title><![CDATA[JavaScript高级]]></title>
<url>%2F2017%2F05%2F11%2Fjavascript%E9%AB%98%E7%BA%A7%2F</url>
<content type="text"><![CDATA[课程介绍 课程大纲 目标 理解面向对象开发思想 掌握 JavaScript 面向对象开发相关模式 掌握在 JavaScript 中使用正则表达式 案例演示 贪吃蛇 基本概念复习 由于 JavaScript 高级还是针对 JavaScript 语言本身的一个进阶学习,所以在开始之前我们先对以前所学过的 JavaScript 相关知识点做一个快速复习总结。 重新介绍 JavaScriptJavaScript 是什么 解析执行:轻量级解释型的,或是 JIT 编译型的程序设计语言 语言特点:动态,头等函数 (First-class Function) 又称函数是 JavaScript 中的一等公民 执行环境:在宿主环境(host environment)下运行,浏览器是最常见的 JavaScript 宿主环境 但是在很多非浏览器环境中也使用 JavaScript ,例如 node.js 编程范式:基于原型、多范式的动态脚本语言,并且支持面向对象、命令式和声明式(如:函数式编程)编程风格 JavaScript 与浏览器的关系 JavaScript 的组成 组成部分 说明 ECMAScript 描述了该语言的语法和基本对象 DOM 描述了处理网页内容的方法和接口 BOM 描述了与浏览器进行交互的方法和接口 JavaScript 可以做什么 Any application that can be written in JavaScript, will eventually be written in JavaScript. 凡是能够用JavaScript实现的应用,最终都必将用JavaScript实现。 知乎 - JavaScript 能做什么,该做什么? 最流行的编程语言 JavaScript 能做什么? JavaScript 发展历史 JavaScript 标准参考教程 - JavaScript 语言的历史 JavaScript 的诞生 JavaScript 与 ECMAScript 的关系 JavaScript 与 Java 的关系 JavaScript 的版本 JavaScript 周边大事记 小结基本概念 本小节快速过即可,主要是对学过的内容做知识点梳理。 语法 区分大小写 标识符 标识符是指变量、函数、属性的名字,或者函数的参数 注释 严格模式 “use strict”; 语句 关键字和保留字 变量 数据类型 typeof 操作符 undefined null boolean number string object 运算符(操作符) 流程控制语句(if else else if switch for while do…while break continue) 函数 JavaScript 中的数据类型JavaScript 有 5 种简单数据类型:Undefined、Null、Boolean、Number、String 和 1 种复杂数据类型 object 。 基本类型(值类型) Undefined Null Boolean Number String 复杂类型(引用类型) Object Array Date RegExp Function 基本包装类型 Boolean Number String 单体内置对象 Global 1)所有在全局作用域内定义的属性和方法,都是Global对象的属性。 2)Global对象不能直接使用,也不能用new运算符创建。 3)Global对象在JavaScript引擎被初始化时创建,并初始化其方法和属性。 4)浏览器把Global对象作为window对象的一部分实现了,因此,所有的全局属性和函数都是window对象的属性和方法。 Math 类型检测 typeof instanceof Object.prototype.toString.call() 值类型和引用类型在内存中的存储方式(画图说明) 值类型按值存储 引用类型按引用存储 值类型复制和引用类型复制(画图说明) 值类型按值复制 引用类型按引用复制 值类型和引用类型参数传递(画图说明) 值类型按值传递 引用类型按引用传递 值类型与引用类型的差别 基本类型在内存中占据固定大小的空间,因此被保存在栈内存中 从一个变量向另一个变量复制基本类型的值,复制的是值的副本 引用类型的值是对象,保存在堆内存 包含引用类型值的变量实际上包含的并不是对象本身,而是一个指向该对象的指针 从一个变量向另一个变量复制引用类型的值的时候,复制是引用指针,因此两个变量最终都指向同一个对象 小结 类型检测方式 值类型和引用类型的存储方式 值类型复制和引用类型复制 方法参数中 值类型数据传递 和 引用类型数据传递 JavaScript 执行过程JavaScript 运行分为两个阶段: 预解析 全局预解析(所有变量和函数声明都会提前;同名的函数和变量函数的优先级高) 函数内部预解析(所有的变量、函数和形参都会参与预解析) 函数 形参 普通变量 执行 先预解析全局作用域,然后执行全局作用域中的代码,在执行全局代码的过程中遇到函数调用就会先进行函数预解析,然后再执行函数内代码。 JavaScript 面向对象编程 面向对象介绍什么是对象 Everything is object (万物皆对象) 对象到底是什么,我们可以从两次层次来理解。 (1) 对象是单个事物的抽象。 一本书、一辆汽车、一个人都可以是对象,一个数据库、一张网页、一个与远程服务器的连接也可以是对象。当实物被抽象成对象,实物之间的关系就变成了对象之间的关系,从而就可以模拟现实情况,针对对象进行编程。 (2) 对象是一个容器,封装了属性(property)和方法(method)。 属性是对象的状态,方法是对象的行为(完成某种任务)。比如,我们可以把动物抽象为animal对象,使用“属性”记录具体是那一种动物,使用“方法”表示动物的某种行为(奔跑、捕猎、休息等等)。 在实际开发中,对象是一个抽象的概念,可以将其简单理解为:数据集或功能集。 ECMAScript-262 把对象定义为:无序属性的集合,其属性可以包含基本值、对象或者函数。严格来讲,这就相当于说对象是一组没有特定顺序的值。对象的每个属性或方法都有一个名字,而每个名字都映射到一个值。 提示:每个对象都是基于一个引用类型创建的,这些类型可以是系统内置的原生类型,也可以是开发人员自定义的类型。 什么是面向对象 面向对象不是新的东西,它只是过程式代码的一种高度封装,目的在于提高代码的开发效率和可维护性。 面向对象编程 —— Object Oriented Programming,简称 OOP ,是一种编程开发思想。它将真实世界各种复杂的关系,抽象为一个个对象,然后由对象之间的分工与合作,完成对真实世界的模拟。 在面向对象程序开发思想中,每一个对象都是功能中心,具有明确分工,可以完成接受信息、处理数据、发出信息等任务。因此,面向对象编程具有灵活、代码可复用、高度模块化等特点,容易维护和开发,比起由一系列函数或指令组成的传统的过程式编程(procedural programming),更适合多人合作的大型软件项目。 面向对象与面向过程: 面向过程就是亲力亲为,事无巨细,面面俱到,步步紧跟,有条不紊 面向对象就是找一个对象,指挥得结果 面向对象将执行者转变成指挥者 面向对象不是面向过程的替代,而是面向过程的封装 面向对象的特性: 封装性 继承性 [多态性] 扩展阅读: 维基百科 - 面向对象程序设计 知乎:如何用一句话说明什么是面向对象思想? 知乎:什么是面向对象编程思想? 程序中面向对象的基本体现在 JavaScript 中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类( Class )的概念。 我们以一个例子来说明面向过程和面向对象在程序流程上的不同之处。 假设我们要处理学生的成绩表,为了表示一个学生的成绩,面向过程的程序可以用一个对象表示: 12var std1 = { name: 'Michael', score: 98 }var std2 = { name: 'Bob', score: 81 } 而处理学生成绩可以通过函数实现,比如打印学生的成绩: 123function printScore (student) { console.log('姓名:' + student.name + ' ' + '成绩:' + student.score)} 如果采用面向对象的程序设计思想,我们首选思考的不是程序的执行流程,而是 Student 这种数据类型应该被视为一个对象,这个对象拥有 name 和 score 这两个属性(Property)。如果要打印一个学生的成绩,首先必须创建出这个学生对应的对象,然后,给对象发一个 printScore 消息,让对象自己把自己的数据打印出来。 抽象数据行为模板(Class): 12345678function Student (name, score) { this.name = name this.score = score}Student.prototype.printScore = function () { console.log('姓名:' + this.name + ' ' + '成绩:' + this.score)} 根据模板创建具体实例对象(Instance): 12var std1 = new Student('Michael', 98)var std2 = new Student('Bob', 81) 实例对象具有自己的具体行为(给对象发消息): 12std1.printScore() // => 姓名:Michael 成绩:98std2.printScore() // => 姓名:Bob 成绩 81 面向对象的设计思想是从自然界中来的,因为在自然界中,类(Class)和实例(Instance)的概念是很自然的。Class 是一种抽象概念,比如我们定义的 Class——Student ,是指学生这个概念,而实例(Instance)则是一个个具体的 Student ,比如, Michael 和 Bob 是两个具体的 Student 。 所以,面向对象的设计思想是: 抽象出 Class 根据 Class 创建 Instance 指挥 Instance 得结果 面向对象的抽象程度又比函数要高,因为一个 Class 既包含数据,又包含操作数据的方法。 创建对象简单方式我们可以直接通过 new Object() 创建: 1234567var person = new Object()person.name = 'Jack'person.age = 18person.sayName = function () { console.log(this.name)} 每次创建通过 new Object() 比较麻烦,所以可以通过它的简写形式对象字面量来创建: 1234567var person = { name: 'Jack', age: 18, sayName: function () { console.log(this.name) }} 对于上面的写法固然没有问题,但是假如我们要生成两个 person 实例对象呢? 123456789101112131415var person1 = { name: 'Jack', age: 18, sayName: function () { console.log(this.name) }}var person2 = { name: 'Mike', age: 16, sayName: function () { console.log(this.name) }} 通过上面的代码我们不难看出,这样写的代码太过冗余,重复性太高。 简单方式的改进:工厂函数我们可以写一个函数,解决代码重复问题: 123456789function createPerson (name, age) { return { name: name, age: age, sayName: function () { console.log(this.name) } }} 然后生成实例对象: 12var p1 = createPerson('Jack', 18)var p2 = createPerson('Mike', 18) 这样封装确实爽多了,通过工厂模式我们解决了创建多个相似对象代码冗余的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。 构造函数内容引导: 构造函数语法 分析构造函数 构造函数和实例对象的关系 实例的 constructor 属性 instanceof 操作符 普通函数调用和构造函数调用的区别 构造函数的返回值 构造函数的静态成员和实例成员 函数也是对象 实例成员 静态成员 构造函数的问题 更优雅的工厂函数:构造函数一种更优雅的工厂函数就是下面这样,构造函数: 12345678910111213function Person (name, age) { this.name = name this.age = age this.sayName = function () { console.log(this.name) }}var p1 = new Person('Jack', 18)p1.sayName() // => Jackvar p2 = new Person('Mike', 23)p2.sayName() // => Mike 解析构造函数代码的执行在上面的示例中,Person() 函数取代了 createPerson() 函数,但是实现效果是一样的。这是为什么呢? 我们注意到,Person() 中的代码与 createPerson() 有以下几点不同之处: 没有显示的创建对象 直接将属性和方法赋给了 this 对象 没有 return 语句 函数名使用的是大写的 Person 而要创建 Person 实例,则必须使用 new 操作符。以这种方式调用构造函数会经历以下 4 个步骤: 创建一个新对象 看不到 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象) 看不到 执行构造函数中的代码 返回新对象 下面是具体的伪代码: 12345678910111213141516function Person (name, age) { // 当使用 new 操作符调用 Person() 的时候,实际上这里会先创建一个对象 // var instance = {} // 然后让内部的 this 指向 instance 对象 // this = instance // 接下来所有针对 this 的操作实际上操作的就是 instance this.name = name this.age = age this.sayName = function () { console.log(this.name) } // 在函数的结尾处会将 this 返回,也就是 instance // return this} 构造函数和实例对象的关系使用构造函数的好处不仅仅在于代码的简洁性,更重要的是我们可以识别对象的具体类型了。在每一个实例对象中同时有一个 constructor 属性,该属性指向创建该实例的构造函数: 123console.log(p1.constructor === Person) // => trueconsole.log(p2.constructor === Person) // => trueconsole.log(p1.constructor === p2.constructor) // => true 对象的 constructor 属性最初是用来标识对象类型的,但是,如果要检测对象的类型,还是使用 instanceof 操作符更可靠一些: 12console.log(p1 instanceof Person) // => trueconsole.log(p2 instanceof Person) // => true 总结: 构造函数是根据具体的事物抽象出来的抽象模板 实例对象是根据抽象的构造函数模板得到的具体实例对象 每一个实例对象都具有一个 constructor 属性,指向创建该实例的构造函数 注意: constructor 是实例的属性的说法不严谨,具体后面的原型会讲到 可以通过实例的 constructor 属性判断实例和构造函数之间的关系 注意:这种方式不严谨,推荐使用 instanceof 操作符,后面学原型会解释为什么 构造函数的问题使用构造函数带来的最大的好处就是创建对象更方便了,但是其本身也存在一个浪费内存的问题: 1234567891011function Person (name, age) { this.name = name this.age = age this.type = 'human' this.sayHello = function () { console.log('hello ' + this.name) }}var p1 = new Person('lpz', 18)var p2 = new Person('Jack', 16) 在该示例中,从表面上好像没什么问题,但是实际上这样做,有一个很大的弊端。那就是对于每一个实例对象,type 和 sayHello 都是一模一样的内容,每一次生成一个实例,都必须为重复的内容,多占用一些内存,如果实例对象很多,会造成极大的内存浪费。 1console.log(p1.sayHello === p2.sayHello) // => false 对于这种问题我们可以把需要共享的函数定义到构造函数外部: 123456789101112131415function sayHello = function () { console.log('hello ' + this.name)}function Person (name, age) { this.name = name this.age = age this.type = 'human' this.sayHello = sayHello}var p1 = new Person('lpz', 18)var p2 = new Person('Jack', 16)console.log(p1.sayHello === p2.sayHello) // => true 这样确实可以了,但是如果有多个需要共享的函数的话就会造成全局命名空间冲突的问题。 你肯定想到了可以把多个函数放到一个对象中用来避免全局命名空间冲突的问题: 12345678910111213141516171819202122var fns = { sayHello: function () { console.log('hello ' + this.name) }, sayAge: function () { console.log(this.age) }}function Person (name, age) { this.name = name this.age = age this.type = 'human' this.sayHello = fns.sayHello this.sayAge = fns.sayAge}var p1 = new Person('lpz', 18)var p2 = new Person('Jack', 16)console.log(p1.sayHello === p2.sayHello) // => trueconsole.log(p1.sayAge === p2.sayAge) // => true 至此,我们利用自己的方式基本上解决了构造函数的内存浪费问题。但是代码看起来还是那么的格格不入,那有没有更好的方式呢? 小结 构造函数语法 分析构造函数 构造函数和实例对象的关系 实例的 constructor 属性 instanceof 操作符 构造函数的问题 原型内容引导: 使用 prototype 原型对象解决构造函数的问题 分析 构造函数、prototype 原型对象、实例对象 三者之间的关系 属性成员搜索原则:原型链 实例对象读写原型对象中的成员 原型对象的简写形式 原生对象的原型 Object Array String … 原型对象的问题 构造的函数和原型对象使用建议 更好的解决方案: prototypeJavascript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。 这也就意味着,我们可以把所有对象实例需要共享的属性和方法直接定义在 prototype 对象上。 1234567891011121314151617function Person (name, age) { this.name = name this.age = age}console.log(Person.prototype)Person.prototype.type = 'human'Person.prototype.sayName = function () { console.log(this.name)}var p1 = new Person(...)var p2 = new Person(...)console.log(p1.sayName === p2.sayName) // => true 这时所有实例的 type 属性和 sayName() 方法,其实都是同一个内存地址,指向 prototype 对象,因此就提高了运行效率。 构造函数、实例、原型三者之间的关系 任何函数都具有一个 prototype 属性,该属性是一个对象。 123456function F () {}console.log(F.prototype) // => objectF.prototype.sayHi = function () { console.log('hi!')} 构造函数的 prototype 对象默认都有一个 constructor 属性,指向 prototype 对象所在函数。 1console.log(F.constructor === F) // => true 通过构造函数得到的实例对象内部会包含一个指向构造函数的 prototype 对象的指针 __proto__。 12var instance = new F()console.log(instance.__proto__ === F.prototype) // => true __proto__ 是非标准属性。(现在是标准的属性) 实例对象可以直接访问原型对象成员。 1instance.sayHi() // => hi! 总结: 任何函数都具有一个 prototype 属性,该属性是一个对象 构造函数的 prototype 对象默认都有一个 constructor 属性,指向 prototype 对象所在函数 通过构造函数得到的实例对象内部会包含一个指向构造函数的 prototype 对象的指针 __proto__ 所有实例都直接或间接继承了原型对象的成员 属性成员的搜索原则:原型链了解了 构造函数-实例-原型对象 三者之间的关系后,接下来我们来解释一下为什么实例对象可以访问原型对象中的成员。 每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性 搜索首先从对象实例本身开始 如果在实例中找到了具有给定名字的属性,则返回该属性的值 如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性 如果在原型对象中找到了这个属性,则返回该属性的值 也就是说,在我们调用 person1.sayName() 的时候,会先后执行两次搜索: 首先,解析器会问:“实例 person1 有 sayName 属性吗?”答:“没有。 ”然后,它继续搜索,再问:“ person1 的原型有 sayName 属性吗?”答:“有。 ”于是,它就读取那个保存在原型对象中的函数。 当我们调用 person2.sayName() 时,将会重现相同的搜索过程,得到相同的结果。 而这正是多个对象实例共享原型所保存的属性和方法的基本原理。 总结: 先在自己身上找,找到即返回 自己身上找不到,则沿着原型链向上查找,找到即返回 如果一直到原型链的末端还没有找到,则返回 undefined 实例对象读写原型对象成员读取: 先在自己身上找,找到即返回 自己身上找不到,则沿着原型链向上查找,找到即返回 如果一直到原型链的末端还没有找到,则返回 undefined 值类型成员写入(实例对象.值类型成员 = xx): 当实例期望重写原型对象中的某个普通数据成员时实际上会把该成员添加到自己身上 也就是说该行为实际上会屏蔽掉对原型对象成员的访问 引用类型成员写入(实例对象.引用类型成员 = xx): 同上 复杂类型修改(实例对象.成员.xx = xx): 同样会先在自己身上找该成员,如果自己身上找到则直接修改 如果自己身上找不到,则沿着原型链继续查找,如果找到则修改 如果一直到原型链的末端还没有找到该成员,则报错(实例对象.undefined.xx = xx) 更简单的原型语法我们注意到,前面例子中每添加一个属性和方法就要敲一遍 Person.prototype 。为减少不必要的输入,更常见的做法是用一个包含所有属性和方法的对象字面量来重写整个原型对象: 1234567891011function Person (name, age) { this.name = name this.age = age}Person.prototype = { type: 'human', sayHello: function () { console.log('我叫' + this.name + ',我今年' + this.age + '岁了') }} 在该示例中,我们将 Person.prototype 重置到了一个新的对象。这样做的好处就是为 Person.prototype 添加成员简单了,但是也会带来一个问题,那就是原型对象丢失了 constructor 成员。 所以,我们为了保持 constructor 的指向正确,建议的写法是: 123456789101112function Person (name, age) { this.name = name this.age = age}Person.prototype = { constructor: Person, // => 手动将 constructor 指向正确的构造函数 type: 'human', sayHello: function () { console.log('我叫' + this.name + ',我今年' + this.age + '岁了') }} 原生对象的原型 所有函数都有 prototype 属性对象。 Object.prototype Function.prototype Array.prototype String.prototype Number.prototype Date.prototype … 练习:为数组对象和字符串对象扩展原型方法。 原型对象的问题 共享数组 共享对象 如果真的希望可以被实例对象之间共享和修改这些共享数据那就不是问题。但是如果不希望实例之间共享和修改这些共享数据则就是问题。 一个更好的建议是,最好不要让实例之间互相共享这些数组或者对象成员,一旦修改的话会导致数据的走向很不明确而且难以维护。 原型对象使用建议 私有成员(一般就是非函数成员)放到构造函数中 共享成员(一般就是函数)放到原型对象中 如果重置了 prototype 记得修正 constructor 的指向 案例:随机方块 面向对象游戏案例:贪吃蛇案例相关源码以上传到 GitHub :https://github.com/lipengzhou/new-snake 案例介绍游戏演示在线演示地址:贪吃蛇 案例目标游戏的目的是用来体会js高级语法的使用 不需要具备抽象对象的能力,使用面向对象的方式分析问题,需要一个漫长的过程。 功能实现搭建页面放一个容器盛放游戏场景 div#map,设置样式 123456#map { width: 800px; height: 600px; background-color: #ccc; position: relative;} 分析对象 游戏对象 蛇对象 食物对象 创建食物对象 Food 属性 x y width height color 方法 render 随机创建一个食物对象,并输出到map上 创建Food的构造函数,并设置属性 1234567891011var position = 'absolute';var elements = [];function Food(x, y, width, height, color) { this.x = x || 0; this.y = y || 0; // 食物的宽度和高度(像素) this.width = width || 20; this.height = height || 20; // 食物的颜色 this.color = color || 'green';} 通过原型设置render方法,实现随机产生食物对象,并渲染到map上 12345678910111213141516Food.prototype.render = function (map) { // 随机食物的位置,map.宽度/food.宽度,总共有多少分food的宽度,随机一下。然后再乘以food的宽度 this.x = parseInt(Math.random() * map.offsetWidth / this.width) * this.width; this.y = parseInt(Math.random() * map.offsetHeight / this.height) * this.height; // 动态创建食物对应的div var div = document.createElement('div'); map.appendChild(div); div.style.position = position; div.style.left = this.x + 'px'; div.style.top = this.y + 'px'; div.style.width = this.width + 'px'; div.style.height = this.height + 'px'; div.style.backgroundColor = this.color; elements.push(div);} 通过自调用函数,进行封装,通过window暴露Food对象 1window.Food = Food; 创建蛇对象 Snake 属性 width 蛇节的宽度 默认20 height 蛇节的高度 默认20 body 数组,蛇的头部和身体,第一个位置是蛇头 direction 蛇运动的方向 默认right 可以是 left top bottom 方法 render 把蛇渲染到map上 Snake构造函数 1234567891011121314var position = 'absolute';var elements = [];function Snake(width, height, direction) { // 设置每一个蛇节的宽度 this.width = width || 20; this.height = height || 20; // 蛇的每一部分, 第一部分是蛇头 this.body = [ {x: 3, y: 2, color: 'red'}, {x: 2, y: 2, color: 'red'}, {x: 1, y: 2, color: 'red'} ]; this.direction = direction || 'right';} render方法 12345678910111213Snake.prototype.render = function(map) { for(var i = 0; i < this.body.length; i++) { var obj = this.body[i]; var div = document.createElement('div'); map.appendChild(div); div.style.left = obj.x * this.width + 'px'; div.style.top = obj.y * this.height + 'px'; div.style.position = position; div.style.backgroundColor = obj.color; div.style.width = this.width + 'px'; div.style.height = this.height + 'px'; }} 在自调用函数中暴露Snake对象 1window.Snake = Snake; 创建游戏对象游戏对象,用来管理游戏中的所有对象和开始游戏 Game 属性 food snake map 方法 start 开始游戏(绘制所有游戏对象) 构造函数 12345function Game(map) { this.food = new Food(); this.snake = new Snake(); this.map = map;} 开始游戏,渲染食物对象和蛇对象 1234Game.prototype.start = function () { this.food.render(this.map); this.snake.render(this.map);} 游戏的逻辑写蛇的move方法 在蛇对象(snake.js)中,在Snake的原型上新增move方法 让蛇移动起来,把蛇身体的每一部分往前移动一下 蛇头部分根据不同的方向决定 往哪里移动 1234567891011121314151617181920212223Snake.prototype.move = function (food, map) { // 让蛇身体的每一部分往前移动一下 var i = this.body.length - 1; for(; i > 0; i--) { this.body[i].x = this.body[i - 1].x; this.body[i].y = this.body[i - 1].y; } // 根据移动的方向,决定蛇头如何处理 switch(this.direction) { case 'left': this.body[0].x -= 1; break; case 'right': this.body[0].x += 1; break; case 'top': this.body[0].y -= 1; break; case 'bottom': this.body[0].y += 1; break; }} 在game中测试 12this.snake.move(this.food, this.map);this.snake.render(this.map); 让蛇自己动起来 私有方法 1234什么是私有方法? 不能被外部访问的方法如何创建私有方法? 使用自调用函数包裹 在game.js中 添加runSnake的私有方法,开启定时器调用蛇的move和render方法,让蛇动起来 判断蛇是否撞墙 1234567891011121314151617181920212223function runSnake() { var timerId = setInterval(function() { this.snake.move(this.food, this.map); // 在渲染前,删除之前的蛇 this.snake.render(this.map); // 判断蛇是否撞墙 var maxX = this.map.offsetWidth / this.snake.width; var maxY = this.map.offsetHeight / this.snake.height; var headX = this.snake.body[0].x; var headY = this.snake.body[0].y; if (headX < 0 || headX >= maxX) { clearInterval(timerId); alert('Game Over'); } if (headY < 0 || headY >= maxY) { clearInterval(timerId); alert('Game Over'); } }.bind(that), 150);} 在snake中添加删除蛇的私有方法,在render中调用 12345678910function remove() { // 删除渲染的蛇 var i = elements.length - 1; for(; i >= 0; i--) { // 删除页面上渲染的蛇 elements[i].parentNode.removeChild(elements[i]); // 删除elements数组中的元素 elements.splice(i, 1); }} 在game中通过键盘控制蛇的移动方向 12345678910111213141516171819202122function bindKey() { document.addEventListener('keydown', function(e) { switch (e.keyCode) { case 37: // left this.snake.direction = 'left'; break; case 38: // top this.snake.direction = 'top'; break; case 39: // right this.snake.direction = 'right'; break; case 40: // bottom this.snake.direction = 'bottom'; break; } }.bind(that), false);} 在start方法中调用 1bindKey(); 判断蛇是否吃到食物123456789101112131415161718// 在Snake的move方法中// 在移动的过程中判断蛇是否吃到食物// 如果蛇头和食物的位置重合代表吃到食物// 食物的坐标是像素,蛇的坐标是几个宽度,进行转换var headX = this.body[0].x * this.width;var headY = this.body[0].y * this.height;if (headX === food.x && headY === food.y) { // 吃到食物,往蛇节的最后加一节 var last = this.body[this.body.length - 1]; this.body.push({ x: last.x, y: last.y, color: last.color }) // 把现在的食物对象删除,并重新随机渲染一个食物对象 food.render(map);} 其它处理把html中的js代码放到index.js中避免html中出现js代码 自调用函数的参数1234(function (window, undefined) { var document = window.document;}(window, undefined)) 传入window对象 将来代码压缩的时候,可以吧 function (window) 压缩成 function (w) 传入undefined 在将来会看到别人写的代码中会把undefined作为函数的参数(当前案例没有使用)因为在有的老版本的浏览器中 undefined可以被重新赋值,防止undefined 被重新赋值 整理代码现在的代码结构清晰,谁出问题就找到对应的js文件即可。通过自调用函数,已经防止了变量命名污染的问题 但是,由于js文件数较多,需要在页面上引用,会产生文件依赖的问题(先引入那个js,再引入哪个js)将来通过工具把js文件合并并压缩。现在手工合并js文件演示 问题1 1234567891011121314// 如果存在多个自调用函数要用分号分割,否则语法错误// 下面代码会报错(function () {}())(function () {}())// 所以代码规范中会建议在自调用函数之前加上分号// 下面代码没有问题;(function () {}());(function () {}()) 问题2 123456789// 当自调用函数 前面有函数声明时,会把自调用函数作为参数// 所以建议自调用函数前,加上;var a = function () { alert('11');} (function () { alert('22');}()) 继承什么是继承 现实生活中的继承 程序中的继承 构造函数的属性继承:借用构造函数12345678910111213function Person (name, age) { this.type = 'human' this.name = name this.age = age}function Student (name, age) { // 借用构造函数继承属性成员 Person.call(this, name, age)}var s1 = Student('张三', 18)console.log(s1.type, s1.name, s1.age) // => human 张三 18 构造函数的原型方法继承:拷贝继承(for-in)12345678910111213141516171819202122function Person (name, age) { this.type = 'human' this.name = name this.age = age}Person.prototype.sayName = function () { console.log('hello ' + this.name)}function Student (name, age) { Person.call(this, name, age)}// 原型对象拷贝继承原型对象成员for(var key in Person.prototype) { Student.prototype[key] = Person.prototype[key]}var s1 = Student('张三', 18)s1.sayName() // => hello 张三 另一种继承方式:原型继承12345678910111213141516171819202122function Person (name, age) { this.type = 'human' this.name = name this.age = age}Person.prototype.sayName = function () { console.log('hello ' + this.name)}function Student (name, age) { Person.call(this, name, age)}// 利用原型的特性实现继承Student.prototype = new Person()var s1 = Student('张三', 18)console.log(s1.type) // => humans1.sayName() // => hello 张三 函数进阶函数的定义方式 函数声明 函数表达式 new Function 函数声明123function foo () {} 函数表达式123var foo = function () {} 函数声明与函数表达式的区别 函数声明必须有名字 函数声明会函数提升,在预解析阶段就已创建,声明前后都可以调用 函数表达式类似于变量赋值 函数表达式可以没有名字,例如匿名函数 函数表达式没有变量提升,在执行阶段创建,必须在表达式执行之后才可以调用 下面是一个根据条件定义函数的例子: 123456789if (true) { function f () { console.log(1) }} else { function f () { console.log(2) }} 以上代码执行结果在不同浏览器中结果不一致。 不过我们可以使用函数表达式解决上面的问题: 1234567891011var fif (true) { f = function () { console.log(1) }} else { f = function () { console.log(2) }} 函数的调用方式 普通函数 构造函数 对象方法 函数内 this 指向的不同场景函数的调用方式决定了 this 指向的不同: 调用方式 非严格模式 备注 普通函数调用 window 严格模式下是 undefined 构造函数调用 实例对象 原型方法中 this 也是实例对象 对象方法调用 该方法所属对象 紧挨着的对象 事件绑定方法 绑定事件对象 定时器函数 window 这就是对函数内部 this 指向的基本整理,写代码写多了自然而然就熟悉了。 函数也是对象 所有函数都是 Function 的实例 call、apply、bind那了解了函数 this 指向的不同场景之后,我们知道有些情况下我们为了使用某种特定环境的 this 引用,这时候时候我们就需要采用一些特殊手段来处理了,例如我们经常在定时器外部备份 this 引用,然后在定时器函数内部使用外部 this 的引用。然而实际上对于这种做法我们的 JavaScript 为我们专门提供了一些函数方法用来帮我们更优雅的处理函数内部 this 指向问题。这就是接下来我们要学习的 call、apply、bind 三个函数方法。 callcall() 方法调用一个函数, 其具有一个指定的 this 值和分别地提供的参数(参数的列表)。 注意:该方法的作用和 apply() 方法类似,只有一个区别,就是 call() 方法接受的是若干个参数的列表,而 apply() 方法接受的是一个包含多个参数的数组。 语法: 1fun.call(thisArg[, arg1[, arg2[, ...]]]) 参数: thisArg 在 fun 函数运行时指定的 this 值 如果指定了 null 或者 undefined 则内部 this 指向 window arg1, arg2, ... 指定的参数列表 applyapply() 方法调用一个函数, 其具有一个指定的 this 值,以及作为一个数组(或类似数组的对象)提供的参数。 注意:该方法的作用和 call() 方法类似,只有一个区别,就是 call() 方法接受的是若干个参数的列表,而 apply() 方法接受的是一个包含多个参数的数组。 语法: 1fun.apply(thisArg, [argsArray]) 参数: thisArg argsArray apply() 与 call() 非常相似,不同之处在于提供参数的方式。apply() 使用参数数组而不是一组参数列表。例如: 1fun.apply(this, ['eat', 'bananas']) bindbind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5 规范中内置的call属性)。当目标函数被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。绑定函数被调用时,bind() 也接受预设的参数提供给原函数。一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。 语法: 1fun.bind(thisArg[, arg1[, arg2[, ...]]]) 参数: thisArg 当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用new 操作符调用绑定函数时,该参数无效。 arg1, arg2, … 当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。 返回值: 返回由指定的this值和初始化参数改造的原函数拷贝。 示例1: 123456789101112131415this.x = 9; var module = { x: 81, getX: function() { return this.x; }};module.getX(); // 返回 81var retrieveX = module.getX;retrieveX(); // 返回 9, 在这种情况下,"this"指向全局作用域// 创建一个新函数,将"this"绑定到module对象// 新手可能会被全局的x变量和module里的属性x所迷惑var boundGetX = retrieveX.bind(module);boundGetX(); // 返回 81 示例2: 12345678910111213141516function LateBloomer() { this.petalCount = Math.ceil(Math.random() * 12) + 1;}// Declare bloom after a delay of 1 secondLateBloomer.prototype.bloom = function() { window.setTimeout(this.declare.bind(this), 1000);};LateBloomer.prototype.declare = function() { console.log('I am a beautiful flower with ' + this.petalCount + ' petals!');};var flower = new LateBloomer();flower.bloom(); // 一秒钟后, 调用'declare'方法 小结 call 和 apply 特性一样 都是用来调用函数,而且是立即调用 但是可以在调用函数的同时,通过第一个参数指定函数内部 this 的指向 call 调用的时候,参数必须以参数列表的形式进行传递,也就是以逗号分隔的方式依次传递即可 apply 调用的时候,参数必须是一个数组,然后在执行的时候,会将数组内部的元素一个一个拿出来,与形参一一对应进行传递 如果第一个参数指定了 null 或者 undefined 则内部 this 指向 window bind 可以用来指定内部 this 的指向,然后生成一个改变了 this 指向的新的函数 它和 call、apply 最大的区别是:bind 不会调用 bind 支持传递参数,它的传参方式比较特殊,一共有两个位置可以传递 在 bind 的同时,以参数列表的形式进行传递 在调用的时候,以参数列表的形式进行传递 那到底以谁 bind 的时候传递的参数为准呢还是以调用的时候传递的参数为准 两者合并:bind 的时候传递的参数和调用的时候传递的参数会合并到一起,传递到函数内部 函数的其它成员 arguments 实参集合 caller 函数的调用者 length 形参的个数 name 函数的名称 12345678910111213function fn(x, y, z) { console.log(fn.length) // => 形参的个数 console.log(arguments) // 伪数组实参参数集合 console.log(arguments.callee === fn) // 函数本身 console.log(fn.caller) // 函数的调用者 console.log(fn.name) // => 函数的名字}function f() { fn(10, 20, 30)}f() 高阶函数 函数可以作为参数 函数可以作为返回值 作为参数12345678910function eat (callback) { setTimeout(function () { console.log('吃完了') callback() }, 1000)}eat(function () { console.log('去唱歌')}) 作为返回值1234567891011function genFun (type) { return function (obj) { return Object.prototype.toString.call(obj) === type }}var isArray = genFun('[object Array]')var isObject = genFun('[object Object]')console.log(isArray([])) // => trueconsole.log(isArray({})) // => true 函数闭包1234567891011121314151617function fn () { var count = 0 return { getCount: function () { console.log(count) }, setCount: function () { count++ } }}var fns = fn()fns.getCount() // => 0fns.setCount()fns.getCount() // => 1 作用域、作用域链、预解析 全局作用域 函数作用域 没有块级作用域 12345678910{ var foo = 'bar'}console.log(foo)if (true) { var a = 123}console.log(a) 作用域链示例代码: 123456789101112131415161718var a = 10function fn () { var b = 20 function fn1 () { var c = 30 console.log(a + b + c) } function fn2 () { var d = 40 console.log(c + d) } fn1() fn2()} 内层作用域可以访问外层作用域,反之不行 什么是闭包闭包就是能够读取其他函数内部变量的函数,由于在 Javascript 语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成 “定义在一个函数内部的函数”。所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。 闭包的用途: 可以在函数外部读取函数内部成员 让函数内成员始终存活在内存中 一些关于闭包的例子示例1: 123456var arr = [10, 20, 30]for(var i = 0; i < arr.length; i++) { arr[i] = function () { console.log(i) }} 示例2: 12345678console.log(111)for(var i = 0; i < 3; i++) { setTimeout(function () { console.log(i) }, 0)}console.log(222) 示例3:投票 示例4:判断类型 示例5:沙箱模式 闭包的思考题思考题 1: 1234567891011var name = "The Window";var object = { name: "My Object", getNameFunc: function () { return function () { return this.name; }; }};console.log(object.getNameFunc()()) 思考题 2: 1234567891011var name = "The Window"; var object = { name: "My Object", getNameFunc: function () { var that = this; return function () { return that.name; }; }};console.log(object.getNameFunc()()) 小结函数递归递归执行模型123456789101112131415161718192021222324function fn1 () { console.log(111) fn2() console.log('fn1')}function fn2 () { console.log(222) fn3() console.log('fn2')}function fn3 () { console.log(333) fn4() console.log('fn3')}function fn4 () { console.log(444) console.log('fn4')}fn1() 举个栗子:计算阶乘的递归函数1234567function factorial (num) { if (num <= 1) { return 1 } else { return num * factorial(num - 1) }} 递归应用场景 深拷贝 菜单树 遍历 DOM 树 正则表达式 了解正则表达式基本语法 能够使用JavaScript的正则对象 正则表达式简介什么是正则表达式正则表达式:用于匹配规律规则的表达式,正则表达式最初是科学家对人类神经系统的工作原理的早期研究,现在在编程语言中有广泛的应用。正则表通常被用来检索、替换那些符合某个模式(规则)的文本。正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。 正则表达式的作用 给定的字符串是否符合正则表达式的过滤逻辑(匹配) 可以通过正则表达式,从字符串中获取我们想要的特定部分(提取) 强大的字符串替换能力(替换) 正则表达式的特点 灵活性、逻辑性和功能性非常的强 可以迅速地用极简单的方式达到字符串的复杂控制 对于刚接触的人来说,比较晦涩难懂 正则表达式的测试 在线测试正则 工具中使用正则表达式 sublime/vscode/word 演示替换所有的数字 正则表达式的组成 普通字符 特殊字符(元字符):正则表达式中有特殊意义的字符 示例演示: \d 匹配数字 ab\d 匹配 ab1、ab2 元字符串通过测试工具演示下面元字符的使用 常用元字符串 元字符 说明 \d 匹配数字 \D 匹配任意非数字的字符 \w 匹配字母或数字或下划线 \W 匹配任意不是字母,数字,下划线 \s 匹配任意的空白符 \S 匹配任意不是空白符的字符 . 匹配除换行符以外的任意单个字符 ^ 表示匹配行首的文本(以谁开始) $ 表示匹配行尾的文本(以谁结束) 限定符 限定符 说明 * 重复零次或更多次 + 重复一次或更多次 ? 重复零次或一次 {n} 重复n次 {n,} 重复n次或更多次 {n,m} 重复n到m次 其它1234567[] 字符串用中括号括起来,表示匹配其中的任一字符,相当于或的意思[^] 匹配除中括号以内的内容\ 转义符| 或者,选择两者中的一个。注意|将左右两边分为两部分,而不管左右两边有多长多乱() 从两个直接量中选择一个,分组 eg:gr(a|e)y匹配gray和grey[\u4e00-\u9fa5] 匹配汉字 案例验证手机号: 1^\d{11}$ 验证邮编: 1^\d{6}$ 验证日期 2012-5-01 1^\d{4}-\d{1,2}-\d{1,2}$ 验证邮箱 xxx@itcast.cn: 1^\w+@\w+\.\w+$ 验证IP地址 192.168.1.10 1^\d{1,3}\(.\d{1,3}){3}$ JavaScript 中使用正则表达式创建正则对象方式1: 12var reg = new Regex('\d', 'i');var reg = new Regex('\d', 'gi'); 方式2: 12var reg = /\d/i;var reg = /\d/gi; 参数 标志 说明 i 忽略大小写 g 全局匹配 gi 全局匹配+忽略大小写 正则匹配1234// 匹配日期var dateStr = '2015-10-10';var reg = /^\d{4}-\d{1,2}-\d{1,2}$/console.log(reg.test(dateStr)); 正则提取123456789101112131415161718192021222324252627// 1. 提取工资var str = "张三:1000,李四:5000,王五:8000。";var array = str.match(/\d+/g);console.log(array);// 2. 提取email地址var str = "123123@xx.com,fangfang@valuedopinions.cn 286669312@qq.com 2、emailenglish@emailenglish.englishtown.com 286669312@qq.com...";var array = str.match(/\w+@\w+\.\w+(\.\w+)?/g);console.log(array);// 3. 分组提取 // 3. 提取日期中的年部分 2015-5-10var dateStr = '2016-1-5';// 正则表达式中的()作为分组来使用,获取分组匹配到的结果用Regex.$1 $2 $3....来获取var reg = /(\d{4})-\d{1,2}-\d{1,2}/;if (reg.test(dateStr)) { console.log(RegExp.$1);}// 4. 提取邮件中的每一部分var reg = /(\w+)@(\w+)\.(\w+)(\.\w+)?/;var str = "123123@xx.com";if (reg.test(str)) { console.log(RegExp.$1); console.log(RegExp.$2); console.log(RegExp.$3);} 正则替换123456789// 1. 替换所有空白var str = " 123AD asadf asadfasf adf ";str = str.replace(/\s/g,"xx");console.log(str);// 2. 替换所有,|,var str = "abc,efg,123,abc,123,a";str = str.replace(/,|,/g, ".");console.log(str); 案例:表单验证12345QQ号:<input type="text" id="txtQQ"><span></span><br>邮箱:<input type="text" id="txtEMail"><span></span><br>手机:<input type="text" id="txtPhone"><span></span><br>生日:<input type="text" id="txtBirthday"><span></span><br>姓名:<input type="text" id="txtName"><span></span><br> 12345678910111213141516171819202122232425262728293031323334353637383940//获取文本框var txtQQ = document.getElementById("txtQQ");var txtEMail = document.getElementById("txtEMail");var txtPhone = document.getElementById("txtPhone");var txtBirthday = document.getElementById("txtBirthday");var txtName = document.getElementById("txtName");//txtQQ.onblur = function () { //获取当前文本框对应的span var span = this.nextElementSibling; var reg = /^\d{5,12}$/; //判断验证是否成功 if(!reg.test(this.value) ){ //验证不成功 span.innerText = "请输入正确的QQ号"; span.style.color = "red"; }else{ //验证成功 span.innerText = ""; span.style.color = ""; }};//txtEMailtxtEMail.onblur = function () { //获取当前文本框对应的span var span = this.nextElementSibling; var reg = /^\w+@\w+\.\w+(\.\w+)?$/; //判断验证是否成功 if(!reg.test(this.value) ){ //验证不成功 span.innerText = "请输入正确的EMail地址"; span.style.color = "red"; }else{ //验证成功 span.innerText = ""; span.style.color = ""; }}; 表单验证部分,封装成函数: 12345678910111213141516171819var regBirthday = /^\d{4}-\d{1,2}-\d{1,2}$/;addCheck(txtBirthday, regBirthday, "请输入正确的出生日期");//给文本框添加验证function addCheck(element, reg, tip) { element.onblur = function () { //获取当前文本框对应的span var span = this.nextElementSibling; //判断验证是否成功 if(!reg.test(this.value) ){ //验证不成功 span.innerText = tip; span.style.color = "red"; }else{ //验证成功 span.innerText = ""; span.style.color = ""; } };} 通过给元素增加自定义验证属性对表单进行验证: 1234567<form id="frm"> QQ号:<input type="text" name="txtQQ" data-rule="qq"><span></span><br> 邮箱:<input type="text" name="txtEMail" data-rule="email"><span></span><br> 手机:<input type="text" name="txtPhone" data-rule="phone"><span></span><br> 生日:<input type="text" name="txtBirthday" data-rule="date"><span></span><br> 姓名:<input type="text" name="txtName" data-rule="cn"><span></span><br></form> 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475// 所有的验证规则var rules = [ { name: 'qq', reg: /^\d{5,12}$/, tip: "请输入正确的QQ" }, { name: 'email', reg: /^\w+@\w+\.\w+(\.\w+)?$/, tip: "请输入正确的邮箱地址" }, { name: 'phone', reg: /^\d{11}$/, tip: "请输入正确的手机号码" }, { name: 'date', reg: /^\d{4}-\d{1,2}-\d{1,2}$/, tip: "请输入正确的出生日期" }, { name: 'cn', reg: /^[\u4e00-\u9fa5]{2,4}$/, tip: "请输入正确的姓名" }];addCheck('frm');//给文本框添加验证function addCheck(formId) { var i = 0, len = 0, frm =document.getElementById(formId); len = frm.children.length; for (; i < len; i++) { var element = frm.children[i]; // 表单元素中有name属性的元素添加验证 if (element.name) { element.onblur = function () { // 使用dataset获取data-自定义属性的值 var ruleName = this.dataset.rule; var rule =getRuleByRuleName(rules, ruleName); var span = this.nextElementSibling; //判断验证是否成功 if(!rule.reg.test(this.value) ){ //验证不成功 span.innerText = rule.tip; span.style.color = "red"; }else{ //验证成功 span.innerText = ""; span.style.color = ""; } } } }}// 根据规则的名称获取规则对象function getRuleByRuleName(rules, ruleName) { var i = 0, len = rules.length; var rule = null; for (; i < len; i++) { if (rules[i].name == ruleName) { rule = rules[i]; break; } } return rule;} 补充伪数组和数组在JavaScript中,除了5种原始数据类型之外,其他所有的都是对象,包括函数(Function)。 对象与数组的关系在说区别之前,需要先提到另外一个知识,就是 JavaScript 的原型继承。所有 JavaScript 的内置构造函数都是继承自 Object.prototype 。在这个前提下,可以理解为使用 new Array() 或 [] 创建出来的数组对象,都会拥有 Object.prototype 的属性值。 1234var obj = {};// 拥有 Object.prototype 的属性值var arr = [];//使用数组直接量创建的数组,由于 Array.prototype 的属性继承自 Object.prototype,//那么,它将同时拥有 Array.prototype 和 Object.prototype 的属性值 可以得到对象和数组的第一个区别:对象没有数组 Array.prototype 的属性值。 什么是数组数组具有一个最基本特征:索引,这是对象所没有的,下面来看一段代码: 12345678910var obj = {};var arr = []; obj[2] = 'a';arr[2] = 'a'; console.log(obj[2]); // => aconsole.log(arr[2]); // => aconsole.log(obj.length); // => undefinedconsole.log(arr.length); // => 3 obj[2]输出’a’,是因为对象就是普通的键值对存取数据 而arr[2]输出’a’ 则不同,数组是通过索引来存取数据,arr[2]之所以输出’a’,是因为数组arr索引2的位置已经存储了数据 obj.length并不具有数组的特性,并且obj没有保存属性length,那么自然就会输出undefined 而对于数组来说,length是数组的一个内置属性,数组会根据索引长度来更改length的值 为什么arr.length输出3,而不是1 在给数组添加元素时,并没有按照连续的索引添加,所以导致数组的索引不连续,那么就导致索引长度大于元素个数 什么是伪数组 拥有 length 属性,其它属性(索引)为非负整数(对象中的索引会被当做字符串来处理,这里你可以当做是个非负整数串来理解) 不具有数组所具有的方法 伪数组,就是像数组一样有 length 属性,也有 0、1、2、3 等属性的对象,看起来就像数组一样,但不是数组,比如: 123456789101112var fakeArray = { "0": "first", "1": "second", "2": "third", length: 3}; for (var i = 0; i < fakeArray.length; i++) { console.log(fakeArray[i]);} Array.prototype.join.call(fakeArray,'+'); 常见的伪数组有: 函数内部的 arguments DOM 对象列表(比如通过 document.getElementsByTags 得到的列表) jQuery 对象(比如 $("div") ) 伪数组是一个 Object,而真实的数组是一个 Array。 伪数组存在的意义,是可以让普通的对象也能正常使用数组的很多方法,比如: 123456789101112var arr = Array.prototype.slice.call(arguments); Array.prototype.forEach.call(arguments, function(v) { // 循环arguments对象});// push// some// every// filter// map// ... 以上在借用数组的原型方法的时候都可以通过数组直接量来简化使用: 1234567891011121314var obj = { 0: 'a', 1: 'b', 2: 'c', length: 3};[].push.call(obj, 'd')console.log([].slice.call(obj));[].forEach.call(obj, function (num, index) { console.log(num)}) 小结 对象没有数组 Array.prototype 的属性值,类型是 Object ,而数组类型是 Array 数组是基于索引的实现, length 会自动更新,而对象是键值对 使用对象可以创建伪数组,伪数组可以正常使用数组的大部分方法 JavaScript 垃圾回收机制JavaScript 运行机制:Event LoopObject静态成员 Object.assign() Object.create() Object.keys() Object.defineProperty() 实例成员 constructor hasOwnProperty() isPrototypeOf propertyIsEnumerable() toString() valueOf() 附录A 代码规范代码风格 JavaScript Standard Style Airbnb JavaScript Style Guide() { 校验工具 JSLint JSHint ESLint B Chrome 开发者工具C 文档相关工具 电子文档制作工具: docute 流程图工具:DiagramDesigner]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>js</tag>
</tags>
</entry>
<entry>
<title><![CDATA[CSS之清除浮动]]></title>
<url>%2F2017%2F04%2F22%2FCSS%E4%B9%8B%E6%B8%85%E9%99%A4%E6%B5%AE%E5%8A%A8%2F</url>
<content type="text"><![CDATA[1.什么是浮动首先我们需要知道定位元素在页面中的位置就是定位,解决问题之前我们先来了解下几种定位方式1、普通流定位 static(默认方式)普通流定位,又称为文档流定位,是页面元素的默认定位方式页面中的块级元素:按照从上到下的方式逐个排列页面中的行内元素:按照从左到右的方式逐个排列 但是如何让多个块级元素在一行内显示?这里就引出了浮动定位2、浮动定位 floatfloat属性 取值为 left/right这个属性原本不是用来布局的,而是用来做文字环绕的,但是后来人们发现做布局也不错,就一直这么用了,甚至有些时候都忘了用他做文字环绕3、相对定位 relative元素会相对于它原来的位置偏移某个距离,改变元素位置后,元素原本的空间依然会被保留语法属性:position取值:relative配合着 偏移属性(top/right/bottom/left)实现位置的改变 4、绝对定位 absolute如果元素被设置为绝对定位的话,将具备以下几个特征1、脱离文档流-不占据页面空间2、通过偏移属性固定元素位置3、相对于 最近的已定位的祖先元素实现位置固定4、如果没有已定位祖先元素,那么就相对于最初的包含块(body,html)去实现位置的固定语法属性:position取值:absolute配合着 偏移属性(top/right/bottom/left)实现位置的固定 5、固定定位 fixed将元素固定在页面的某个位置处,不会随着滚动条而发生位置移动语法属性:position取值:fixed配合着 偏移属性(top/right/bottom/left)实现位置的固定 2.浮动的效果 浮动 之后会怎么样?1、浮动定位元素会被排除在文档流之外-脱离文档流(不占据页面空间),其余的元素要上前补位2、浮动元素会停靠在父元素的左边或右边,或停靠在其他已浮动元素的边缘上(元素只能在当前所在行浮动)3、浮动元素依然位于父元素之内4、浮动元素处理的问题-解决多个块级元素在一行内显示的问题注意1、一行内,显示不下所有的已浮动元素时,最后一个将换行2、元素一旦浮动起来之后,那么宽度将变成自适应(宽度由内容决定)3、元素一旦浮动起来之后,那么就将变成块级元素,尤其对行内元素,影响最大块级元素:允许修改尺寸行内元素:不允许修改尺寸4、文本,行内元素,行内块元素时采用环绕的方式来排列的,是不会被浮动元素压在底下的,会巧妙的避开浮动元素 浮动 之后会有什么样的影响?由于浮动元素会脱离文档流,所以导致不占据页面空间,所以会对父元素高度带来一定影响。如果一个元素中包含的元素全部是浮动元素,那么该元素高度将变成0(高度塌陷) 3.如何清除浮动解决方案 及 原理分析方案1直接设置父元素的高度优势:极其简单弊端:必须要知道父元素高度是多少 方案2在父元素中,追加空子元素,并设置其clear属性为bothclear是css中专用于清除浮动的属性作用:清除当前元素前面的元素浮动所带来的影响取值:1、none默认值,不做任何清除浮动的操作2、left清除前面元素左浮动带来的影响3、right清除前面元素右浮动带来的影响4、both清除前面元素所有浮动带来的影响优势:代码量少 容易掌握 简单易懂弊端:会添加许多无意义的空标签,有违结构与表现的分离,不便于后期的维护 方案3设置父元素浮动优势:简单,代码量少,没有结构和语义化问题弊端:对后续元素会有影响 方案4为父元素设置overflow属性取值:hidden 或 auto优势:简单,代码量少弊端:如果有内容要溢出显示(弹出菜单),也会被一同隐藏 方案5父元素设置display:table优势:不影响结构与表现的分离,语义化正确,代码量少弊端:盒模型属性已经改变,会造成其他问题 方案6使用内容生成的方式清除浮动 12345.clearfix:after { content:""; display: block; clear:both; }12345 :after 选择器向选定的元素之后插入内容content:""; 生成内容为空display: block; 生成的元素以块级元素显示,clear:both; 清除前面元素浮动带来的影响相对于空标签闭合浮动的方法优势:不破坏文档结构,没有副作用弊端:代码量多 方案712345.cf:before,.cf:after { content:""; display:table;}.cf:after { clear:both; }12345 优势:不破坏文档结构,没有副作用弊端: 代码量多注意:display:table本身无法触发BFC,但是它会产生匿名框(anonymous boxes),而匿名框中的display:table-cell可以触发BFC,简单说就是,触发块级格式化上下文的是匿名框,而不是display:table。所以通过display:table和display:table-cell创建的BFC效果是不一样的(后面会说到BFC)。 CSS2.1 表格模型中的元素,可能不会全部包含在除HTML之外的文档语言中。这时,那些“丢失”的元素会被模拟出来,从而使得表格模型能够正常工作。所有的表格元素将会自动在自身周围生成所需的匿名table对象,使其符合table/inline-table、table-row、table- cell的三层嵌套关系。 疑问为什么会margin边距重叠?overflow:hidden, 语义应该是溢出:隐藏,按道理说,子元素浮动了,但依然是在父元素里的,而父元素高度塌陷,高度为0了,子元素应该算是溢出了,为什么没有隐藏,反而撑开了父元素的高度?为什么display:table也能清除浮动,原理是什么? 解释要解释这些疑问,我们就要提到Formatting contextFormatting context是W3C CSS2.1规范中的一个概念。它是页面中的一块渲染区域,并且有一套渲染规则,它决定了其子元素将如何定位,以及和其他元素的关系和相互作用。最常见的Formatting context有Block fomatting context(简称BFC)和Inline formatting context(简称IFC)。CSS2.1 中只有BFC和IFC, CSS3中还增加了GFC和FFC这里主要说BFCBFC(Block formatting context)直译为”块级格式化上下文”。它是一个独立的渲染区域,只有Block-level box参与, 它规定了内部的Block-level Box如何布局,并且与这个区域外部毫不相干。block-level box,display属性为block, list-item, table的元素,会生成block-level box。并且参与block fomatting context。inline-level box, display属性为inline, inline-block, inline-table的元素,会生成inline-level box。并且参与inline formatting context。BFC布局规则:1、内部的Box会在垂直方向,按照从上到下的方式逐个排列。2、Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠3、每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。4、BFC的区域不会与float box重叠。5、BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。6、计算BFC的高度时,浮动元素的高度也参与计算触发BFC的条件1、根元素2、float (left,right)3、overflow 除了visible 以外的值(hidden,auto,scroll )4、display (table-cell,table-caption,inline-block)5、position(absolute,fixed)举例详解BFC 123456789101112131415161718<style> .top{ width:100px; height:100px; background:red; margin:50px; } .bottom{ width:100px; height:100px; background:blue; margin:20px; }</style><body> <div class="top">上</div> <div class="bottom">下</div></body> 依据BFC布局规则第二条: Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠注意:发生重叠后,外边距的高度等于两个发生重叠的外边距的高度中的较大者 1234567891011121314151617<style> .top{ width:100px; height:100px; background:red; float:left; } .bottom{ width:200px; height:200px; background:blue; }</style><body> <div class="top"></div> <div class="bottom"></div></body> 依据BFC布局规则第三条: 每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。我们可以看到,虽然有浮动的元素top,但是bottom的左边依然与包含块的左边相接触。 123456789101112131415161718192021width:100px;height:100px;background:red;float:left;<style> .top{ width:100px; height:100px; background:red; float:left; } .bottom{ width:200px; height:200px; background:blue; overflow:hidden; }</style><body> <div class="top"></div> <div class="bottom"></div></body> 依据BFC布局规则第四条: BFC的区域不会与float box重叠。看代码和效果图,可以看出,这次的代码比上面的代码多了一行overflow:hidden;用这行代码触发新的BFC后,由于这个新的BFC不会与浮动的top重叠,所以bottom的位置改变了 1234567891011121314151617181920<style> p{ width:100px; height:100px; background:red; float:left; } div{ width:200px; border:1px solid blue; }</style><body> <div> <p></p> </div></body>width:100px;height:100px;background:red;float:left; 当div增加 overflow:hidden; 时 效果如下 依据BFC布局规则第六条: 计算BFC的高度时,浮动元素的高度也参与计算。到此我们应该是解决了上面的所有疑问了。 总结清除浮动的方式有很多种,但是实现的原理主要是靠clear属性,和触发新的BFC,通过详细的解释与比较,最后两种内容生成的方式是比较推荐使用的,如果需要考虑margin重叠的问题,就用方案7,不考虑就用方案6。 文章转载自CSDN]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>CSS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Linux]]></title>
<url>%2F2017%2F04%2F16%2FLinux%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4%2F</url>
<content type="text"><![CDATA[ 以前读书的时候做IC仿真调试,因为IC软件是装在linux系统环境,所以我们就在在基于RedHat操作系统上进行操作,那也是我第一次切切实实的使用Linux系统,界面的轻巧,文件夹设置等都给我留下了很深刻的印象。 后来我又因为自己和师傅的缘故,陆陆续续接触了centos、ubuntu等,也从那个时候接触到操作系统、服务器等皮毛知识。 一开始的时候,对于纯命令的操作是很不习惯的,后来接触到centos7并用xshell远程控制过一些服务器,才觉得很好用,那么久整理一下关于linux不错的资源网站吧。 *菜鸟论坛,适合基础入门学习。 *Linux命令大全,很全的查找命令的手册。 *网易开源镜像站,各种系统资源均可以找到,此外还有各大高校的镜像资源站,华农的镜像资源也比较齐全。 ]]></content>
<categories>
<category>随笔</category>
</categories>
<tags>
<tag>Linux</tag>
</tags>
</entry>
<entry>
<title><![CDATA[48 个 JavaScript 代码片段]]></title>
<url>%2F2017%2F04%2F16%2F48%E4%B8%AAjs%E4%BB%A3%E7%A0%81%E7%89%87%E6%AE%B5%2F</url>
<content type="text"><![CDATA[48 个 JavaScript 代码片段Anagrams of string(带有重复项) 使用递归。对于给定字符串中的每个字母,为字母创建字谜。使用map()将字母与每部分字谜组合,然后使用reduce()将所有字谜组合到一个数组中,最基本情况是字符串长度等于2或1。 const anagrams = str => { if (str.length <= 2) return str.length === 2 ? [str, str[1] + str[0]] : [str]; return str.split(‘’).reduce((acc, letter, i) => acc.concat(anagrams(str.slice(0, i) + str.slice(i + 1)).map(val => letter + val)), []); }; // anagrams(‘abc’) -> [‘abc’,’acb’,’bac’,’bca’,’cab’,’cba’] 数组平均数 使用reduce()将每个值添加到累加器,初始值为0,总和除以数组长度。 const average = arr => arr.reduce((acc, val) => acc + val, 0) / arr.length; // average([1,2,3]) -> 2 大写每个单词的首字母 使用replace()匹配每个单词的第一个字符,并使用toUpperCase()来将其大写。 const capitalizeEveryWord = str => str.replace(/[a-z]/g, char => char.toUpperCase()); // capitalizeEveryWord(‘hello world!’) -> ‘Hello World!’ 首字母大写 使用slice(0,1)和toUpperCase()大写第一个字母,slice(1)获取字符串的其余部分。 省略lowerRest参数以保持字符串的其余部分不变,或将其设置为true以转换为小写。(注意:这和上一个示例不是同一件事情) const capitalize = (str, lowerRest = false) => str.slice(0, 1).toUpperCase() + (lowerRest ? str.slice(1).toLowerCase() : str.slice(1)); // capitalize(‘myName’, true) -> ‘Myname’ 检查回文 将字符串转换为toLowerCase(),并使用replace()从中删除非字母的字符。然后,将其转换为tolowerCase(),将(’’)拆分为单独字符,reverse(),join(’’),与原始的非反转字符串进行比较,然后将其转换为tolowerCase()。 const palindrome = str => { const s = str.toLowerCase().replace(/[W_]/g,’’); return s === s.split(‘’).reverse().join(‘’); } // palindrome(‘taco cat’) -> true 计数数组中值的出现次数 每次遇到数组中的特定值时,使用reduce()来递增计数器。 const countOccurrences = (arr, value) => arr.reduce((a, v) => v === value ? a + 1 : a + 0, 0); // countOccurrences([1,1,2,1,2,3], 1) -> 3 当前URL 使用window.location.href来获取当前URL。 const currentUrl = _ => window.location.href; // currentUrl() -> ‘https://google.com‘ Curry 使用递归。如果提供的参数(args)数量足够,则调用传递函数f,否则返回一个curried函数f。 const curry = (fn, arity = fn.length, …args) => arity <= args.length ? fn(…args) : curry.bind(null, fn, arity, …args); // curry(Math.pow)(2)(10) -> 1024 // curry(Math.min, 3)(10)(50)(2) -> 2 Deep flatten array 使用递归,使用reduce()来获取所有不是数组的元素,flatten每个元素都是数组。 const deepFlatten = arr => arr.reduce((a, v) => a.concat(Array.isArray(v) ? deepFlatten(v) : v), []); // deepFlatten([1,[2],[[3],4],5]) -> [1,2,3,4,5] 数组之间的区别 从b创建一个Set,然后在a上使用Array.filter(),只保留b中不包含的值。 const difference = (a, b) => { const s = new Set(b); return a.filter(x => !s.has(x)); }; // difference([1,2,3], [1,2]) -> [3] 两点之间的距离 使用Math.hypot()计算两点之间的欧几里德距离。 const distance = (x0, y0, x1, y1) => Math.hypot(x1 - x0, y1 - y0); // distance(1,1, 2,3) -> 2.23606797749979 可以按数字整除 使用模运算符(%)来检查余数是否等于0。 const isDivisible = (dividend, divisor) => dividend % divisor === 0; // isDivisible(6,3) -> true 转义正则表达式 使用replace()来转义特殊字符。 const escapeRegExp = str => str.replace(/[.*+?^${}()|[]]/g, ‘$&’); // escapeRegExp(‘(test)’) -> (test) 偶数或奇数 使用Math.abs()将逻辑扩展为负数,使用模(%)运算符进行检查。 如果数字是偶数,则返回true;如果数字是奇数,则返回false。 const isEven = num => num % 2 === 0; // isEven(3) -> false 阶乘 使用递归。如果n小于或等于1,则返回1。否则返回n和n - 1的阶乘的乘积。 const factorial = n => n <= 1 ? 1 : n * factorial(n - 1); // factorial(6) -> 720 斐波那契数组生成器 创建一个特定长度的空数组,初始化前两个值(0和1)。使用Array.reduce()向数组中添加值,后面的一个数等于前面两个数相加之和(前两个除外)。 const fibonacci = n => Array(n).fill(0).reduce((acc, val, i) => acc.concat(i > 1 ? acc[i - 1] + acc[i - 2] : i), []); // fibonacci(5) -> [0,1,1,2,3] 过滤数组中的非唯一值 将Array.filter()用于仅包含唯一值的数组。 const filterNonUnique = arr => arr.filter(i => arr.indexOf(i) === arr.lastIndexOf(i)); // filterNonUnique([1,2,2,3,4,4,5]) -> [1,3,5] Flatten数组 使用reduce()来获取数组中的所有元素,并使用concat()来使它们flatten。 const flatten = arr => arr.reduce((a, v) => a.concat(v), []); // flatten([1,[2],3,4]) -> [1,2,3,4] 从数组中获取最大值 使用Math.max()与spread运算符(…)结合得到数组中的最大值。 const arrayMax = arr => Math.max(…arr); // arrayMax([10, 1, 5]) -> 10 从数组中获取最小值 使用Math.min()与spread运算符(…)结合得到数组中的最小值。 const arrayMin = arr => Math.min(…arr); // arrayMin([10, 1, 5]) -> 1 获取滚动位置 如果已定义,请使用pageXOffset和pageYOffset,否则使用scrollLeft和scrollTop,可以省略el来使用window的默认值。 const getScrollPos = (el = window) => ({x: (el.pageXOffset !== undefined) ? el.pageXOffset : el.scrollLeft, y: (el.pageYOffset !== undefined) ? el.pageYOffset : el.scrollTop}); // getScrollPos() -> {x: 0, y: 200} 最大公约数(GCD) 使用递归。基本情况是当y等于0时。在这种情况下,返回x。否则,返回y的GCD和x / y的其余部分。 const gcd = (x, y) => !y ? x : gcd(y, x % y); // gcd (8, 36) -> 4 Head of list 返回ARR[0] const head = arr => arr[0]; // head([1,2,3]) -> 1 list初始化 返回arr.slice(0,-1) const initial = arr => arr.slice(0, -1); // initial([1,2,3]) -> [1,2] 用range初始化数组 使用Array(end-start)创建所需长度的数组,使用map()来填充范围中的所需值,可以省略start使用默认值0。 const initializeArrayRange = (end, start = 0) => Array.apply(null, Array(end - start)).map((v, i) => i + start); // initializeArrayRange(5) -> [0,1,2,3,4] 用值初始化数组 使用Array(n)创建所需长度的数组,fill(v)以填充所需的值,可以忽略value使用默认值0。 const initializeArray = (n, value = 0) => Array(n).fill(value); // initializeArray(5, 2) -> [2,2,2,2,2] 列表的最后 返回arr.slice(-1)[0] const last = arr => arr.slice(-1)[0]; // last([1,2,3]) -> 3 测试功能所花费的时间 使用performance.now()获取函数的开始和结束时间,console.log()所花费的时间。第一个参数是函数名,随后的参数传递给函数。 const timeTaken = callback => { console.time(‘timeTaken’); const r = callback(); console.timeEnd(‘timeTaken’); return r; }; // timeTaken(() => Math.pow(2, 10)) -> 1024 // (logged): timeTaken: 0.02099609375ms 来自键值对的对象 使用Array.reduce()来创建和组合键值对。 const objectFromPairs = arr => arr.reduce((a, v) => (a[v[0]] = v[1], a), {}); // objectFromPairs([[‘a’,1],[‘b’,2]]) -> {a: 1, b: 2} 管道 使用Array.reduce()通过函数传递值。 const pipe = (…funcs) => arg => funcs.reduce((acc, func) => func(acc), arg); // pipe(btoa, x => x.toUpperCase())(“Test”) -> “VGVZDA==” Powerset 使用reduce()与map()结合来遍历元素,并将其组合成包含所有组合的数组。 const powerset = arr => arr.reduce((a, v) => a.concat(a.map(r => [v].concat(r))), [[]]); // powerset([1,2]) -> [[], [1], [2], [2,1]] 范围内的随机整数 使用Math.random()生成一个随机数并将其映射到所需的范围,使用Math.floor()使其成为一个整数。 const randomIntegerInRange = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min; // randomIntegerInRange(0, 5) -> 2 范围内的随机数 使用Math.random()生成一个随机值,使用乘法将其映射到所需的范围。 const randomInRange = (min, max) => Math.random() * (max - min) + min; // randomInRange(2,10) -> 6.0211363285087005 随**机化数组的顺序** 使用sort()重新排序元素,利用Math.random()来随机排序。 const shuffle = arr => arr.sort(() => Math.random() - 0.5); // shuffle([1,2,3]) -> [2,3,1] 重定向到URL 使用window.location.href或window.location.replace()重定向到url。 传递第二个参数来模拟链接点击(true - default)或HTTP重定向(false)。 const redirect = (url, asLink = true) => asLink ? window.location.href = url : window.location.replace(url); // redirect(‘https://google.com‘) 反转一个字符串 使用数组解构和Array.reverse()来颠倒字符串中的字符顺序。合并字符以使用join(‘’)获取字符串。 const reverseString = str => […str].reverse().join(‘’); // reverseString(‘foobar’) -> ‘raboof’ RGB到十六进制 使用按位左移运算符(<<)和toString(16),然后padStart(6,“0”)将给定的RGB参数转换为十六进制字符串以获得6位十六进制值。 const rgbToHex = (r, g, b) => ((r << 16) + (g << 8) + b).toString(16).padStart(6, ‘0’); // rgbToHex(255, 165, 1) -> ‘ffa501’ 滚动到顶部 使用document.documentElement.scrollTop或document.body.scrollTop获取到顶部的距离。 从顶部滚动一小部分距离。 使用window.requestAnimationFrame()来滚动。 const scrollToTop = _ => { const c = document.documentElement.scrollTop || document.body.scrollTop; if (c > 0) { window.requestAnimationFrame(scrollToTop); window.scrollTo(0, c - c / 8); } }; // scrollToTop() 随机数组值 使用Array.map()和Math.random()创建一个随机值的数组。使用Array.sort()根据随机值对原始数组的元素进行排序。 数组之间的相似性 使用filter()移除不是values的一部分值,使用includes()确定。 const similarity = (arr, values) => arr.filter(v => values.includes(v)); // similarity([1,2,3], [1,2,4]) -> [1,2] 按字符串排序(按字母顺序排列) 使用split(’’)分割字符串,sort()使用localeCompare(),使用join(’’)重新组合。 const sortCharactersInString = str => str.split(‘’).sort((a, b) => a.localeCompare(b)).join(‘’); // sortCharactersInString(‘cabbage’) -> ‘aabbceg’ 数组总和 使用reduce()将每个值添加到累加器,初始化值为0。 const sum = arr => arr.reduce((acc, val) => acc + val, 0); // sum([1,2,3,4]) -> 10 交换两个变量的值 使用数组解构来交换两个变量之间的值。 [varA, varB] = [varB, varA]; // [x, y] = [y, x] 列表的tail 返回arr.slice(1) const tail = arr => arr.length > 1 ? arr.slice(1) : arr; // tail([1,2,3]) -> [2,3] // tail([1]) -> [1] 数组唯一值 使用ES6 Set和… rest操作符去掉所有重复值。 const unique = arr => […new Set(arr)]; // unique([1,2,2,3,4,4,5]) -> [1,2,3,4,5] URL参数 使用match() 与适当的正则表达式来获得所有键值对,适当的map() 。使用Object.assign()和spread运算符(…)将所有键值对组合到一个对象中,将location.search作为参数传递给当前url。 const getUrlParameters = url => url.match(/([^?=&]+)(=([^&]*))/g).reduce( (a, v) => (a[v.slice(0, v.indexOf(‘=’))] = v.slice(v.indexOf(‘=’) + 1), a), {} ); // getUrlParameters(‘http://url.com/page?name=Adam&surname=Smith‘) -> {name: ‘Adam’, surname: ‘Smith’} UUID生成器 使用crypto API生成符合RFC4122版本4的UUID。 const uuid = _ => ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c => (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16) ); // uuid() -> ‘7982fcfe-5721-4632-bede-6000885be57d’ 验证数字 使用!isNaN和parseFloat()来检查参数是否是一个数字,使用isFinite()来检查数字是否是有限的。 const validateNumber = n => !isNaN(parseFloat(n)) && isFinite(n) && Number(n) == n; // validateNumber(‘10’) -> true]]></content>
<categories>
<category>代码</category>
</categories>
<tags>
<tag>js</tag>
</tags>
</entry>
<entry>
<title><![CDATA[First Blog]]></title>
<url>%2F2017%2F04%2F05%2Fhello-world%2F</url>
<content type="text"><![CDATA[Welcome to my blog!本博客基于Hexo博客框架搭建,并部署在GithubPages上,仍在不断完善中。 因为关于部署搭建的文章很多,所以我将不会赘述关于部署搭建相关的内容,不过在搭建的过程中的的确确遇到各种各样的小Bug,但是当网站搭建出来,老实说真的蛮开心的! 页面的右下角是网站的“守护兽”,叫hijiki,毕竟我不经常有空更新,所以总要有人看家嘛!不要调戏它哦!超凶嘚!喵~ 如果你有什么想法或建议想与我联系,欢迎加我QQ1107840447或者你也可以邮箱发送到:mrgowell1994@gmail.com(墙外用户)或megowell@qq.com。 最后,欢迎您的访问,大爷!有空常来看看哟!]]></content>
<categories>
<category>blog</category>
</categories>
<tags>
<tag>life</tag>
</tags>
</entry>
</search>