-
Notifications
You must be signed in to change notification settings - Fork 0
/
content.json
1 lines (1 loc) · 105 KB
/
content.json
1
{"meta":{"title":"linxun","subtitle":"linxunzyf","description":"林寻的博客","author":"linxunzyf","url":"https://jx-zyf.github.io"},"pages":[{"title":"about","date":"un22fin22","updated":"un55fin55","comments":true,"path":"about/index.html","permalink":"https://jx-zyf.github.io/about/index.html","excerpt":"","text":""},{"title":"categories","date":"un22fin22","updated":"un22fin22","comments":false,"path":"categories/index.html","permalink":"https://jx-zyf.github.io/categories/index.html","excerpt":"","text":""},{"title":"tags","date":"un22fin22","updated":"un22fin22","comments":false,"path":"tags/index.html","permalink":"https://jx-zyf.github.io/tags/index.html","excerpt":"","text":""}],"posts":[{"title":"React父组件中使用子组件的方法","slug":"react父组件使用子组件中的方法","date":"un66fin66","updated":"un66fin66","comments":true,"path":"/posts/e527abd2/","link":"","permalink":"https://jx-zyf.github.io/posts/e527abd2/","excerpt":"","text":"父组件中使用子组件的方法12345678910111213141516171819202122232425262728293031323334import React, { Component, Fragment } from 'react';class Child extends Component { componentDidMount() { this.props.onRef(this); } testEvent = () => { console.log('我被触发了'); } render() { return ( '我是子组件' ) }}export default class Parent extends Component { onRef = (ref) => { this.child = ref; } handleClick = () => { this.child.testEvent(); } render() { return ( <Fragment> <Child onRef={this.onRef} /> <button onClick={this.handleClick}>click</button> </Fragment> ) }}","categories":[{"name":"coder","slug":"coder","permalink":"https://jx-zyf.github.io/categories/coder/"}],"tags":[{"name":"JavaScript","slug":"JavaScript","permalink":"https://jx-zyf.github.io/tags/JavaScript/"},{"name":"react","slug":"react","permalink":"https://jx-zyf.github.io/tags/react/"}]},{"title":"js小技巧(二)","slug":"js小技巧(2)","date":"un66fin66","updated":"un66fin66","comments":true,"path":"/posts/cfcdf77d/","link":"","permalink":"https://jx-zyf.github.io/posts/cfcdf77d/","excerpt":"","text":"获取当前浏览器信息12window.navigator// 里面包含当前设备操作系统信息 platform oscpu JS判断是否为闰年123if (year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0)) { // 是闰年} JS求当天是本年的第几天12345678910111213const date = new Date();const dateArr = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];const day = date.getDate();const month = date.getMonth() + 1;const year = date.getFullYear();let numOfYear = 0;for ( let i = 1; i < month; i++) { numOfYear += dateArr[i];}numOfYear += day;if (month > 2 && (year % 4 === 0) && (year % 100 !== 0 || year % 400 === 0)) { numOfYear += 1;} 深复制(兼容各种数据类型)123456789function clone(data) { let result; if(typeof data === 'object' && data !== null) { result = Object.prototype.toString.call(data) === '[object Array]' ? [ ...data ] : { ...data }; } else { result = data; } return result;}","categories":[{"name":"coder","slug":"coder","permalink":"https://jx-zyf.github.io/categories/coder/"}],"tags":[{"name":"JavaScript","slug":"JavaScript","permalink":"https://jx-zyf.github.io/tags/JavaScript/"}]},{"title":"小程序学习","slug":"微信小程序遇到的坑","date":"un33fin33","updated":"un66fin66","comments":true,"path":"/posts/8cc9dda6/","link":"","permalink":"https://jx-zyf.github.io/posts/8cc9dda6/","excerpt":"","text":"START最近公司需要开发一个简单微信小程序,记录下遇到的一些问题(本人第一次开发小程序,UI库使用weui) PROBLEM1.图片显示问题在微信小程序image图片标签中,如果在样式中只设置了宽度,没有设置高度或者高度设置为auto,将会导致图片不显示,需要指定图片的高度才能显示。不过image标签封装了mode属性,可以根据需要设置。 2.wx.showModal的局限性官方的wx.showModal只能展示文本内容,content不能支持富文本和wxml标签,并且也不能自定义样式。一番折腾后发现可以自定义modal组件的内容,比如像这样:123456789101112<button bindtap="openAlert">打开弹窗</button><modal title="标题" hidden="{{modalHidden}}" bindconfirm="modalConfirm" bindcancel="modalCancel"> <view> <image class="image" src="{{imgurl}}"></image> <text>自定义内容</text> </view></modal> 不过这样自定义弹窗我们需要手动控制弹窗的显示和隐藏,代码如下:12345678910111213141516171819202122232425Page({ data: { modalHidden: true, }, openAlert: function(e) { this.setData({ modalHidden: false }); }, modalConfirm: function() { this.setData({ modalHidden: true }); console.log('点击确定'); }, modalCancel: function() { this.setData({ modalHidden: true }); console.log('点击取消'); },}) 这样我们的弹窗就可以随心所欲了,而且样式也可以自己控制。 假如我们不想要取消按钮,将modal的no-cancel设为true即可 3.css(wxss)不能使用本地资源wxss中background-image不能使用本地资源,可以使用网络图片,或者 base64,或者使用标签;若使用会报错。 4.微信开发者工具文件缓存问题描述:在wx.showToast的时候,官方文档中轻提示的icon只支持success,loading,none三种图标,明显不能满足我们的业务需求,好在它还支持image参数——自定义图标的本地路径,并且优先级高于icon。一阵开心选了个图标,发现好丑啊,想换一个,于是将图标文件替换,以为大功告成。万万没想到,在硬盘里图标是已经更换了的,但是开发者工具里的图标死活就是不更新,不管我怎么清缓存都没用。 比较蠢的解决办法:将原来的文件重命名一下,图标居然更新了!!!可是晚期强迫症表示我就想要原来的名称,所以我又重命名回原来的名称。。。问题又来了,我编译预览的时候图标还是原来的,心态崩了。没办法只能重命名,编译预览,图标更新(再重命名回原来的名称,编译预览——强迫症患者强行加戏)大功告成! 新人求助有没有大佬有更好的解决办法,望告知 END由于开发的是一个很简单的小程序,涉及的内容比较少,以后再慢慢填坑","categories":[{"name":"coder","slug":"coder","permalink":"https://jx-zyf.github.io/categories/coder/"}],"tags":[{"name":"小程序","slug":"小程序","permalink":"https://jx-zyf.github.io/tags/小程序/"}]},{"title":"Node反向代理请求","slug":"node反向代理","date":"un66fin66","updated":"un66fin66","comments":true,"path":"/posts/11645e24/","link":"","permalink":"https://jx-zyf.github.io/posts/11645e24/","excerpt":"","text":"前一阵子有个需求是上传文件,遇到跨域的问题,我的解决办法是使用node搭建本地静态服务器,把请求转发到后端服务器。 撸起袖子就是干,却发现一个问题,node这里拿不到上传过来的formData格式的文件,然后疯狂查资料,花了我一个上午的时间,还是不行。最后在老大的帮助下解决了,其实很简单,我不需要拿到上传的文件,只需要把请求原封不动的全部转发出去就行了。怪我太天真,多写了代码,还没有效果。下面是代理的关键代码: 123456789101112const express = require('express');const proxyMiddleWare = require(\"http-proxy-middleware\");const proxyPath = \"http://xxx\"; //目标后端服务地址const proxyOption ={ target: proxyPath, changeOrigoin: true };const app = express();app.use(\"/\", proxyMiddleWare(proxyOption));","categories":[{"name":"coder","slug":"coder","permalink":"https://jx-zyf.github.io/categories/coder/"}],"tags":[{"name":"JavaScript","slug":"JavaScript","permalink":"https://jx-zyf.github.io/tags/JavaScript/"},{"name":"Node","slug":"Node","permalink":"https://jx-zyf.github.io/tags/Node/"}]},{"title":"js小技巧(一)","slug":"js小技巧(1)","date":"un66fin66","updated":"un66fin66","comments":true,"path":"/posts/60fc6c1f/","link":"","permalink":"https://jx-zyf.github.io/posts/60fc6c1f/","excerpt":"","text":"将浮点数(或字符串)转化为整数123456~~'1.2' // 1~~'1' // 1~~1.2 // 11.2|0 // 1'1.2'|0 // 1'1'|0 // 1 不声明第三个变量的值交换123let a = 2, b = 1;a = [b, b=a][0];console.log(`a:${a},b:${b}`); // a:1,b:2 获取文件的后缀名123456function getFileExtension(fileName) { let arr = fileName.split('.'); return arr.length >= 2 ? arr.pop() : '';}console.log(getFileExtension('test')); // ''console.log(getFileExtension('test.test.txt')); // txt 验证座机号码正则1/((\\+\\d{2,4}-)?(0\\d{1,3}-)\\d{7,8})/ 不定期更新…","categories":[{"name":"coder","slug":"coder","permalink":"https://jx-zyf.github.io/categories/coder/"}],"tags":[{"name":"JavaScript","slug":"JavaScript","permalink":"https://jx-zyf.github.io/tags/JavaScript/"}]},{"title":"常用排序算法(一)","slug":"常用算法","date":"un66fin66","updated":"un66fin66","comments":true,"path":"/posts/daae2fce/","link":"","permalink":"https://jx-zyf.github.io/posts/daae2fce/","excerpt":"","text":"冒泡排序描述:一次比较相邻的两个元素,如果它们顺序错误就交换位置;每次循环后最后的元素会是我们期待的数(最大或最小);针对所有元素重复以上步骤,除了第一个。直到两层循环全部执行完毕,排序完成。 时间复杂度:O(n²)空间复杂度:O(1)代码实现:1234567891011function bubbleSort(arr) { const len = arr.length; for (let i = 0; i < len; i++) { for (let j = 0; j < len - 1 - i; j++) { if (arr[j] > arr[j + 1]) { arr[j] = [arr[j + 1], arr[j + 1] = arr[j]][0]; } } } return arr;} 选择排序描述:首先在未排序队列中找出最大(小)的元素,存放到已排序队列中的起始位置;然后再从剩余未排序队列中继续找出最大(小)的元素,存放到已排序队列的末尾;以此类推,直到排序完毕。 时间复杂度:O(n²)空间复杂度:O(1)代码实现:1234567891011121314function selectionSort(arr) { const len = arr.length; let minIndex; for (let i = 0; i < len - 1; i++) { minIndex = i; for (let j = i + 1; j < len; j++) { if (arr[j] < arr[minIndex]) { minIndex = j; // 保存最小(大)值得索引 } } arr[i] = [arr[minIndex], arr[minIndex] = arr[i]][0]; } return arr;} 插入排序描述:将第一个元素看做已排序队列,剩余元素为未排序队列;从未排序队列取出一个元素,在已排序队列从后向前扫描,若该元素(已排序)大(小)于新元素(未排序队列取出的),则将该元素移到下一位置;直到该元素小(大)于或者等于新元素,将新元素插入到位置。重复以上步骤直到所有元素排序完成。 时间复杂度:O(n²)空间复杂度:O(1)代码实现:1234567891011121314function insertSort(arr) { const len = arr.length; let preIndex, current; for (let i = 1; i < len; i++) { preIndex = i - 1; current = arr[i]; while(preIndex >= 0 && arr[preIndex] > current) { arr[preIndex + 1] = arr[preIndex]; preIndex--; } arr[preIndex + 1] = current; } return arr;} 快速排序描述:选择一个合适的元素a(一般是第一个元素)作为排序基准,将未排序队列中比a小和比a大的分别放在两侧,将两侧的子数组重复以上步骤(递归),直到最后只剩下一个元素(递归出口),排序完成。 时间复杂度:O(n㏒₂n)空间复杂度:O(n㏒₂n)代码实现:1234567891011function quickSort(arr) { // 递归出口 if (arr.length <= 1) return arr; // 基准(可以随意选择) let number = arr[0]; let left = [], right = []; for (let i = 1; i < arr.length; i++) { arr[i] <= number ? left.push(arr[i]) : right.push(arr[i]); } return quickSort(left).concat([number], quickSort(right));}","categories":[{"name":"coder","slug":"coder","permalink":"https://jx-zyf.github.io/categories/coder/"}],"tags":[{"name":"JavaScript","slug":"JavaScript","permalink":"https://jx-zyf.github.io/tags/JavaScript/"}]},{"title":"前端请求之jquery ajax, fetch, axios","slug":"前端请求","date":"un33fin33","updated":"un44fin44","comments":true,"path":"/posts/9258a52/","link":"","permalink":"https://jx-zyf.github.io/posts/9258a52/","excerpt":"","text":"前端是个发展迅速的领域,前端请求自然也发展迅速,从原生的XHR到jquery ajax,再到现在的axios和fetch。 jquery ajax12345678$.ajax({ type: 'POST', url: url, data: data, dataType: dataType, success: function() {}, error: function() {}}) 它是对原生XHR的封装,还支持JSONP,非常方便;真的是用过的都说好。但是随着react,vue等前端框架的兴起,jquery早已不复当年之勇。很多情况下我们只需要使用ajax,但是却需要引入整个jquery,这非常的不合理,于是便有了fetch的解决方案。 fetchfetch号称是ajax的替代品,它的API是基于Promise设计的,旧版本的浏览器不支持Promise,需要使用polyfill es6-promise 举个例子:123456789101112131415161718// 原生XHRvar xhr = new XMLHttpRequest();xhr.open('GET', url);xhr.onreadystatechange = function() { if (xhr.readyState === 4 && xhr.status === 200) { console.log(xhr.responseText) // 从服务器获取数据 }}xhr.send()// fetchfetch(url) .then(response => { if (response.ok) { response.json() } }) .then(data => console.log(data)) .catch(err => console.log(err)) 看起来好像是方便点,then链就像之前熟悉的callback。 在MDN上,讲到它跟jquery ajax的区别,这也是fetch很奇怪的地方: 当接收到一个代表错误的 HTTP 状态码时,从 fetch()返回的 Promise 不会被标记为 reject, 即使该 HTTP 响应的状态码是 404 或 500。相反,它会将 Promise 状态标记为 resolve (但是会将 resolve 的返回值的 ok 属性设置为 false ), 仅当网络故障时或请求被阻止时,才会标记为 reject。默认情况下, fetch 不会从服务端发送或接收任何 cookies, 如果站点依赖于用户 session,则会导致未经认证的请求(要发送 cookies,必须设置 credentials 选项). 突然感觉这还不如jquery ajax好用呢?别急,再搭配上async/await将会让我们的异步代码更加优雅:12345async function test() { let response = await fetch(url); let data = await response.json(); console.log(data)} 看起来是不是像同步代码一样?简直完美!好吧,其实并不完美,async/await是ES7的API,目前还在试验阶段,还需要我们使用babel进行转译成ES5代码。 还要提一下的是,fetch是比较底层的API,很多情况下都需要我们再次封装。比如:123456789// jquery ajax$.post(url, {name: 'test'})// fetchfetch(url, { method: 'POST', body: Object.keys({name: 'test'}).map((key) => { return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]); }).join('&')}) 由于fetch是比较底层的API,所以需要我们手动将参数拼接成’name=test’的格式,而jquery ajax已经封装好了。所以fetch并不是开箱即用的。 另外,fetch还不支持超时控制。 哎呀,感觉fetch好垃圾啊,,还需要继续成长。。 axiosaxios是尤雨溪大神推荐使用的,它也是对原生XHR的封装。它有以下几大特性: 可以在node.js中使用 提供了并发请求的接口 支持Promise API 简单使用123456axios({ method: 'GET', url: url,}).then(res => {console.log(res)}).catch(err => {console.log(err)}) 写法有很多种,自行查看文档 并发请求123456789101112function getUserAccount() { return axios.get('/user/12345');}function getUserPermissions() { return axios.get('/user/12345/permissions');}axios.all([getUserAccount(), getUserPermissions()]) .then(axios.spread(function (acct, perms) { // Both requests are now complete })); 这是官方的并发案例,好像是挺厉害的样子。不过感觉它的all方法应该是基于Promise.all()的 axios体积比较小,也没有上面fetch的各种问题,我认为是当前最好的请求方式 最后,这都是些基础用法,还没有深入了解,还是要在实战中踩过坑才能运用的更加自如。","categories":[{"name":"coder","slug":"coder","permalink":"https://jx-zyf.github.io/categories/coder/"}],"tags":[{"name":"JavaScript","slug":"JavaScript","permalink":"https://jx-zyf.github.io/tags/JavaScript/"}]},{"title":"css 变量","slug":"css变量","date":"un22fin22","updated":"un22fin22","comments":true,"path":"/posts/5f2b98d1/","link":"","permalink":"https://jx-zyf.github.io/posts/5f2b98d1/","excerpt":"","text":"原生css终于有变量了!今天就学习下css变量 定义css变量在css中,变量都是以两个横线(–)开头的,css变量也被称为”自定义属性” 123.test { --font-size: 14px;} 像上面的”–font-size”就是变量 使用css变量使用过预处理器的都知道,我们可以通过引用变量名来使用变量。比如: 1234$font-size: 14px;.test { font-size: $font-size;} 但是在css中使用就不一样了,我们需要通过var()函数来引用变量,像这样: 123456:root { --font-size: 14px;}.test { font-size: var(--font-size);} 在很多预处理工具中,我们可以在任何地方使用变量。但是在原生css中,变量只能作为属性值,不能作为属性名。 123456:root { --bg-color: background-color;}.test { var(--bg-color): red;} 上面的代码会报错! 另外,css变量也不能像预处理工具那样直接做数学计算,需要通过css的calc()函数。 1234.test { --font-size: calc(20px * 2); font-size: var(--font-size);} css变量作用域我们都知道javascript变量的作用域分为全局作用域和局部作用域,css变量也是这样。 在”:root”中定义的变量就是全局变量,在其他选择器中定义的就是局部变量。 将css变量作为自定义属性看待,因此它跟很多普通属性类似。可以在任意元素(包括伪元素)上自定义属性,并且也存在继承 注意 css变量区分大小写 不要在代码中出现循环依赖 1234567:root { --test: var(--test);}.test { --test1: var(--test2); --test2: var(--test1);} 不要使用非法变量,比如: 1234.test { --color: red; font-size: var(--color);} 上面代码中font-size的值被解析成red,这显然是不合法的,它最后会被解析成默认值即14px 使用单独符号要小心,举个例子1234.test { --font-size: 20; font-size: var(--font-size)px;} 上面的写法不会解析成20px,而是20 px,中间是有空格的,所以样式不会应用到元素上面。解决办法有下面两种:12345678910<!-- 第一种方法 -->.test { --font-size: 20px; font-size: var(--font-size);}<!-- 第二种方法 -->.test { --font-size:20; font-size: calc(var(--font-size) * 1px);} 小测试1234567891011121314<style>/* 定义变量 */:root { --color: blue; }div { --color: green; }#alert { --color: red; }/* 使用变量 */* { color: var(--color); }</style><p>第一个p标签</p><div>第一个div</div><div id='alert'> 第二个div<p>第二个p标签</p></div> 上面代码中的元素中的字体分别是什么颜色? 第一个p标签只匹配到了:root选择器,所以是blue;第一个div匹配到了div选择器,所以是green;第二个div匹配到了#alert选择器,所以是red;第二个p标签是第二个div的子元素,会继承它的字体颜色,所以是red。 参考:关于 CSS 变量,你需要了解的一切","categories":[{"name":"coder","slug":"coder","permalink":"https://jx-zyf.github.io/categories/coder/"}],"tags":[{"name":"css","slug":"css","permalink":"https://jx-zyf.github.io/tags/css/"}]},{"title":"git 常用操作","slug":"git常用操作","date":"un11fin11","updated":"un11fin11","comments":true,"path":"/posts/3ac54c0c/","link":"","permalink":"https://jx-zyf.github.io/posts/3ac54c0c/","excerpt":"","text":"git在我们的工作中是必不可少的,这里简单记录下常用的git操作,方便以后查询 git init: 初始化仓库(版本库) git add : 添加文件(修改的内容)到暂存区 git commit -m “提交说明”: 提交暂存区中的所有修改到分支 git push origin <分支名>: 将提交推送到远程分支 git status: 查看仓库状态 git diff : 查看具体修改了什么内容 git diff HEAD – : 查看工作区和版本库里最新版本的区别 git log (–pretty=oneline): 查看提交记录(提交记录内容一条显示),会有每次提交的commit id,以便确定要回退到哪个版本 在Git中,用HEAD表示当前版本,上一个版本就是HEAD^,上上一个版本就是HEAD^^,当然往上100个版本写100个^比较容易数不过来,所以写成HEAD~100 git reset –hard HEAD^: 回到上一个版本 git reset –hard : 回到指定版本(版本号不用写全,但一定要让Git能找到唯一的那个版本) git checkout – : 将该文件在工作区的修改全部撤销(好像 – 没有也可以…)其实是用版本库中的版本替换工作区的版本(修改和删除都可以用) git reset HEAD : 将暂存区的修改撤销,重新放回工作区 git rm : 从版本库中删除文件 (后面需要提交 git commit -m “说明”) git checkout -b <分支名>: 创建并切换到该分支 git branch <分支名>: 创建分支 git checkout <分支名>: 切换到该分支 git branch: 查看当前分支(本地) git branch -r: 查看远程分支 git branch -a: 查看所有分支(包括远程) git merge <分支名>: 将指定分支合并到当前分支 git branch -d <分支名>: 删除指定分支(本地) git push origin test:test: 将本地test分支推送到远程test(远程本身没有test) git push origin :test: 删除远程分支test(推送一个不存在的分支到test) git push origin –delete test: 删除远程分支test git stash: 工作进行到一半,还没有提交时,把当前工作现场’储藏’起来,等以后恢复现场后继续工作 git stash list: 查看工作现场列表 git stash apply: 恢复工作现场,但是此方法恢复后stash内容并不删除,需要用 git stash drop 删除 git stash pop: 恢复工作现场的同时把stash内容也删除了","categories":[{"name":"coder","slug":"coder","permalink":"https://jx-zyf.github.io/categories/coder/"}],"tags":[{"name":"git","slug":"git","permalink":"https://jx-zyf.github.io/tags/git/"}]},{"title":"antd-pro 使用笔记","slug":"antd-pro","date":"un33fin33","updated":"un33fin33","comments":true,"path":"/posts/23508308/","link":"","permalink":"https://jx-zyf.github.io/posts/23508308/","excerpt":"","text":"Ant Design Pro 是一个企业级中后台前端/设计解决方案。由阿里蚂蚁金服维护,已经开源 基于 antd-pro@1.1.0 介绍有两种方式安装,参见安装 安装之后会生成如下目录: ├── mock # 本地模拟数据├── public│ └── favicon.ico # Favicon├── src│ ├── assets # 本地静态资源│ ├── common # 应用公用配置,如导航信息│ ├── components # 业务通用组件│ ├── e2e # 集成测试用例│ ├── layouts # 通用布局│ ├── models # dva model│ ├── routes # 业务页面入口和常用模板│ ├── services # 后台接口服务│ ├── utils # 工具库│ ├── g2.js # 可视化图形配置│ ├── theme.js # 主题配置│ ├── index.ejs # HTML 入口模板│ ├── index.js # 应用入口│ ├── index.less # 全局样式│ └── router.js # 路由入口├── tests # 测试工具├── README.md└── package.json 介绍一下常用或者需要注意的几个目录: router.js 配置前端路由,查看项目的整体结构 layouts 页面的整体布局,抽离成单独的组件, common 公用配置,包括菜单配置,路由配置等 index.js 在之前的版本中,我们每次新建一个model,需要在这里注册才能使用;现在的版本不需要了 models 数据仓库,每个文件都需要一个命名空间。文件包含以下几个模块(实际上都是javascript对象):state => 数据构成effects => 处理异步action,采用generator的相关概念,将异步转化成同步写法。reducers => 处理同步action,相当于redux中的reducer函数,纯函数(即相同的输入得到相同的输出)subscriptions => 官方的解释是:subscriptions是一种从 源 获取数据的方法,它来自于elm。我的理解是订阅某些事件并对其做处理,不过我还没有使用过 参考 routes 业务组件,通常是需要连接数据仓库的组件。我们通过connect连接数据仓库之后可以通过this.props获取到数据和dispatch方法,并使用dispatch派发action来达到更新state(即更新数据仓库)的操作。 components 通用组件(纯组件),通常是一些复用性很强的组件,不需要连接数据仓库。通过props传递数据,父组件传递相同的props,会渲染相同的页面。不做任何业务上的处理。 services 所有的请求都写在这里,官方已经为我们封装了request方法,我们只需要传入参数即可。 开发流程开发流程因人而异,这只是我个人的习惯 定义路由。之前版本的菜单和路由配置在一个文件,现在已经分离开来。 定义model数据仓库:命名空间 定义前端请求,在services中 在routers中开发业务组件,并连接到model 根据需要在components中封装可复用的纯组件 在routers中使用state并通过dispatch派发action到model来修改state 参考antd-pro官方文档","categories":[{"name":"coder","slug":"coder","permalink":"https://jx-zyf.github.io/categories/coder/"}],"tags":[{"name":"JavaScript","slug":"JavaScript","permalink":"https://jx-zyf.github.io/tags/JavaScript/"},{"name":"antd","slug":"antd","permalink":"https://jx-zyf.github.io/tags/antd/"}]},{"title":"async/await笔记","slug":"async-await","date":"un55fin55","updated":"un55fin55","comments":true,"path":"/posts/ec020e93/","link":"","permalink":"https://jx-zyf.github.io/posts/ec020e93/","excerpt":"","text":"异步是JavaScript的一大难点之一,因此js的异步解决方案也是多种多样的。从最早的回调函数,到Promise对象,到Generator函数,再到现在的async/await。很多人都认为async/await是异步操作的终极解决方案,今天就来简单介绍下它。 async/await 这名字取得就很语义化,async声明一个异步function,await用于等待异步function执行完成。并且语法规定,await只能出现在async函数中。 aysncasync到底做了什么? 我们看一下下面的代码12345async function test() { return 'hello world'}console.log(test()) 我们来看看打印出了什么1Promise { 'hello world' } 没错,它返回了一个Promise对象。从文档中我们知道await用于等待一个Promise对象。但是在上面的例子中直接返回了一个字符串,它不是Promise对象,async函数会通过Promise.resolve()把它封装成一个Promise对象。 我们回顾一下Promise的特点——无等待。如果这个async函数没有await的话,它会立即执行返回一个Promise对象,不会阻塞后面代码的运行。 await我们知道await是在等待一个async函数完成,而async函数会返回一个Promise对象,因此await表达式的运算结果就是这个Promise对象resolve()的值。 继续看个例子:123456789101112131415161718192021function test1() { return 'test'}function testAsync(n) { return new Promise((resolve, reject) => { setTimeout(() => { resolve(n) }, 1000) })}async function test() { const result1 = await test1() console.log(result1) const result2 = await testAsync(999) console.log(result2)}test()console.log('同步代码') 上述代码首先打出了”同步代码”,随后又立即打出”test”,并在一秒后打出了”999”。我们可以得出以下结论: await会阻塞它所在的异步函数的后面代码的执行(因为它需要等到testAsync函数的结果才会往下执行),但是不会阻塞异步函数之外的代码的执行(因为先打印的”同步代码”),其实它内部的阻塞都被封装到一个Promise对象中异步执行,我想可能是因为这样await才必须在async函数中吧。 如果await等到的不是Promise对象的话会将它转成立即resolve的Promise对象,并将该值作为await表达式的结果(打印完”同步代码”之后立即打出”test”);如果是的话就等着Promise对象resolve(),然后将resolve的结果作为await表达式的结果(一秒后打印”999”)。 在async函数中,这样的代码看起来就像是同步代码,await等待到结果才执行后面的语句,但是却不会阻塞async函数之外的代码的执行。 async/await刚开始可能会感觉async/await就是将Promise中的then变成了用await的方式书写,好像并没有什么优势。 我们先举个栗子:假设一个功能需要多个步骤完成,并且每个步骤都是异步的,而且后面的步骤依赖于前面步骤的结果12345678910111213141516171819202122function asycnTest(time) { return new Promise(resolve => { setTimeout(() => { resolve(time + 500) }, time) })}function step1(time) { console.log(`step1用时${time}`) return asycnTest(time)}function step2(time) { console.log(`step2用时${time}`) return asycnTest(time)}function step3(time) { console.log(`step3用时${time}`) return asycnTest(time)} 先看看用Promise要怎么写:1234567891011121314151617function promiseDoIt() { console.time('promiseDoIt') step1(1000) .then(time2 => step2(time2)) .then(time3 => step3(time3)) .then(result => { console.log(`结果是${result}`) console.timeEnd('promiseDoIt') })}promiseDoIt()// step1用时1000// step2用时1500// step3用时2000// 结果是2500// promiseDoIt: 4507.135ms 上述三个步骤一共用时1000+1500+2000共4500ms,和console.time()/console.timeEnd()运算结果一致。如果用async/await来写会怎么样呢?123456789101112131415async function asyncDoIt() { console.time('asyncDoIt') const time2 = await step1(1000) const time3 = await step2(time2) const result = await step3(time3) console.log(`结果是${result}`) console.timeEnd('asyncDoIt')}asyncDoIt()// step1用时1000// step2用时1500// step3用时2000// 结果是2500// asyncDoIt: 4504.294ms 运行结果都是一样的,但是这看起来就像同步代码,非常清晰。再改一下需求,如果后面步骤依赖前面每一个步骤的结果,又该怎么改呢?反正用async/await修改是很方便的,只需要将前面步骤的结果当参数传递进去就行了,至于Promise的写法,好像就有点麻烦了… 另外,Promise对象不会一直是resolve啊,也会reject的,所以需要将await放在try…catch代码块中对错误进行处理 123456789101112131415async function asyncTest() { try { await Promise.reject('出错了') } catch (err) { console.log(err); }}// 或者直接使用Promise的catchasync function asyncTest() { await Promise.reject('出错了').catch(err => { console.log(err) });} 这样才不会阻塞代码的正常运行 今天就先到这吧,以后有了新的理解再补充","categories":[{"name":"coder","slug":"coder","permalink":"https://jx-zyf.github.io/categories/coder/"}],"tags":[{"name":"JavaScript","slug":"JavaScript","permalink":"https://jx-zyf.github.io/tags/JavaScript/"}]},{"title":"css预处理器——less的常见用法","slug":"less的常见用法","date":"un22fin22","updated":"un22fin22","comments":true,"path":"/posts/356ff799/","link":"","permalink":"https://jx-zyf.github.io/posts/356ff799/","excerpt":"","text":"写在开头为什么要有CSS预处理器CSS作为前端开发必不可少的一个环节,然而很多开发者的却并不喜欢写CSS代码。这是因为它并不像其他编程语言一样,有自己的变量,常量等;它只是一行行单纯的属性描述,书写起来很需要耐心,而且代码难以维护。于是有人开始就想着能不能给CSS加入一些编程元素,让CSS能像其他编程语言一样可以做一些预定的处理,于是就有了“CSS预处理器” 什么是CSS预处理器CSS预处理器定义了一种新的语言,目的是为了用一种专门的编程语言,为CSS增加一些编程的特性,而不是作为解释型语言。这样,我们可以在CSS中使用变量,简单的逻辑程序,函数等编程特性,让我们的CSS更容易书写和维护,可读性也更强。 目前,CSS预处理器技术已经很成熟,出现了很多不同的CSS预处理器语言,其中最受欢迎的就是Sass,Less和Stylus了。今天重点讲讲Less的常见用法 本文参考Less中文文档 Less的介绍Less是一款比较流行预处理CSS,支持变量,混合,嵌套,运算,函数等功能 官网中文文档 常见用法定义变量我们可以把重复使用或经常修改的值定义为变量,在需要使用的地方引用这个变量即可。这样书写方便并且便于维护。12345@bgColor: #fff;body { background-color: @bgColor;} 编译为123body { background-color: #fff;} 我们还可以用变量表示选择器名,绝对路径等12345678@my-selector: banner;@images: '../images'.@{my-selector} { color: #000; background-image: url('@{images}/bg-img.png'); /* ... */} 编译为1234.banner { color: #000; background-color: url('../images/bg-img.png');} 这样,当选择器发生改变时我们只需要更改变量定义即可,当绝对路径的url很长时用变量书写也是很不错的选择 混合混合的作用就是将重复的代码放到一个类中,我们每次使用时只需要引用类名即可 定义一个类12345.borderRadius(@radius: 5px) { border-radius: @radius; -moz-border-radius: @radius; -webkit-border-radius: @radius;} 上面代码中这个类定义了一个参数,默认值是5px 使用这个类123456.content: { .borderRadius;}.footer: { .borderRadius(10px);} 嵌套写CSS最烦的应该就是选择器很长很长,比如说:123456789101112.header { width: 80vw;}.header>.title { font-size: 20px;}.header>.title a { color: #fff;}.header>.title a:hover { color: lightblue;} 上面的代码相对实际开发来说嵌套的不是很多,但是依旧很繁琐,如果用less,就比较简洁了123456789101112.header { width: 80vw; >.title { font-size: 20px; a { color: #fff; &:hover { color: lightblue; } } }} 书写起来也比较快 运算less还提供我们常用的加减乘除的运算123456789101112131415161718// 能转换单位并且有意义的@conversion-1: 5cm + 10mm; // result is 6cm@conversion-2: 2 - 3cm - 5mm; // result is -1.5cm// 不能转换单位的@incompatible-units: 2 + 5px - 3cm; // result is 4px// 带变量的运算@base: 5%;@filler: @base * 2; // result is 10%@other: @base + @filler; // result is 15%// 乘法和除法不能转换,在大多数情况下都是没有意义的,比如长度乘长度得到面积(一块区域),而CSS不支持区域,所以得到的依旧是一维的单位@base: 2cm * 3mm; // result is 6cm// 颜色的数学运算@color: #224488 / 2; //results in #112244background-color: #112244 + #111; // result is #223355 calc()特例为了与 CSS 保持兼容,calc() 并不对数学表达式进行计算,但是在嵌套函数中会计算变量和数学公式的值。 12@var: 50vh/2;width: calc(50% + (@var - 20px)); // result is calc(50% + (25vh - 20px)) Escaping转译:我们可以使用任意字符串作为属性或者变量值123456@min768: ~\"(min-width: 768px)\";.element { @media @min768 { font-size: 1.2rem; }} 函数Less内置了很多函数,用于转换颜色、处理字符串、算数运算等,具体参考函数手册 这里讲一下 lighten 和 darken 这两个内置函数1234body { background-color: lighten(#000, 10%); // 让黑色变亮 10% color: darken(#fff, 10%); // 让白色变暗 10%} 注释less文件中有两种注释 第一种 1// 这种注释转换成css后将会删除 第二章 1/* CSS注释语法,转换成css后仍然保留 */ Import在开发过程中,我们常常将不同的样式放到多个文件中,最后通过@import的方式导入。 写在最后最终浏览器认识的还只能是css代码,所以我们要把less文件转译成css文件,我用的最多的是使用webpack。 使用方法","categories":[{"name":"coder","slug":"coder","permalink":"https://jx-zyf.github.io/categories/coder/"}],"tags":[{"name":"css","slug":"css","permalink":"https://jx-zyf.github.io/tags/css/"}]},{"title":"react 简单优化","slug":"react简单优化","date":"un22fin22","updated":"un22fin22","comments":true,"path":"/posts/d5323688/","link":"","permalink":"https://jx-zyf.github.io/posts/d5323688/","excerpt":"","text":"在url后加 ?react_pref 可以结合Chrome自带的Performance做性能测试 单组件优化:核心:减少render函数执行次数 事件绑定: 在constructor中使用bind绑定: 1234constructor(props) { super(props) this.eventHandle = this.eventHandle.bind(this)} 优点:只需要绑定一次,并且只会执行一次; 缺点:即使不需要state也需要添加constructor来绑定,代码量多一点 在render中使用bind绑定: 1234567// ...render() { return ( <button onClick={this.clickHandle.bind(this)}></button> )}// ... 优点:写法简单; 缺点:每次执行render都会重新执行一次,多次使用同一个函数需要绑定多次(不推荐使用该方法) 在render中使用箭头函数绑定: 1234567// ...render() { return ( <button onClick={() => this.clickHandle()}></button> )}// ... 优点:写法简单,相对于上一种方法性能稍好一点; 缺点:每次执行render都要重新生成一个函数 在函数初始化时使用箭头函数绑定(实验性): 12345678910// ...clickHandle = () => { // ...}render() { return ( <button onClick={this.clickHandle}></button> )}// ... 优点:写法简单,结合了以上三种方法的优点; 缺点:目前是实验性的语法,需要babel转译 总结:方式一是官方推荐的方式,也是性能最好的方式;方法二和方法三有性能问题,并且当方法作为属性传递给子组件时会触发re-render,因为子组件的props发生了改变;方法四是最好的绑定方式,但是需要结合babel转译 属性传递:由于js的特性(对象在内存中的存储),如果传递的是一个对象,尽量提前声明一个索引指向该对象,避免每次render都重新在内存中生成一个新的对象。 多组件优化:核心:减少子组件render函数的执行次数(减少子组件的重渲) 定制子组件的shouldComponentUpdate当父组件发生了render,子组件都会重新渲染。很多时候子组件并没有发生任何改变,它接收到父组件传给它的props并没有发生变化,自身的state也没有变化,这种情况下子组件的无脑重新render就可以在其shouldComponentUpdate()钩子函数里做一些操作。这个钩子函数默认返回true,即render会执行;我们在里面做些业务上的检测,再返回true或false来确定子组件该不该重新渲染。 使用React.PureComponent(React@15.3新增)它类似于React.Component,区别在于PurComponent简单的实现了shouldComponentUpdate()而Component没有实现。适用情况:组件的render函数在给定相同的props和state时渲染相同的结果(纯组件),可以提升性能。 Note(官网上的Note)React.PureComponent’s shouldComponentUpdate() only shallowly compares the objects. If these contain complex data structures, it may produce false-negatives for deeper differences. Only extend PureComponent when you expect to have simple props and state, or use forceUpdate() when you know deep data structures have changed. Or, consider using immutable objects to facilitate fast comparisons of nested data.Furthermore, React.PureComponent’s shouldComponentUpdate() skips prop updates for the whole component subtree. Make sure all the children components are also “pure”. PureComponent的shouldComponent()只实现了浅对比,深对比太消耗性能(对象的深比较需要用到递归,复杂度太高,性能消耗太大)。 可以使用immutable这个库来进行对象的深比较,复杂度比较低,但是这个库比较大。使用方法参考文档 redux优化当我们页面使用的数据需要对获取的redux中的数据进行一些处理(复杂的计算)才能使用,并且很多状态是经常切换得时候,我们可以将那些状态缓存起来,避免每次切换都重新计算。可以使用react官方推荐一个库——reselect,使用方法参考文档 列表类组件优化当我们渲染一个列表时,必须给每一项传key,否则react会报一个警告。这个key不建议使用数组遍历时的index,因为它只能帮助我们消除警告而已,并没有任何实际意义。如果我们在数组前面添加一个元素,那么react会认为所有的元素都发生了改变,所以全部都重新渲染了,我们并没有感受到虚拟DOM带给我们的优化。所以key的值必须是唯一的才有意义。 推荐阅读react组件性能优化探索实践高性能 React 组件","categories":[{"name":"coder","slug":"coder","permalink":"https://jx-zyf.github.io/categories/coder/"}],"tags":[{"name":"JavaScript","slug":"JavaScript","permalink":"https://jx-zyf.github.io/tags/JavaScript/"},{"name":"react","slug":"react","permalink":"https://jx-zyf.github.io/tags/react/"}]},{"title":"react 基础笔记","slug":"react基础","date":"un55fin55","updated":"un55fin55","comments":true,"path":"/posts/cb30c8d6/","link":"","permalink":"https://jx-zyf.github.io/posts/cb30c8d6/","excerpt":"","text":"以下仅是我个人一些笔记合集,如理解有误,还望帮忙及时指出,方便及时更正。 react 生命周期mounte(首次渲染) constructor(): 构造函数 如果组件中存在constructor,则必须调用super(),如果不执行super,this将无法初始化; 如果只是单纯的调用super,而没有传props,那么在constructor内就无法调用this.props,但是在组件其他地方依然可以调用this.props; 不能在里面调用setState(),会报一个警告Warning: setState(…): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op 总结:有constructor,就必须有super();constructor中不需要访问this.props时可以不传props。 componentWillMount(): 组件挂载之前被调用 由于它在render之前被调用,所以在这里setState()之后是可以在render中体现的,并且只渲染一次,不会导致重绘。 render():组件渲染,必须有return,返回一个jsx或者null。 componentDidMount():组件挂载之后立即被调用 在这里可以做一些数据请求,DOM初始化等操作,也可以setState(),不过会重新渲染,即再次调用render。 update(props或state发生改变) componentWillReceiveProps(nextProps): 已挂载组件收到新的props时调用 当父组件发生render时,不管props有没有发生改变,子组件都会触发该函数。所以使用这个钩子函数时,记得比较当前props和nextProps 可以在这里setState() shouldComponentUpdate(nextProps, nextState): 是否应该更新组件,默认更新 当组件收到新的props或state时,在render之前调用,默认返回true。我们可以定制这个钩子函数,根据业务返回true或false,可以在这里做优化 如果返回false,那么之后的componentWillUpdate(),render(),componentDidUpdate()都不会调用,但是这只能阻止当前组件的render,并不能阻止子组件的render 当你调用了forceUpdate()时,React会调用render()并忽略shouldComponentUpdate() componentWillUpdate(): props或state改变之后,render之前调用 不能在这里setState(),因为它本身就是state改变之后调用的,如果再在里面setState()将会导致无限循环。如果需要更新state,官方建议使用componentWillReceiveProps()代替 render(): 同上 componentDidUpdate(): 组件更新后立即调用 在这里可以操作DOM,发请求等,也可以setState() unmount(卸载) componentWillUnmount(): 组件卸载之前调用 在这里适合做一些收尾操作,比如清理定时器,取消网络请求,解绑DOM等 不可以setState() render()是react组件中必要的一个函数,其他钩子都不是必须的,记住:千万不能在render中setState() 触发render有以下途径: 初次渲染 state发生改变(并不是setState()一次就render一次,多次setState可能会合并) 父组件更新,即使props没有发生改变,或者父子组件没有数据交互 调用this.forceUpdate() (还没试过这种方法) jsxreact内部执行了 React.createElement(type, config, children) type(类型:String) 是必须的:可以是HTML标签名,也可以是ReactClass config(类型:Object)非必须:该元素的属性配置,比如className,id或者自定义属性 children(可以一个一个传,也可以在一个数组里一次性传,数组的话需要key)非必须:该元素的子元素,如果又是一个元素节点(非文本节点),会一直createElement 下去,形成一颗树 setState()异步更新组件状态,将需要更新的状态更新到一个队列里面,并不是setState()一次就render一次,多次setState可能会合并,不要在render函数,componentwillUpdate()和componentWillUnMount()里使用setState 它接收两个参数: 第一个参数是一个函数updater,它返回一个对象,就是我们平常书写的对象。updater函数接收两个参数:prevState和props; 第二个参数是[callback]:可选的 当然,第一个参数也可以直接写成一个对象(大部分时间我们是这样写的),但是如果后面的状态取决于前一个状态,建议写成函数参数的形式,因为函数参数可以拿到之前的状态。 虚拟DOM目前我所理解的就是 用js对象去模拟DOM树,然后用这个对象构造真正的DOM树,并且插入到文档; 当状态发生改变的时候重新构造一颗新的js对象树,然后用新的对象树与旧的树对比(diff算法),记录不同的地方。(只在同级对比,如果完全对比复杂度太高了),具体关于算法的我就不是很清楚了…深度优先,列表对比(因为这个所以我们遍历数组生成jsx时需要传key)什么的,,总之很牛逼就是了。 旧的树和实际的DOM树是完全相同的,所以对上面找出的差异进行实际的DOM操作(patch)。只对有差异的地方进行操作 总之应该就是js运行很快,DOM操作太慢了,所以才有的虚拟DOM吧。。。 context可以看做全局变量.如果需要跨很多组件传递props,并且中间的组件并不需要这个参数,只是充当一个传递的作用,就可以使用context传递。(当然你也可以使用简单粗暴的props层层传递,不过这样会损耗性能,并且代码也不易于维护)由于全局变量在开发中是很不受欢迎的,所以使用context需要严格的校验。我们使用PropTypes来做验证,早期版本PropTypes是集成在react库中的,现在的版本为了让react更轻量级,将它抽离出来,放在prop-types这个库中,这也是react官方的库。使用: 在父组件中使用getChildContext()函数,它返回你所需要传递的属性; 123getChildContext() { return { user: this.state.user }} 并且在父组件中要声明你所传递属性的类型;(在getChildContext中返回了哪些属性,这里必须全部声明,否则会报错) 123static childContextTypes = { user: PropTypes.string} 然后在你需要使用该属性的子组件中声明:(只有这里声明了才能用this.context获取到) 123static contextTypes = { user: PropTypes.string} 我个人觉得我们应该很少会使用到context吧,一般项目中都会使用redux管理我们的全局状态,不过了解一下还是很有必要的,说不定react-redux的实现就是依靠context呢!虽然我没有看过源码。 先这样吧,溜了溜了","categories":[{"name":"coder","slug":"coder","permalink":"https://jx-zyf.github.io/categories/coder/"}],"tags":[{"name":"JavaScript","slug":"JavaScript","permalink":"https://jx-zyf.github.io/tags/JavaScript/"},{"name":"react","slug":"react","permalink":"https://jx-zyf.github.io/tags/react/"}]},{"title":"浅谈JS运行机制","slug":"js运行机制","date":"un22fin22","updated":"un33fin33","comments":true,"path":"/posts/338a161/","link":"","permalink":"https://jx-zyf.github.io/posts/338a161/","excerpt":"","text":"本文仅是我个人的见解,如理解有误,还望帮忙及时指出,方便及时更正。 正文开始~ 进程与线程这里先贴上阮大神的文章:进程与线程的一个简单解释 我是这样理解的: 一个进程就好比工厂的一个车间,一个线程就好比车间里的一个工人,对应一个进程由一个或多个线程组成 一个车间有它的独立资源,对应系统分配的独立内存 每一个车间是相互独立的,对应每个进程之间是相互独立的 每个车间有一或多个工人协同完成任务,对应多个线程在进程中协同完成任务 每个车间的空间是工人们共享的,对应一个进程的内存空间是每个线程都可以共享 车间里的房间大小不一,能容纳的工人数也不一样,当别的工人占用该房间时,其他人就不能使用。对应当一个线程使用某些内存时,其他线程必须等它结束,才能使用这块内存 为了防止多个线程之间产生冲突,就有了很多协调机制,这里就不赘述了 最后,再用官方的话解释下: 进程是cpu资源分配的最小单位(是能拥有资源和独立运行的最小单位) 线程是cpu调度的最小单位(线程是建立在进程的基础上的一次程序运行单位,一个进程中可以有多个线程) 浏览器是多进程的稍微深入了解了进程与线程后,就得对我们js最初运行的环境——浏览器——有点新的认识了。 浏览器之所以可以运行,是因为操作系统给它分配了CPU和内存 浏览器是多进程的 一般来说,一个标签页就是一个独立的浏览器进程 上张图: 从上图我们可以看出,浏览器是多进程的。另外,由于浏览器的优化,有些进程会合并,所以一个便签页对应一个进程并不是绝对的。 浏览器多进程有很多好处,比如当我们打开很多个网页,就相当于打开了多个进程,其中一个网页的卡顿不会对别的网页造成影响,让用户的体验更佳。 浏览器内核(渲染进程)终于到了重点!前面讲了那么多进程,然而对于前端开发工作人员,最重要的就是渲染进程 js的执行,页面的渲染等操作都在这个进程中进行,而它是多线程的 一个浏览器内核通常包括以下线程: GUI 渲染线程 负责页面的渲染,包括重绘 它与JS引擎线程是互斥的,当JS引擎执行时GUI渲染线程会被挂起(相当于被冻结了),GUI更新会被保存在一个队列中直到JS引擎空闲时立即被执行 JavaScript引擎线程 负责处理js程序,运行js代码 同样,它与GUI渲染引擎也是互斥的,所以当js代码执行时间过长时,就会造成页面阻塞 定时触发器线程 setTimeout和setInterval所在的线程 浏览器定时计数器并不是由JS引擎计数的,因为JS引擎是单线程的,如果处于阻塞线程状态就会影响记计时的准确,因此通过单独线程来计时并触发定时是更为合理的方案 事件触发线程 当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理。这些事件可以是当前执行的代码块如定时任务、也可来自浏览器内核的其他线程如鼠标点击、AJAX异步请求等,但由于JS的单线程关系所有这些事件都得排队等待JS引擎处理 异步http请求线程 在XMLHttpRequest在连接后是通过浏览器新开一个线程请求,当检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件放到JS引擎的处理队列中等待处理 执行栈与任务队列事件循环javascript是单线程的,这是由于它的诞生就是浏览器脚本语言,就是为了与用户交互以及操作DOM。如果它是多线程的话,当多个线程同时操作DOM时,浏览器应该以哪个线程为准呢?所以,它生来就是单线程。 当javascript代码执行的时候会将不同的变量存于内存中的不同位置:堆(heap)和栈(stack)中来加以区分。其中,堆里存放着一些对象;而栈中则存放着一些基础类型变量以及对象的索引。我们这里说的执行栈和上面这个栈的意义却有些不同。我们知道,当我们调用一个方法的时候,js会生成一个与这个方法对应的执行环境(context),也叫执行上下文。这个执行环境中存在着这个方法的私有作用域,上层作用域的指向,方法的参数,这个作用域中定义的变量以及这个作用域的this对象。而当一系列方法被依次调用的时候,因为js是单线程的,同一时间只能执行一个方法,于是这些方法被排队在一个单独的地方。这个地方被称为执行栈。 单线程就意味着在同一时间只有一个任务能被执行,所有的任务需要排队,只有前一个任务执行完毕后面的任务才能被执行。如果因为计算量比较大,CPU忙不过来,也还能忍受;可是大部分情况下CPU是空闲的,比如ajax读取数据很慢,必须等结果出来才继续往下执行。这样性能就很低,于是便有了同步任务和异步任务。同步任务是指在主线程上排队的任务,当前一个任务执行完毕就会执行后一个任务;异步任务是指不进入主线程,而是进入“任务队列”,只有“任务队列”通知主线程某个异步任务可以执行了,它才会进入主线程。 可总结如下: 所有同步任务都在主线程上执行,形成执行栈 所有异步任务都在“任务队列”上,只要异步任务有了结果,就在异步任务中添加一个事件 当执行栈中所有任务都执行完毕,主线程处于闲置状态时,从“任务队列”的队首读取事件加入到执行栈执行 一直循环以上步骤,这个过程就叫做“事件循环(Event Loop)” 下图可以很好的展示这个情况: 图中的stack就是我们说的执行栈,WebAPIs代表一些异步事件,callback queue代表事件队列。 micro task 和 macro task我们先看一段代码:12345678910111213console.log('script start');setTimeout(function() { console.log('setTimeout');}, 0);Promise.resolve().then(function() { console.log('promise1');}).then(function() { console.log('promise2');});console.log('script end'); 以上代码的执行结果是什么呢?不卖关子了,直接上结果吧 12345script startscript endpromise1promise2setTimeout 你答对了吗?没答对也不要紧,下面我们来分析一下 之前介绍的事件循环只是大概的一个过程,实际上不同的异步任务,它们的执行优先级也不一样。异步任务分为两类:微任务(micro task)和宏任务(macro task) macro task:每次执行栈执行的代码就是一个宏任务 主代码块,setTimeout,setInterval等 micro task:当前宏任务执行结束后立即执行的任务 Promise,MutaionObserver,process.nextTick等 我们知道,在一个事件循环中,异步事件返回结果后会添加一个事件到任务队列。实际上,会根据这个异步事件的类型,会被添加到对应的宏任务队列或者微任务队列上。当执行栈为空时,主线程会查看微任务队列是否有事件存在。如果不存在,再去宏任务队列取出事件加入到当前执行栈;如果存在,则依次执行队列中的事件,直到队列为空,再去执行宏任务队列中的事件。我们只需要记住:微任务队列中的事件优先级大于宏任务队列,微任务永远在宏任务之前执行 定时器前面我们说过定时器(setTimeout,setTimeInterval)并不是由JS引擎计数的,而是在由单独线程计数。定时器功能主要由setTimeout() 和 setInterval()两个函数来完成,这两个函数内部运行机制完全一样,唯一的区别在于前者指定的代码只执行一次,而后者反复执行。这里主要用前者举例。 12setTimeout(() => {console.log(1)}, 0);console.log(2); 以上代码的执行结果永远是2,1;因为第二行代码是同步任务,在主线程中,而第一行是异步任务,在任务队列(宏任务队列)中;只有主线程任务全部执行完毕后才会执行任务队列中的回调。setTimeout(fn, 0)就意味着fn会尽可能早的执行,但它永远在同步任务、微任务队列以及现有的宏任务队列(已经完成的异步任务)之后才会执行。 HTML5标准规定了setTimeout()的第二个参数的最小值(最短间隔),不得低于4毫秒,如果低于这个值,就会自动增加。在此之前,老版本的浏览器都将最短间隔设为10毫秒。 注意:setTimeout()只是将事件插入了”任务队列”,必须等到当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数。要是当前代码耗时很长,有可能要等很久,所以并没有办法保证,回调函数一定会在setTimeout()指定的时间执行。 到这里,我们再看一下这段代码:12345678910111213console.log('script start');setTimeout(function() { console.log('setTimeout');}, 0);Promise.resolve().then(function() { console.log('promise1');}).then(function() { console.log('promise2');});console.log('script end'); 现在就可以解释上面代码的执行结果了。 参考资料JavaScript 运行机制详解:再谈Event Loop Tasks, microtasks, queues and schedules","categories":[{"name":"coder","slug":"coder","permalink":"https://jx-zyf.github.io/categories/coder/"}],"tags":[{"name":"JavaScript","slug":"JavaScript","permalink":"https://jx-zyf.github.io/tags/JavaScript/"}]},{"title":"vue-music总结","slug":"vue-music总结","date":"un55fin55","updated":"un22fin22","comments":true,"path":"/posts/461d16ef/","link":"","permalink":"https://jx-zyf.github.io/posts/461d16ef/","excerpt":"","text":"由于本人比较喜欢听歌,所以这次跟着慕课网的实战课程做了这个项目。视频地址:Vue 2.0 高级实战-开发移动端音乐 WebApp 概述基于vue.js的音乐播放器,数据来源于QQ音乐。大部分接口都是JSONP,抓取较容易,其中有些接口限制了host,需要设置header,所以自己编写接口转发请求来绕过host的限制。具体代码参考build目录下dev-server.js 或者 prod.server.js。使用的技术栈:vue2.0+vuex+vue-router;另外使用了很多第三方插件。 启动项目源码12npm inpm start 效果预览 src目录─src │ App.vue │ main.js // 打包入口 ├─api // 数据抓取 ├─base // 基础可复用组件 ├─common // 共用字体,图片,js方法,样式等 ├─components // 业务组件 ├─router // 路由 └─store // 状态管理 数据处理由于很多JSONP请求,这里使用了一个第三方插件jsonp,这个插件使用很简单,有兴趣的可以学习一下。使用时,将请求的参数拼接到url上,调用他的jsonp方法即可。我们对请求回来的数据做了Promise化,方便我们后面使用数据。代码如下:1234567891011121314151617181920212223242526import originJSONP from 'jsonp'export default function jsonp(url, data, option) { let subUrl = param(data).length > 0 ? `?${param(data)}` : '' url += subUrl return new Promise((resolve, reject) => { originJSONP(url, option, (err, data) => { if(!err) { resolve(data) } else { reject(err) } }) })}function param(data) { let url = '' for(let p in data) { let value = data[p] === undefined ? '' : data[p] url += `&${p}=${encodeURIComponent(value)}` } url = url.length > 0 ? url.substring(1) : '' return url} 播放器内核是这个项目的重点,vuex的设计非常重要,这里设计好了,后面的代码书写起来才更轻松。123456789101112singer: {}, // 当前歌手playing: false, // 播放状态fullScreen: false, // 播放器状态(全屏/迷你)playlist: [], // 播放列表(不同的播放模式不一样)sequenceList: [], // 播放列表(任何模式都一样)mode: playMode.sequence, // 播放模式(循环播放,单曲循环,随机播放)currentIndex: -1, // 当前歌曲在列表中的索引disc: {}, // 当前歌单topList: {}, // 当前排行榜searchHistory: loadSearch(),// 搜索历史playHistory: loadPlay(), // 播放历史likeList: loadLike() // 收藏列表 细节1.去除移动端点击事件300ms延迟,也是用的第三方插件,fastclick,文档在这里1fastclick.attach(document.body) 2.图片懒加载:项目中使用的是vue-lazyload,使用方法参考文档,代码如下:12345Vue.use(VueLazyload, { preLoad: 1.3, loading: require('common/image/default.jpg'), attempt: 1}) 然后在需要使用图片懒加载的地方使用v-lazy替代:src即可 3.项目中有很多业务逻辑多个组件是可以共用的,所以使用了mixins,具体可以参考官方文档 混合 (mixins) 是一种分发 Vue 组件中可复用功能的非常灵活的方式。混合对象可以包含任意组件选项。以组件使用混合对象时,所有混合对象的选项将被混入该组件本身的选项。 可以把它看做一个组件,它也是一个对象,可以把各个组件中的共用代码写在里面,然后在组件中引入即可。 4.项目中使用了很多audio的API,比如 timeupdate,play,ended 等。这都大大方便了我们的开发,比如 timeupdate 可以然我们准确得到当前播放的时间,进度条的实现就依赖这个方法。另外还要注意的是这里没有使用 canplay 而是使用了 play。我们设置了一个标志位songReady,当它为true的时候才能切换歌曲,如果我们将它设为true的时间是canplay的话,快速切换歌曲后点击暂停的话vuex中的状态虽然已经改变,但歌曲并没有停止播放。所以这里使用了play,只有当歌曲确实播放了才会将songReady置为true,这样即使点击再快也不会出现问题。 5.项目作者为了更好的交互体验,很多地方用到了滚动,上拉加载更多等。这里面用到了作者自己写的一个第三方库,better-scroll,确实很好用。很多地方也用到了vue的 transition 动画,比如页面之间的切换,播放器的打开和缩放到迷你播放器,这个动画比较好玩,用到了 transition 组件的几个事件:enter,after-enter,leave,after-leave。具体实现参考官方文档 6.路由懒加载:直接上文档吧!简单粗暴 7…… 最后确实用了好多插件啊! 在这个项目中学到了很多,再次感受到组件化,模块化开发的方便,也遇到了很多问题,最后都解决了。继续爬行吧!少年!","categories":[{"name":"coder","slug":"coder","permalink":"https://jx-zyf.github.io/categories/coder/"}],"tags":[{"name":"JavaScript","slug":"JavaScript","permalink":"https://jx-zyf.github.io/tags/JavaScript/"},{"name":"vue","slug":"vue","permalink":"https://jx-zyf.github.io/tags/vue/"}]},{"title":"react 实战总结","slug":"react-demo项目总结","date":"un22fin22","updated":"un55fin55","comments":true,"path":"/posts/68a33ea4/","link":"","permalink":"https://jx-zyf.github.io/posts/68a33ea4/","excerpt":"","text":"虽然不是第一次接触react,但之前都没有写博客记录,好记性不如烂笔头,希望以后养成多做笔记的好习惯吧。 关于react react 作为当下最流行的前端框架,之所以能被如此多前端开发者所喜爱自然是有原因的。react有一个很重要的特性——限制(Constraint),这里的“限制”并不是贬义,因为对于大部分开发者来说,代码的可维护性和扩展性并不是很好,所以只有施加“限制”,代码的管理才会更加简单。必须按照react的规范来写代码,不然程序就跑不了,就是这么霸道。 react 还有很多优势,个人认为最重要的就是虚拟DOM和组件化。 虚拟DOM:现在的框架很少有直接操作DOM的,虽然jQuery曾经称霸一方。当页面上的DOM树改变的时候,虚拟DOM机制会将前后DOM树进行对比(通过一些高效的算法),如果两个DOM树有不一样的地方,react只会针对不一样的地方进行修改,自然就提高了性能。举个栗子:现在的html结构是这样 12345<ul> <li>1</li> <li>2</li> <li>3</li></ul> 你想让它变成这样123456<ul> <li>4</li> <li>5</li> <li>6</li> <li>7</li></ul> 如果是原始的DOM操作,你需要先将开始的三个li删除,然后再将后面的四个li添加进去;可能你也会这样操作:将三个li的innerHTML修改,只需要添加一个7就行了。那么恭喜你,做的很好。react有个很重要的思想,状态管理。只要DOM节点的对应的state发生了改变,react就会将其标注为“脏状态”,在一个Event loop结束后,对这些有标注的地方进行修改,并且只对修改了的地方重新渲染,减少了很多冗余的DOM操作。具体实现可以参考 深度剖析:如何实现一个 Virtual DOM 算法 组件化:简单的说下好处,可以模板化,可复用,可嵌套的UI模块,当然组件之间会存在依赖关系。可以将UI组件当作独立的js模块使用,推荐使用ES6的import来引用组件模块,当然也可以配合CommonJs、AMD、CMD等规范来require我们需要的组件模块,最重要的是处理好依赖关系。 项目前面说了这么多,终于到重点了。这个项目是参照慕课网实战视频做的,视频地址:React高级实战 打造大众点评 WebApp 启动: 123npm inpm run mocknpm start 项目预览 项目源码 项目目录 123456789101112131415161718192021222324252627282930313233343536373839404142│ package.json // 配置文件│ README.md├─build // 打包后的文件│├─config // 配置文件│├─mock // mock数据│ │ server.js // 请求数据接口│ ├─detail│ ├─home│ ├─public│ ├─search│ └─user│├─public│├─scripts│ build.js // 项目打包入口│ start.js // 项目启动入口│ test.js│└─src │ config.js // 常量配置 │ index.js // 程序入口 │ index.less │ ├─actions // actions │ ├─components // 木偶组件 │ ├─containers // 智能组件,与redux连接 │ ├─fetch // 获取数据 │ ├─reducers // reducers │ ├─router │ index.jsx // 前端路由 │ ├─static // 静态资源 │ └─util // 工具函数 webpack配置首先,项目采用 create-react-app 脚手架搭建,很多配置都已经帮我们写好了,但是仍需要自定义一些配置,所以我 npm run eject。对于这个命令,官方是这样解释的: Note: this is a one-way operation. Once you eject, you can’t go back! If you aren’t satisfied with the build tool and configuration choices, you can eject at any time. This command will remove the single build dependency from your project. Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except eject will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. You don’t have to ever use eject. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 这个项目我使用less编译css样式文件,所以需要添加配置:1234567891011121314151617181920212223242526272829303132333435{ test: /\\.(css|less)$/, use: [ require.resolve('style-loader'), { loader: require.resolve('css-loader'), options: { importLoaders: 1, }, }, { loader: require.resolve('postcss-loader'), options: { // Necessary for external CSS imports to work // https://github.com/facebookincubator/create-react-app/issues/2677 ident: 'postcss', plugins: () => [ require('postcss-flexbugs-fixes'), autoprefixer({ browsers: [ '>1%', 'last 4 versions', 'Firefox ESR', 'not ie < 9', // React doesn't support IE8 anyway ], flexbox: 'no-2009', }), ], }, }, { loader: require.resolve('less-loader') // compiles Less to CSS }, ],}, 在开发环境和产品环境都需要配置,即在webpack.config.dev.js和webpack.config.prod.js都需要配置 前端路由配置然后就是前端页面路由配置,视频使用的是react-router2.0的版本,我用的是4.0版本,接触过的都知道,这两者差异很大。 history 不用props传递,使用HashRouter Route 不能嵌套使用,都在同一层级 想要匹配所有不存在的路由,就不传path 动态参数 :param 可选参数 :param? 精确匹配 exact 代码如下:12345678910111213import { HashRouter, Route, Switch } from 'react-router-dom'<HashRouter> <Switch> <Route path='/' exact component={Home} /> <Route path='/city' component={City}/> <Route path='/search/:category/:keyword' component={Search}/> <Route path='/detail/:id' component={Detail}/> <Route path='/login' component={Login}/> <Route path='/user' component={User}/> <Route component={NotFound}/> </Switch></HashRouter> 服务器配置我这里使用的koa2.0来做后端接口的模拟 由于我们的数据是采用静态数据模拟的,所以请求数据其实就是请求js文件,并将数据返回 代码如下:123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129const Koa = require('koa')const Router = require('koa-router')const path = require('path')const static = require('koa-static')const cors = require('koa2-cors');const bodyParser = require('koa-body')();const app = new Koa()const router = new Router()// 跨域app.use(cors({ credentials: true, allowMethods: ['GET', 'POST', 'DELETE'], allowHeaders: ['Content-Type', 'Authorization', 'Accept'],}))// 访问静态资源app.use(static( path.join(__dirname, '/public')))// 主页广告router.get('/data/homead', async (ctx) => { ctx.body = require('./home/ad')})// 主页列表const homeListData = require('./home/list')router.get('/data/homelist/:cityname/:page', async (ctx) => { const pageSize = 1 const { cityname, page } = ctx.params let filterData = homeListData.filter(item => item.cityname === cityname ) let hasMore = pageSize * page >= filterData.length ? false : true ctx.body = { result: filterData.slice(0, page * pageSize), hasMore: hasMore }})// 搜索页列表const searchListData = require('./search/list')router.get('/data/search/:cityname/:category/:keyword/:page', async (ctx) => { const pageSize = 1 const { cityname, category, keyword, page } = ctx.params let filterData let filterData1 = searchListData.filter(item => item.cityname === cityname ) if (keyword === 'all') { filterData = filterData1.filter(item => item.category.includes(category) ) } else { filterData = filterData1.filter(item => item.title.includes(keyword) || item.subTitle.includes(keyword) ) } let hasMore = pageSize * page >= filterData.length ? false : true ctx.body = { result: filterData.slice(0, page * pageSize), hasMore: hasMore }})// 详情页 商家详情const detailData = require('./detail/info')router.get('/data/detail/info/:id', async (ctx) => { let { id } = ctx.params id = parseInt(id) const filterData = detailData.filter(item => item.id === id ) ctx.body = filterData})// 详情页 评论列表const CommentData = require('./detail/comment')router.get('/data/detail/comment/:id/:page', async (ctx) => { const pageSize = 2 const { id, page } = ctx.params let filterData = CommentData let hasMore = pageSize * page >= filterData.length ? false : true ctx.body = { result: filterData.slice(0, page * pageSize), hasMore: hasMore }})// 登录router.post('/data/login', bodyParser, async (ctx) => { const { username, password } = ctx.request.body if (username === 'linxun' && password === 'linxun') { ctx.body = { status: 1, msg: '登录成功' } } else { ctx.body = { status: 0, msg: '用户名或密码错误' } }})// 收藏router.get('/data/collect/:id', async (ctx) => { let { id } = ctx.params id = parseInt(id, 10) const filterData = detailData.filter(item => item.id === id ) ctx.body = filterData})// 订单const orderListData = require('./user/orderList')router.get('/data/orderList', async (ctx) => { ctx.body = orderListData})// 加载路由中间件app.use(router.routes()).use(router.allowedMethods())app.listen(8000, () => { console.log('route-use-middleware is starting at port 8000')}) 前端数据请求由于我们的数据是采用静态数据模拟的,所以请求数据其实就是请求js文件,并将数据返回 这里使用fetch和promise,在组件中只需要发送请求,然后在.then()中即可拿到需要的数据 代码如下:123456789101112131415161718192021222324252627282930import 'whatwg-fetch'import 'es6-promise'export function get(url) { return fetch(url, { headers: { 'Accept': 'application/json, text/plain, */*', } })}export function post(url, paramsObj) { return fetch(url, { method: 'POST', headers: { 'Accept': 'application/json, text/plain, */*', 'Content-Type': 'application/x-www-form-urlencoded' }, body: obj2params(paramsObj) });}function obj2params(obj) { let result = '', item; for (item in obj) { result += '&' + item + '=' + encodeURIComponent(obj[item]); } result = result.length > 0 ? result.slice(1) : result; return result;} react与redux连接在智能组件中 代码如下:12345678910function mapStateToProps(state) { return { ... } // 需要的仓库中的数据}import { bindActionCreators } from 'redux'function mapDispatchToProps(dispatch) { // actionCreater:只负责生成action (一个普通对象:必须包含type,其他自定义) return { actionCreater: bindActionCreators(actionCreater, dispatch) } } 主要的代码就是这些,样式和素材都是视频中的。","categories":[{"name":"coder","slug":"coder","permalink":"https://jx-zyf.github.io/categories/coder/"}],"tags":[{"name":"JavaScript","slug":"JavaScript","permalink":"https://jx-zyf.github.io/tags/JavaScript/"},{"name":"react","slug":"react","permalink":"https://jx-zyf.github.io/tags/react/"}]},{"title":"JS数组","slug":"数组","date":"un55fin55","updated":"un55fin55","comments":true,"path":"/posts/82c69460/","link":"","permalink":"https://jx-zyf.github.io/posts/82c69460/","excerpt":"","text":"写在开头在js中,数组应该是最常用的类型之一。并且,js中的数组和其他语言的数组有很大的区别。虽然都是有序列表,但是js中数组的每一项可以保存任何类型的值。也就是说,可以用数组的第一项来保存数值,第二项保存字符串,第三项保存对象,非常灵活。而且js数组的大小是具有呼吸性的,即可以随着数据的增加或删除自动改变length的大小。 常见的创建数组的方法有两种:一种是字面量语法,一种是使用Array构造函数创建。 var arr1 = []; // 创建了一个空数组 var arr2 = [1, 2, 3]; // 创建了一个包含3个数字的数组 var arr3 = ['a', 'b', 'c']; // 创建了一个包含3个字符串的数组 var arr4 = new Array(); // 创建了一个空数组 var arr5 = new Array(10); // 创建了一个length为10的数组,如果第一个参数不是数值,就会创建一个包含该项的数组 var arr6 = new Array('a', 'b') // 创建了一个包含两个字符串的数组 我们用的最多的还是第一种方法 常用方法检测数组 instanceof操作符:此方法基于原型/构造函数判断,只适用于只有一个全局环境的情况 arr instanceof Array; // 是数组返回true,否则返回false Array.prototype.isArray():ES5新增,为了解决instanceof的问题,存在兼容性。IE9+,Firefox 4+,Safari 5+,Opera 10.5+和Chrome支持 arr.isArray(); // 是数组返回true,否则返回false 原型链:无兼容性 Object.prototype.toString.call(arr) === '[object Array]' 转换方法 Array.prototype.toString():数组每一项转换成字符串并用’,’拼接成一个字符串 ['a', 'b', 'c'].toString(); // a,b,c Array.prototype.join():使用不同的分隔符构建这个字符串,默认使用’,’ ['a', 'b', 'c'].join(); // a,b,c ['a', 'b', 'c'].join('|'); // a|b|c 栈方法(后进先出) Array.prototype.push():接收任意数量的参数,将它们逐个添加到数组末尾,返回修改后数组的长度 Array.prototype.pop():移除数组最后一项,并返回该项 var arr = [1, 2, 3] var length = arr.push(4, 5); console.log(arr, length); // [1, 2, 3, 4, 5], 5 var item = arr.pop(); console.log(arr, item); // [1, 2, 3, 4], 5 队列方法(先进先出) Array.prototype.push():同上 Array.prototype.shift():移除数组第一项,并返回该项 var arr = [] var length = arr.push(1, 2); console.log(arr, length); // [1, 2], 2 var item = arr.shift(); console.log(arr, item); // [2], 1 补充:Array.prototype.unshift():接收任意数量的参数,将它们逐个添加到数组开头,返回修改后数组的长度 var arr = [1, 2, 3] var length = arr.unshift(4, 5); console.log(arr, length); // [4, 5, 1, 2, 3], 5 重排序方法 Array.prototype.reverse():翻转数组顺序,会改变原数组 console.log([1, 2, 3].reverse()); // [3, 2, 1] Array.prototype.sort():由于翻转不够灵活而出现,可以自定义排序规则,如果不传函数参数,则数组每一项调用toString()方法,然后排序;会改变原数组 var arr = [1, 15, 2, 30]; arr.sort(function(a, b) { return a-b; }); console.log(arr); // [1, 2, 15, 30] 操作方法 Array.prototype.concat():先创建当前数组的一个副本,然后将接收到的参数添加到该副本的末尾。如果参数是一个或多个数组,会将这些数组的每一项添加到副本数组的末尾;如果不是数组,这些值就会简单的添加到副本数组的末尾;如果没传参数,就只是简单的创建一个副本。该方法返回这个新创建的副本数组,原数组不会改变。 var arr1 = [1, 2]; var arr2 = arr1.concat([3, 4], [5]); var arr3 = arr1.concat(3, 4); var arr4 = arr1.concat(); console.log(arr1); // [1, 2] console.log(arr2); // [1, 2, 3, 4, 5] console.log(arr3); // [1, 2, 3, 4] console.log(arr4); // [1, 2] Array.prototype.slice():基于当前数组的一或多项创建一个新数组,即截取数组。接收两个参数,第一个参数(起始位置)必须,第二个参数(结束位置,不截取该项)可选,若不传则默认截取从开始位置到当前数组末尾所有项,即数组的length-1。该方法返回截取的新数组,不会改变原数组。 var arr1 = [1, 2, 3, 4]; var arr2 = arr1.slice(1); var arr3 = arr1.slice(1, 3); console.log(arr1); // [ 1, 2, 3, 4 ] console.log(arr2); // [ 2, 3, 4 ] console.log(arr3); // [ 2, 3 ] Array.prototype.splice():最强大的数组操作方法,删除,插入,替换均可。接收多个参数,分别是操作位置,删除元素个数,新增的元素(可多个);该方法返回删除元素组成的数组(如果没有删除元素则返回一个空数组),会改变原数组。 var arr1 = [1, 2, 3]; var arr2 = arr1.splice(1, 1); console.log(arr1); // [1, 3] console.log(arr2); // [2] var arr3 = arr1.splice(0, 0, 4, 5); console.log(arr1); // [4, 5, 1, 3] console.log(arr3); // [] var arr4 = arr1.splice(1, 1, 6); console.log(arr1); // [4, 6, 1, 3] console.log(arr4); // [5] 位置方法 Array.prototype.indexOf() Array.prototype.lastIndexOf():这两个方法都接收两个参数,要查找的项(必须)和查找起点位置的索引(可选,包含该位置);找到了返回该项在数组中的索引,否则返回-1。不同的是前者从前向后查找,后者从后向前查找。 var arr = [1, 2, 3, 4, 5, 6, 4, 2]; console.log(arr.indexOf(4)); // 3 console.log(arr.indexOf(4, 4)) // 6 console.log(arr.indexOf(4, 7)) // -1 console.log(arr.lastIndexOf(4)) // 6 console.log(arr.lastIndexOf(4, 4)) // 3 console.log(arr.lastIndexOf(4, 2)) // -1 迭代方法五个迭代方法:每个方法都接收两个参数,要在每一项上运行的函数(必须)和运行该函数的作用域对象(可选,影响this的值)。这个函数参数会接收三个参数,分别是迭代的每一项,该项的索引和数组对象本身。原数组都不会改变。 Array.prototype.every():函数参数对每一项都返回true,则返回true,否则返回false。 Array.prototype.filter():返回函数参数返回true的项组成的数组。 Array.prototype.forEach():没有返回值。 Array.prototype.map():返回每次调用函数参数的结果组成的数组。 Array.prototype.some():函数参数对任何一项返回true,则返回true,否则返回false。 var arr = [1, 2, 3, 4, 5]; var result1 = arr.every(function(item, index, arr) { return (item < 3); }); console.log(result1); // false var result2 = arr.some(function(item, index, arr) { return (item < 3); }); console.log(result2); // true var result3 = arr.filter(function(item, index, arr) { return (item < 3); }); console.log(result3); // [1, 2] var result4 = arr.map(function(item, index, arr) { return (item * 2) }); console.log(result4); // [2, 4, 6, 8, 10] arr.forEach(function(item, index, arr) { // 执行某些操作... }); 注意:函数参数的三个参数根据实际需要来确定是否接收,如不需要原数组对象,可以不接收。 缩小方法 Array.prototype.reduce(): Array.prototype.reduceRight():这两个方法都接收两个参数,一个在每一项调用的函数(必须)和作为缩小基础的初始值(可选)。不会改变原数组,并且构建出一个最终的值作为返回值。函数参数接收四个参数:前一项,当前项,当前项索引和原数组对象。这个函数返回的任何值都会作为下次调用该函数时的第一个参数,即前一项。第一次调用该函数时当前项是数组的第二项(即索引为1),第一个参数则为第一项(索引为0)。这两个函数的区别是前者从第一项开始,逐个遍历到最后;后者从最后一项开始,逐个遍历到第一项。 var arr = [1, 2, 3, 4]; var result = arr.reduce(function(pre, cur, index, arr) { return (pre + cur); }); console.log(arr); // [1, 2, 3, 4] console.log(result); // 10 ES6新增 Array.from():将类似数组的对象和可遍历的对象(包括ES6中的Set和Map)转化为真正的数组并返回该数组;字符串也能转化为数组 let arrayLike = { '0': 'a', '1': 'b', '2': 'c', length: 3 }; // es5 console.log(Array.prototype.slice.call(arrayLike, 0)); // ['a', 'b', 'c'] // es6 console.log(Array.from(arrayLike)) // ['a', 'b', 'c'] console.log(Array.from('hello')); // ['h', 'e', 'l', 'l', 'o'] Array.from 方法还接受第二个参数,作用类似于数组的 map() 方法,用来对每个元素进行处理,将处理后的值放入返回的数组 let arrayLike = { '0': 1, '1': 2, '2': 3, length: 3 }; console.log(Array.from(arrayLike, x => x * x)); // [1, 4, 9] // 等同于 console.log(Array.from(arrayLike).map(x => x * x)); // [1, 4, 9] Array.of():将一组值转换为数组 Array.of(1,2,3); // [1, 2, 3] Array.of(3); // [3] Array.of(); // [] Array.of(1,,2,'a'); // error Array.of(1,2,'a'); // [1,2,'a'] Array.prototype.copyWithin():在当前数组内部,将指定位置的成员,复制到其他位置(会覆盖原有成员,修改原数组),然后返回替换后的数组。该方法接收三个参数:(三个参数都是数值,如果不是会自动转化成数值)-target(必需):从该位置开始替换数据-start(可选):从该位置开始读取目标数据,默认值是0-end(可选):从该位置(不包括它)停止读取目标数据,默认值是数组长度(将start到end的成员替换从target开始的成员) console.log([5, 3, 1, 8, 0, 2].copyWithin(3, 0, 2)); // [5, 3, 1, 5, 3, 2] Array.prototype.find(): 该方法接收一个函数作为参数,用于找出参数函数返回true的第一个数组成员(即满足条件的成员),没找到返回 undefined。该函数参数接收三个参数(value,index,arr),分别是当前值,当前位置和原数组。 Array.prototype.findIndex():该方法参数同find(),返回符合条件的第一个数组成员的索引,没找到返回 -1。 console.log([1, 2, 3, 4].find((x) => x > 2)) // 3 console.log([1, 2, 3, 4].find((x) => x > 4)) // undefined console.log([1, 2, 3, 4].findIndex((x) => x > 2)) // 2 console.log([1, 2, 3, 4].findIndex((x) => x > 4)) // -1 Array.prototype.fill():使用指定的值,填充一个数组并返回,会改变原数组。接收三个参数:-value(必需):填充的值-start(可选):填充的起始位置,默认是0-end(可选):填充的结束位置(不包括它),默认是数组的长度 var arr = [1, 2, 3, 4]; console.log(arr.fill(6)); // [6, 6, 6, 6] console.log(arr); // [6, 6, 6, 6] console.log(arr.fill(3,1,3)); // [6, 3, 3, 6] console.log(arr); // [6, 3, 3, 6] Array.prototype.entries(),Array.prototype.keys() 和 Array.prototype.values():这三个方法都返回一个遍历器对象,可以用for...of循环遍历,区别是keys()是对键名的遍历,values()是对键值的遍历,entries()是对键值对的遍历。 for (let key of [1, 2].keys()) { console.log(key); // 0, 1 } for (let value of [1, 2].values()) { console.log(value); } for (let [key, value] of [1, 2].entries()) { console.log(key, value); // 0 1 , 1 2 } 注意:对于values(), Chrome 未实现,Firefox未实现,Edge已实现。 Array.prototype.includes():该方法返回了一个布尔值,判断数组中是否包含给定的值,与字符串的 includes() 类似。该方法可以检测到 NaN,弥补了 indexOf() 的不足;并且 indexOf() 不够具有语义化,它检测的是在数组中的位置。它接收两个参数:-target(必需):需要在数组中匹配的值-start(可选):从该位置(包含该位置)开始匹配,默认为0。如果为负数则表示倒数的位置,如果大于数组长度则重置为0 console.log([1, 2, 3].includes(2)) // true console.log([1, 2, 3].includes(2, 2)) // false console.log([1, 2, 3].includes(2, -2)) // true 注意:该方法目前还存在兼容性 // 兼容性写法 const contains = (() => Array.prototype.includes ? (arr, value, index) => arr.includes(value, index) : (arr, value, index) => arr.some((el, index) => el === value) )(); console.log(contains([1,2,3], 1, 1)); // false 另外:Map 和 Set 数据结构有一个 has 方法,注意与 includes() 的区别:-Map:查找键名(key)-Set:查找键值(value) 参考:JavaScript高级程序设计(第三版) 阮一峰es6入门:数组的扩展 写在最后在如此多的数组方法中,非常容易记混。我认为要抓住以下几点:该方法的作用?所需的参数(必需的和可选的)有哪些?是否有返回值?返回值是什么?是否会改变原数组?","categories":[{"name":"coder","slug":"coder","permalink":"https://jx-zyf.github.io/categories/coder/"}],"tags":[{"name":"JavaScript","slug":"JavaScript","permalink":"https://jx-zyf.github.io/tags/JavaScript/"}]},{"title":"JavaScript基础","slug":"JavaScript原型,闭包及作用域","date":"un33fin33","updated":"un33fin33","comments":true,"path":"/posts/a9ba4134/","link":"","permalink":"https://jx-zyf.github.io/posts/a9ba4134/","excerpt":"","text":"原型在ES6之前,javascript是没有“类”的概念的,这是由于js诞生就是为了响应用户的交互,如果有了“类”,就成了一种完整的面向对象编程语言了。但是,js中都是对象,必须有一种机制,将所有对象联系起来,所以“继承”还是很有必要的。 new运算符我们都知道,C和Java都是通过new来生成实例的,于是js也使用了这个命令。在js中,new后面跟的不是原型对象,而是构造函数。 下面我们看段代码: function People(name) { this.name = name; this.say = function() { console.log(`I'm ${this.name}`) } } let linxun = new People('linxun'); linxun.say() // I'm linxun 注意:this指的是新创建的实例对象 上面的例子中,People是构造函数,其中有一个name属性和一个say方法,每个实例的name属性是不一样的,say方法却是相同的,所以用new生成实例对象有一个很大的缺陷,就是不能实现共享属性和方法,每一个实例对象的属性和方法都是自身独有的,这是对资源极大的浪费。 扯了这么多,终于到重点了! prototype属性的引入这个属性(只要函数才有)包含了一个对象(以下称为“prototype对象”),所有实例需要共享的属性和方法,都放在这个对象上。而那些不需要共享的,实例独有的属性和方法就放在构造函数里面。实例创建之后,会自动引用prototype对象中的属性和方法。 还是刚才那个例子,我们改写一下: function People(name) { this.name = name; } People.prototype = { say: function() { console.log(`I'm ${this.name}`); } } let linxun = new People('linxun'); let zyf = new People('zyf); linxun.say() // I'm linxun zyf.say() // I'm zyf 现在say方法就是所有实例共享的了,如果修改了它,所有的实例对象都会受影响。 People.prototype.say = function() { console.log('我被修改了'); } linxun.say() // 我被修改了 zyf.say() // 我被修改了 原型链的实现引用《JavaScript权威指南》中的一段描述 Every JavaScript object has a second JavaScript object (or null ,but this is rare) associated with it. This second object is known as a prototype, and the first object inherits properties from the prototype. 我的理解就是每个js对象(null除外)都有一个原型对象,并且从原型对象上继承属性和方法。 既然有这么一个原型对象,那么我们的对象怎么和原型对象对应起来呢?对象的 __proto__ (以下称为“原型对象”) 属性就指向它构造函数的prototype(以下称为“原型”),而对象的原型本身也是一个对象,也有 __proto__ 属性,这就形成了原型链。原型链的最顶端是 Object.prototype ,它的 __proto__ 指向null(ECMA规定的,避免无限循环)。 从前面我们知道,函数创建的时候就拥有了prototype属性,我们创建实例的时候依靠它实现属性和方法的继承。 这里插入一下,new操作符到底做了什么事情? 创建一个空对象 var obj = {} 将创建对象的 __proto__ 指向构造函数的prototype obj.__proto__ = 构造函数.protootype 将对象内部的this指向新创建的对象 构造函数.call(obj) 还是看代码吧: var obj1 = {} var obj2 = new Object(); console.log(obj1.__proto__ === Object.prototype); // true console.log(obj2.__proto__ === Object.prototype); // true console.log(Object.prototype.__proto__ === null); // true 还是有点懵?看张图吧,我轻易不放出来。其实只是因为我很懒不想打这么多字…… 属性或方法的查找当查找一个对象的属性或方法时,js会向上遍历原型链,先在自身查找,没找到就到它原型对象上找,依次向上查找,直到原型链的最顶端Object.prototype,如果仍然没有找到,就返回undefined(不存在该属性)或者抛出一个错误(不存在该方法)。 闭包闭包是js的一大特色,也是一大难点。下面就是我对闭包的理解。 变量的作用域说起闭包,就不得不先了解下js特殊的变量作用域。js中变量作用域就两种:全局变量和局部变量。 全局变量:在任何地方都能访问 局部变量:只有在特定的地方才能访问 举个例子: var a = 10; function test() { var b = ‘hello world’; console.log(a, b); } test(); // 10, hello world console.log(a, b); // 10, error: b is not defined 上面例子中的 a 就是全局变量,在哪里都能访问;b 就是局部变量,只有在 test 函数中才能访问。注意:声明 b 变量时 var 不能省略,否则就声明了一个全局变量 如何从函数外部读取局部变量在很多时候,我们需要得到函数内部的局部变量,在正常情况下这是做不到的。我们可以这样,在函数中再声明一个函数。下面代码中,函数fun2中是可以访问fun1函数中的局部变量的,而反过来就不行。这就是js特殊的链式作用域,在下面我们会讲到这个问题。 function fun1() { var a = 10; function fun2() { console.log(a); // 10 } } 既然函数fun2可以读取函数fun1中的局部变量,如果我们将fun2作为fun1的返回值返回出去,那么我们不就可以在外面读取函数fun1的局部变量了吗? function fun1() { var a = 10; return function() { console.log(a); } } var result = fun1(); result(); // 10 上面代码中,声明一个全局变量result来接收fun1的返回值(fun2),意味着fun2将一直存在,而fun1作为fun2的依赖函数,也将一直存在作用域中。如果将result销毁,自然也就访问不到fun1中的局部变量了。 闭包的概念上面例子中的fun2就是闭包。参考阮一峰阮大神的文章:学习javascript闭包 闭包就是能够读取其他函数内部变量的函数 我的理解是:闭包是指有权访问另一个函数作用域中变量的函数。在A函数中声明定义B函数,且B函数中使用了A函数中的局部变量,这就是闭包。即使A函数调用完毕,B函数依然可以访问那个局部变量。如果A函数没有执行完,B函数中使用的局部变量的值是其当前值,如果A函数执行完毕,B函数中使用的局部变量就是最终值。 闭包的作用讲了那么多,闭包究竟有什么用呢? 上面例子中的,读取函数内部的变量 让一个变量常驻内存,上面代码中的变量a就已经常驻内存了,只有通过我们手动销毁。 IIFE下面看一个例子:假设页面上有五个按钮,对每个按钮添加点击事件 var ele = document.getElementsByTagName('button'); for(var i=0, l=ele.length; i<l; i++) { ele[i].addEventListener('click', function() { console.log(i); }); } 上面代码打印出了五个5,而不是我们期待的0,1,2,3,4;因为 i 是全局变量自始至终就只有一个 i,点击事件执行时,循环早已经执行完毕,它闭包到的永远是 i 最后的值。而如果改写成下面这样: var ele = document.getElementsByTagName('button'); for(var i=0, l=ele.length; i<l; i++) { (function(j) { ele[i].addEventListener('click', function() { console.log(j); }); })(i) } 上面代码就打印出我们期待的0,1,2,3,4了;使用了IIFE立即执行函数创建了五个 i 的作用域,每个作用域中的 i 相互独立不受影响,避免了 i 的共用。 闭包的缺陷前面也提到了,闭包会使得函数中的变量一直保存在内存中,所以不能滥用闭包,否则会造成性能问题。解决方法是:在不需要该变量时手动清除。 作用域和作用域链js中变量的作用域分为全局作用域和局部作用域,作用域(执行环境)就是变量和函数的可访问范围。某个执行环境中所有代码执行完毕后,该环境就会被销毁,保存在其中的所有变量和函数定义也会被销毁,全局执行环境直到应用程序退出——如关闭网页或浏览器——时才会被销毁。 在任何地方都能访问到的对象拥有全局作用域,一般有以下几种情况: 最外层函数和在最外层函数外面定义的变量 未申明直接赋值的变量自动声明为全局作用域 所有window对象的属性 局部作用域只在固定的代码片段可以访问,最常见的如函数内部 ES6之前,js没有块级作用域 ( IIFE 实现块级作用域 let:es6) 作用域链:函数创建时有一个内部属性[[Scope]],这个内部属性包含了函数创建时作用域中对象的集合,这个集合决定了哪些数据能被访问。 函数创建的时候,作用域链会填入一个全局对象,包含了所有全局变量 函数执行时会创建一个“运行期上下文”的内部对象,它定义了函数执行的环境。每个运行期上下文都有自己的作用域链 函数[[Scope]]属性中的集合被复制到运行期上下文对象上,组成了一个新的对象,称为“活动对象” 函数执行完毕后,运行期上下文被销毁,活动对象也随之销毁 今天就到这吧了……不定期更新","categories":[{"name":"coder","slug":"coder","permalink":"https://jx-zyf.github.io/categories/coder/"}],"tags":[{"name":"JavaScript","slug":"JavaScript","permalink":"https://jx-zyf.github.io/tags/JavaScript/"}]},{"title":"HTML&CSS基础知识(二)","slug":"HTML与CSS(2)","date":"un11fin11","updated":"un22fin22","comments":true,"path":"/posts/d8be64fd/","link":"","permalink":"https://jx-zyf.github.io/posts/d8be64fd/","excerpt":"","text":"HTML&CSS1.盒子模型盒子模型都包含 content(内容),padding(内边距/内填充),margin(外边距/外填充),border(边框) 标准盒子模型(content-box) 标准盒子模型是w3c盒子模型,从上图可以看出width/height的计算规则是只包含content部分 IE盒子模型(border-box) 从上图可以看出IE盒子模型的width/height计算包含border,padding和content 只要在HTML文件顶部有doctype声明,浏览器就会使用w3c的标准盒子模型;如果没有声明,则不同浏览器会使用不同的盒子模型去加载,如IE使用IE盒子模型,Chrome和Firefox会使用w3c标准盒子模型 在CSS3中,我们可以自己定义盒子模型(box-sizing) box-sizing 有两个值,一个是content-box(默认标准模型),另一个是border-box(IE盒子模型,也加边框模型) 2.行内元素和块级元素行内元素和块级元素的区别: 1.行内元素会在一条直线上排列,而块级元素占据一行,在垂直方向上排列。 2.一般情况下(HTML5之前),行内元素只能包含文本和其他行内元素,而块级元素可以包含行内元素和块级元素 按照新的HTML规范,已经不按inline和block来区分元素类型了 替换元素与非替换元素 从元素本身特点来讲,元素分为替换元素和非替换元素 替换元素:浏览器根据元素属性来决定元素具体显示的内容。比如说浏览器会根据 img 标签的src来读取图片信息,根据 input 标签的type来决定是输入框还是按钮等。HTML中的 img ,input , select , textarea 等都是替换元素,因为这些元素没有实际内容,浏览器根据标签类型或属性来显示这些元素 非替换元素:HTML中多数元素都是不可替换元素,其内容直接显示在浏览器上。接下来就是重点了:1.块级元素width/height/margin/padding的设置都有效2.行内替换元素width/height/margin/padding的设置都有效3.行内非替换元素width/height的设置无效,一般用line-height控制行高(大部分行内元素都是行内非替换元素,如span,a,i,label等);margin的左右设置有效,上下设置无效;padding的上下左右设置我认为其实都有效,但是由于行高没有发生变化,元素的位置只相对于左右变化,而上下没有变化。 3.定位(position) static: 静态定位,默认(没有定位) relative: 绝对定位,相对元素本身定位,也就是相对于它本该存在的位置(正常位置)定位,位置通过 left / right / top / bottom 确定 absolute: 相对定位,相对该元素第一个不是static定位的父元素定位,位置通过 left / right / top / bottom 确定 fixed: 固定定位,相对浏览器窗口定位,位置通过 left / right / top / bottom 确定 inherit: 继承,从父元素继承position定位 定位会脱离文档流,父元素计算高度的时候不会将其计算进去 4.浮动(float)浮动的特点 浮动元素不会遮住其它元素内容,但是会遮住非浮动元素的布局 浮动元素的父元素在对内容进行排版展示时会忽略浮动子元素的存在(脱离文档流) 浮动元素会自动变成块级元素 清除浮动的方法 父元素设置overflow:hidden 父元素设置height 父元素定义伪类 :after 父元素结尾处加一个空标签:clear both 父元素也设置浮动,但是需要设置宽度… 5.css常用布局","categories":[{"name":"coder","slug":"coder","permalink":"https://jx-zyf.github.io/categories/coder/"}],"tags":[{"name":"HTML","slug":"HTML","permalink":"https://jx-zyf.github.io/tags/HTML/"}]},{"title":"HTML&CSS基础知识(一)","slug":"HTML与CSS","date":"un55fin55","updated":"un22fin22","comments":true,"path":"/posts/e8a88825/","link":"","permalink":"https://jx-zyf.github.io/posts/e8a88825/","excerpt":"","text":"HTML&CSS1.主要浏览器有哪些?内核分别是什么?主流浏览器及内核: (1)Chrome:Webkit (2)IE:Trident (3)Safari:Webkit (4)Firefox:Gecko (5)Opera:Presto 2.DoctypeDOCTYPE是用来声明文档类型和DTD(document type definition,文档类型定义)规范的 HTML5只有一种<!DOCTYPE>声明 <!DOCTYPE html> HTML5不基于 SGML,所以不需要对DTD进行引用,但是需要doctype来规范浏览器的行为(让浏览器按照它们应该的方式来运行)。 而HTML4.01基于SGML,所以需要对DTD进行引用,才能告知浏览器文档所使用的文档类型。注意:<!DOCTYPE>不是HTML标签,没有结束标签,不区分大小写 3.浏览器模式浏览器模式分两种:(1).标准模式(standards mode):浏览器根据标准规约来渲染页面 以下情况浏览器会采用标准模式渲染: 给出了完整的DOCTYPE声明DOCTYPE声明了Strict DTDDOCTYPE声明了Transitional DTD和URI (2).混杂/怪异/兼容模式(quirks mode):浏览器采用更宽松的,向后兼容的方式来渲染页面 以下情况浏览器会采用混杂模式渲染: DOCTYPE声明了Transitional DTD但未给出URIDOCTYPE声明不合法 DTD未给出DOCTYPE声明混杂模式下,浏览器会模仿旧浏览器的行为 4.页面引入CSS样式时,link和@import的区别 link属于XHTML标签,除了加载CSS外,还能用于定义RSS,定义rel连接属性等作用;而@import是CSS提供的,只能用于加载CSS;页面加载时,link引入的样式会同时被加载;而@import引入的样式只有在页面加载完才会被加载import是CSS2.1 提出的,只在IE5以上才能被识别,而link是XHTML标签,无兼容问题。 5.浏览器渲染原理挂上大佬的博客,讲的很详细 深度剖析浏览器渲染性能原理,你到底知道多少?文章作者:齐修_qixiuss","categories":[{"name":"coder","slug":"coder","permalink":"https://jx-zyf.github.io/categories/coder/"}],"tags":[{"name":"HTML","slug":"HTML","permalink":"https://jx-zyf.github.io/tags/HTML/"}]},{"title":"About Me","slug":"About","date":"un66fin66","updated":"un22fin22","comments":true,"path":"/posts/f0b34752/","link":"","permalink":"https://jx-zyf.github.io/posts/f0b34752/","excerpt":"","text":"林寻丶我的微博 我是 林寻丶 热爱生活,热爱生命喜欢音乐,喜欢篮球的热血boy motto","categories":[{"name":"about","slug":"about","permalink":"https://jx-zyf.github.io/categories/about/"}],"tags":[]}]}