立即报名
视野决定广度,技术决定深度
没有足够的数据
(゚∀゚ )
暂时没有任何数据
(゚∀゚ )
暂时没有任何数据
X北辰北 关注了专栏 · 2020-06-26
聚焦全栈,专注分享 Angular、TypeScript、Node.js/Java 、Spring 技术栈等全栈干货。 欢迎小伙伴们关注公众号全栈修仙之路,一起升级打怪。
关注 7075
X北辰北 收藏了文章 · 2020-04-07
转载请联系,原文链接
之前写过一篇 一年半经验如何准备阿里巴巴前端面试,给大家分享了一个面试复习导图,有很多朋友说希望能够针对每个 case 提供一个参考答案。
写答案就算了,一是精力有限,二是我觉得大家还是需要自己理解总结会比较好。
给大家整理了一下每个 case 一些还算不错的文章吧(还包括一些躺在我收藏夹里的好文章),大家可以自己看文章总结一下答案,这样也会理解更深刻。
并不是所有文章都需要看,希望是一个抛砖引玉的作用,大家也可以锻炼一下自己寻找有效资料的能力 ~
( 文章排序不分前后,随机排序 ~
建议收藏文章,结合复习导图食用,效果更佳。
完整复习导图全展开太大了,可关注公众号「前端试炼」回复【面试】获取。
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
// 相当于
async function async1() {
console.log('async1 start');
Promise.resolve(async2()).then(() => {
console.log('async1 end');
})
}
这个知识点真的是重在理解,一定要理解彻底
for (const macroTask of macroTaskQueue) {
handleMacroTask();
for (const microTask of microTaskQueue) {
handleMicroTask(microTask);
}
}
太多了... 总的来说就是 API 一定要熟悉...
从思想、生态、语法、数据、通信、diff等角度自己总结一下吧。
思考题,自由发挥
推荐一些值得看的书,基本都是我看完或者有翻过几页觉得不错但是还没时间看的书。
上文整理了网上的一些相关文章和躺在我收藏夹里精选文章,有一些文章还没看,还需要持续学习呀 ~
放弃了假期快落的岛上生活(动森),吐血整理这份资料,希望对大家有所帮助~
欢迎关注公众号「前端试炼」,回复【面试】获取完整复习导图。公众号平时会分享一些实用或者有意思的东西,发现代码之美。专注深度和最佳实践,希望打造一个高质量的公众号。偶尔还会分享一些摄影 ~
扫码_搜索联合传播样式-标准色版.png
也可以扫码加我微信,拉你进交流划水聊天群,有看到好文章/代码都会发在群里。
转载请联系,原文链接 前言 之前写过一篇 一年半经验如何准备阿里巴巴前端面试,给大家分享了一个面试复习导图,有很多朋友说希望能够针对每个 case 提供一个参考答案。 写答案就算了,一是精力有限,二是我觉得大家还是需要自己理解总结会比较好。 给大家整理了一下...
X北辰北 发布了文章 · 2020-03-31
前面两篇文章通过扩展EchartsLayer.js这个图层类,实现了使用ArcGIS JS API和eCharts,在二维和三维场景下绘制迁徙图和散点图。这篇文章继续通过绘制网络路径图的例子,再来验证下我们扩展的这个图层类是否可用,先来看下最终效果:
迁徙图、散点图和网络路径图这种图表跟地理坐标关系紧密,所以仅仅通过第一篇二维普通图表绘制的方式是无法实现这类图表绘制的,所以就需要我们来扩展eCharts的相关功能,使其能够够结合最新版的ArcGIS JS API来完成地图上这类图表的绘制,eCharts官网也提供了相应的扩展插件,但这种插件并不能很好地支持我们ArcGIS JS API的高版本,所以我们在这篇文章里直接扩展了一个图层类,下面是具体的实现思路:
实现ArcGIS JS API和eCharts的结合,最最关键的是要实现两个插件库里的坐标系转换,这是重点,只要搞清楚了这一点,我们完全可以脱离地图API库的束缚,理论上可以实现eCharts跟任意地图库的结合。在此处转换坐标时我们使用了eCharts提供的registerCoordinateSystem方法,通过这个方法我们注册了一个名为"arcgis"的坐标系,里面对eCharts中的dataToPoint、pointToData等方法进行了重写,然后将这些所有内容封装为了一个EchartsLayer图层类。至于这个文件的源码,文章结尾会提供,接下来我们看一下具体的实现步骤。
1、本文所用的demo同样是基于React框架搭建的,所以我们首先基于React框架搭建一个初始化项目,然后改写src目录下的App.js这个主文件,实例化出一张二维地图,这中间用到了esri-loader插件,具体的实现过程可查看我的这篇文章【【番外】 React中使用ArcGIS JS API 4.14开发】,里面有具体的实现步骤。
2、通过上述操作实例化完一张二维地图后,我们接下来就要进行网络路径图的绘制操作了,在开始之前我们需要数据,就是关于网络路径的坐标数据和相应权重值的数据,我将它存在了一份JS文件里,此文件比较大,所以并不打算在此处粘贴出来,有需要的可以到此地址下载。
3、下载到数据后,将数据拷贝至demo代码目录下,然后在组件代码中引入,此处我们是将它引入到我们新建的TrafficNetwork组件目录下,然后编写数据处理的函数,对下载下来的数据进行初始化处理,代码如下:
//初始化数据
_initData=() => {
const _self = this;
let data = trafficData;
let hStep = 300 / (data.length - 1);
_self.state.busLines = [].concat.apply([], data.map(function (busLine, idx) {
let prevPt;
let points = [];
for (let i = 0; i < busLine.length; i += 2) {
let pt = [busLine[i], busLine[i + 1]];
if (i > 0) {
pt = [
prevPt[0] + pt[0],
prevPt[1] + pt[1]
];
}
prevPt = pt;
points.push([pt[0] / 1e4, pt[1] / 1e4]);
}
return {
coords: points,
lineStyle: {
normal: {
color: echarts.color.modifyHSL('#5A94DF', Math.round(hStep * idx))
}
}
}
}))
}
4、数据初始完成后,我们进行网络路径图表的绘制信息配置,也就是初始化series属性,代码如下:
//初始化图表配置
_initCharts=() => {
const _self = this;
_self.state.series = [{
type: 'lines',
coordinateSystem: 'arcgis',
polyline: true,
data: _self.state.busLines,
silent: true,
lineStyle: {
// color: '#c23531',
// color: 'rgb(200, 35, 45)',
opacity: 0.2,
width: 1
},
progressiveThreshold: 500,
progressive: 200
}, {
type: 'lines',
coordinateSystem: 'arcgis',
polyline: true,
data: _self.state.busLines,
lineStyle: {
width: 0
},
effect: {
constantSpeed: 20,
show: true,
trailLength: 0.1,
symbolSize: 1.5
},
zlevel: 1
}]
}
5、接下来和之前文章的流程一样,监听地图的绘制完成事件,然后调用图表绘制函数来绘制网络路径图,代码如下:
view.when(function() {
_self.state.mapview = view;
_self._drawCharts();
});
//绘制图表
_drawCharts=() => {
const _self = this;
const options = {
url: 'https://js.arcgis.com/4.14/dojo/dojo.js',
};
loadModules([
'http://localhost/test/EchartsLayer.min.js'
], options).then(([
echartsLayer
]) => {
console.log(_self.state.mapview)
//_self.state.mapview.when(function(){
let chart = new echartsLayer(_self.state.mapview);
let option = {
title: {
text: 'ArcGIS API for Javascript4.14扩展Echarts4之网络路径图',
subtext: 'Develop By X北辰北',
left: 'center',
textStyle: {
color: '#fff'
}
},
series: _self.state.series
};
chart.setChartOption(option);
//});
}
).catch((err) => {
console.log('图表绘制失败,' + err);
});
}
6、通过以上步骤就完成了网络路径的二维绘制,三维绘制的话很简单,只需要将MapView替换为SceneView即可,代码如下:
let view = new SceneView({
container: "mapview",
map: map,
scale: 50000000,
center: [107.246152,34.414465]
});
我们通过三篇文章,介绍了关于在eCharts中有关地理坐标的图表如何在ArcGIS的底图上绘制的过程,这篇文章中的实现流程跟之前两篇一样,都是通过扩展EchartsLayer.js这个图层类来实现的,所以后续大家有需要将eCharts中有关地理坐标的图表绘制到ArcGIS地图上时,可用此扩展类来实现。关于将eCharts中普通的二维图表绘制到ArcGIS底图上的操作,因为不涉及地理坐标的问题,所以我们通过纯前端的方式即可解决,具体实现请看这篇文章【01 【ArcGIS JS API + eCharts系列】实现地图上二维图表的绘制】。
TrafficNetwork组件代码:
import React,{Component} from 'react';
import {loadModules} from 'esri-loader';
import echarts from 'echarts';
import './trafficNetwork.css';
import trafficData from './data/trafficData';
class TrafficNetwork extends Component {
state = {
busLines: null,
series: null,
}
componentDidMount=() => {
this._initMapView();
this._initData();
this._initCharts();
}
//实例化地图
_initMapView=() => {
const _self = this;
const options = {
url: 'https://js.arcgis.com/4.14/',
css: 'https://js.arcgis.com/4.14/esri/themes/light/main.css'
};
loadModules(['esri/Map',
'esri/Basemap',
'esri/layers/TileLayer',
'esri/views/MapView',
'esri/views/SceneView',
], options).then(([
Map,
Basemap,
TileLayer,
MapView,
SceneView,
]) => {
let basemap = new Basemap({
baseLayers: [
new TileLayer({
url: "http://map.geoq.cn/arcgis/rest/services/ChinaOnlineStreetPurplishBlue/MapServer",
title: "Basemap"
})
],
title: "basemap",
id: "basemap"
});
let map = new Map({
basemap: basemap
});
let view = new MapView({
container: "mapview",
map: map,
zoom: 10,
center: [116.403406,39.91582] //北京 116.403406,39.91582 成都 107.246152,34.414465
});
// let view = new SceneView({
// container: "mapview",
// map: map,
// scale: 50000000,
// center: [107.246152,34.414465]
// });
view.when(function() {
_self.state.mapview = view;
_self._drawCharts();
});
}
).catch((err) => {
console.log('底图创建失败,' + err);
});
}
//初始化数据
_initData=() => {
const _self = this;
let data = trafficData;
let hStep = 300 / (data.length - 1);
_self.state.busLines = [].concat.apply([], data.map(function (busLine, idx) {
let prevPt;
let points = [];
for (let i = 0; i < busLine.length; i += 2) {
let pt = [busLine[i], busLine[i + 1]];
if (i > 0) {
pt = [
prevPt[0] + pt[0],
prevPt[1] + pt[1]
];
}
prevPt = pt;
points.push([pt[0] / 1e4, pt[1] / 1e4]);
}
return {
coords: points,
lineStyle: {
normal: {
color: echarts.color.modifyHSL('#5A94DF', Math.round(hStep * idx))
}
}
}
}))
}
//初始化图表配置
_initCharts=() => {
const _self = this;
_self.state.series = [{
type: 'lines',
coordinateSystem: 'arcgis',
polyline: true,
data: _self.state.busLines,
silent: true,
lineStyle: {
// color: '#c23531',
// color: 'rgb(200, 35, 45)',
opacity: 0.2,
width: 1
},
progressiveThreshold: 500,
progressive: 200
}, {
type: 'lines',
coordinateSystem: 'arcgis',
polyline: true,
data: _self.state.busLines,
lineStyle: {
width: 0
},
effect: {
constantSpeed: 20,
show: true,
trailLength: 0.1,
symbolSize: 1.5
},
zlevel: 1
}]
}
//绘制图表
_drawCharts=() => {
const _self = this;
const options = {
url: 'https://js.arcgis.com/4.14/dojo/dojo.js',
};
loadModules([
'http://localhost/test/EchartsLayer.min.js'
], options).then(([
echartsLayer
]) => {
console.log(_self.state.mapview)
//_self.state.mapview.when(function(){
let chart = new echartsLayer(_self.state.mapview);
let option = {
title: {
text: 'ArcGIS API for Javascript4.14扩展Echarts4之网络路径图',
subtext: 'Develop By X北辰北',
left: 'center',
textStyle: {
color: '#fff'
}
},
series: _self.state.series
};
chart.setChartOption(option);
//});
}
).catch((err) => {
console.log('图表绘制失败,' + err);
});
}
render() {
return (
<div id="mapview"></div>
)
}
}
export default TrafficNetwork;
EchartsLayer.min.js文件代码:
var _0x4564=['prototype','setMapOffset','dataToPoint','point','toScreen','pointToData','toMap','getViewRect','BoundingRect','getRoamTransform','dojo/_base/declare','dojo/_base/lang','esri/geometry/Point','esri/geometry/SpatialReference','EchartsglLayer','registerCoordinateSystem','arcgis','getE3CoordinateSystem','init','setBaseMap','createLayer','view','chartOption','setCharts','box','visible','hidden','chart','off','undefined','extent','xAxis','xmin','xmax','yAxis','ymin','ymax','setOption','animation','createElement','div','setAttribute','echartsData','name','style','width','height','position','absolute','top','left','getElementsByClassName','esri-view-surface','appendChild','startMapEventListeners','outerHTML','originLyr','features','screenData','map_DragStart_Listener','remove','map_DragEnd_Listener','map_ZoomStart_Listener','map_ZoomEnd_Listener','map_ExtentChange_Listener','watch','hitch','resize','rotation','map','_mapOffset','create','eachSeries','get','coordinateSystem','getDimensionsInfo','dimensions'];(function(_0x4ea369,_0x173297){var _0x432a1a=function(_0x3b4d7a){while(--_0x3b4d7a){_0x4ea369['push'](_0x4ea369['shift']());}};_0x432a1a(++_0x173297);}(_0x4564,0xf1));var _0x1824=function(_0x20e690,_0x5f0396){_0x20e690=_0x20e690-0x0;var _0x841fe2=_0x4564[_0x20e690];return _0x841fe2;};define([_0x1824('0x0'),_0x1824('0x1'),_0x1824('0x2'),_0x1824('0x3')],function(_0x4156fb,_0x59c3eb,_0x275378,_0x4d54b1){return _0x4156fb(_0x1824('0x4'),null,{'name':_0x1824('0x4'),'view':null,'box':null,'chart':null,'chartOption':null,'visible':!![],'constructor':function(_0x27b7d3,_0x649a95){echarts[_0x1824('0x5')](_0x1824('0x6'),this[_0x1824('0x7')](_0x27b7d3));this[_0x1824('0x8')](_0x27b7d3,_0x649a95);},'init':function(_0x3a80a9,_0x5617d3){this[_0x1824('0x9')](_0x3a80a9);this[_0x1824('0xa')]();},'setBaseMap':function(_0x3ddf37){this[_0x1824('0xb')]=_0x3ddf37;},'setChartOption':function(_0x497153){this[_0x1824('0xc')]=_0x497153;this[_0x1824('0xd')]();},'setVisible':function(_0x36aa18){if(!this[_0x1824('0xe')]||this[_0x1824('0xf')]===_0x36aa18)return;this[_0x1824('0xe')][_0x1824('0x10')]=!_0x36aa18;this[_0x1824('0xf')]=_0x36aa18;_0x36aa18===!![]&&setCharts();},'refreshBegin':function(){this[_0x1824('0xe')][_0x1824('0x10')]=!![];},'refreshing':function(){setCharts();},'refreshEnd':function(){this[_0x1824('0xe')][_0x1824('0x10')]=![];},'on':function(_0x5dd691,_0x472109,_0x4b90b9){this[_0x1824('0x11')]['on'](_0x5dd691,_0x472109,_0x4b90b9);},'off':function(_0x25e82f,_0x44fdf2,_0x3cd39d){this[_0x1824('0x11')][_0x1824('0x12')](_0x25e82f,_0x44fdf2,_0x3cd39d);},'map_DragStart_Listener':null,'map_DragEnd_Listener':null,'map_ZoomStart_Listener':null,'map_ZoomEnd_Listener':null,'map_ExtentChange_Listener':null,'map_click_Listener':null,'setCharts':function(){if(!this[_0x1824('0xf')])return;if(this[_0x1824('0xc')]==null||this[_0x1824('0xc')]==_0x1824('0x13'))return;let _0x50f53f=this[_0x1824('0xb')][_0x1824('0x14')];this[_0x1824('0xc')][_0x1824('0x15')]={'show':![],'min':_0x50f53f[_0x1824('0x16')],'max':_0x50f53f[_0x1824('0x17')]};this[_0x1824('0xc')][_0x1824('0x18')]={'show':![],'min':_0x50f53f[_0x1824('0x19')],'max':_0x50f53f[_0x1824('0x1a')]};this[_0x1824('0x11')][_0x1824('0x1b')](this[_0x1824('0xc')]);this[_0x1824('0xc')][_0x1824('0x1c')]=![];},'createLayer':function(){let _0x56973d=this[_0x1824('0xe')]=document[_0x1824('0x1d')](_0x1824('0x1e'));_0x56973d[_0x1824('0x1f')]('id',_0x1824('0x20'));_0x56973d[_0x1824('0x1f')](_0x1824('0x21'),_0x1824('0x20'));_0x56973d[_0x1824('0x22')][_0x1824('0x23')]=this[_0x1824('0xb')][_0x1824('0x23')]+'px';_0x56973d[_0x1824('0x22')][_0x1824('0x24')]=this[_0x1824('0xb')][_0x1824('0x24')]+'px';_0x56973d[_0x1824('0x22')][_0x1824('0x25')]=_0x1824('0x26');_0x56973d[_0x1824('0x22')][_0x1824('0x27')]=0x0;_0x56973d[_0x1824('0x22')][_0x1824('0x28')]=0x0;let _0x22f992=document[_0x1824('0x29')](_0x1824('0x2a'))[0x0];_0x22f992[_0x1824('0x2b')](_0x56973d);this[_0x1824('0x11')]=echarts[_0x1824('0x8')](_0x56973d);this[_0x1824('0x2c')]();},'removeLayer':function(){this[_0x1824('0xe')][_0x1824('0x2d')]='';this[_0x1824('0xb')]=null;this[_0x1824('0xe')]=null;this[_0x1824('0x2e')]=null;this[_0x1824('0x2f')]=null;this[_0x1824('0x30')]=[];this[_0x1824('0x11')]=null;this[_0x1824('0xc')]=null;this[_0x1824('0x31')][_0x1824('0x32')]();this[_0x1824('0x33')][_0x1824('0x32')]();this[_0x1824('0x34')][_0x1824('0x32')]();this[_0x1824('0x35')][_0x1824('0x32')]();this[_0x1824('0x36')][_0x1824('0x32')]();},'startMapEventListeners':function(){let _0x576d14=this[_0x1824('0xb')];_0x576d14[_0x1824('0x37')](_0x1824('0x14'),_0x59c3eb[_0x1824('0x38')](this,function(){if(!this[_0x1824('0xf')])return;this[_0x1824('0xd')]();this[_0x1824('0x11')][_0x1824('0x39')]();this[_0x1824('0xe')][_0x1824('0x10')]=![];}));_0x576d14[_0x1824('0x37')](_0x1824('0x3a'),_0x59c3eb[_0x1824('0x38')](this,function(){if(!this[_0x1824('0xf')])return;this[_0x1824('0xd')]();this[_0x1824('0x11')][_0x1824('0x39')]();this[_0x1824('0xe')][_0x1824('0x10')]=![];}));},'getE3CoordinateSystem':function(_0x56f41a){var _0x4504c9=function _0x4504c9(_0x180267){this[_0x1824('0x3b')]=_0x180267;this[_0x1824('0x3c')]=[0x0,0x0];};_0x4504c9[_0x1824('0x3d')]=function(_0x1a4547){_0x1a4547[_0x1824('0x3e')](function(_0x17e9bb){if(_0x17e9bb[_0x1824('0x3f')](_0x1824('0x40'))===_0x1824('0x6')){_0x17e9bb[_0x1824('0x40')]=new _0x4504c9(_0x56f41a);}});};_0x4504c9[_0x1824('0x41')]=function(){return['x','y'];};_0x4504c9[_0x1824('0x42')]=['x','y'];_0x4504c9[_0x1824('0x43')][_0x1824('0x42')]=['x','y'];_0x4504c9[_0x1824('0x43')][_0x1824('0x44')]=function setMapOffset(_0xeffdb8){this[_0x1824('0x3c')]=_0xeffdb8;};_0x4504c9[_0x1824('0x43')][_0x1824('0x45')]=function dataToPoint(_0x209327){var _0x2755d4={'type':_0x1824('0x46'),'x':_0x209327[0x0],'y':_0x209327[0x1],'spatialReference':new _0x4d54b1(0x10e6)};var _0x3676a6=_0x56f41a[_0x1824('0x47')](_0x2755d4);var _0x52b765=this[_0x1824('0x3c')];return[_0x3676a6['x']-_0x52b765[0x0],_0x3676a6['y']-_0x52b765[0x1]];};_0x4504c9[_0x1824('0x43')][_0x1824('0x48')]=function pointToData(_0x5d9368){var _0x4282c5=this[_0x1824('0x3c')];var _0x3a367d={'x':_0x5d9368[0x0]+_0x4282c5[0x0],'y':_0x5d9368[0x1]+_0x4282c5[0x1]};var _0x3a9399=_0x56f41a[_0x1824('0x49')](_0x3a367d);return[_0x3a9399['x'],_0x3a9399['y']];};_0x4504c9[_0x1824('0x43')][_0x1824('0x4a')]=function getViewRect(){return new graphic[(_0x1824('0x4b'))](0x0,0x0,this[_0x1824('0x3b')][_0x1824('0x23')],this[_0x1824('0x3b')][_0x1824('0x24')]);};_0x4504c9[_0x1824('0x43')][_0x1824('0x4c')]=function getRoamTransform(){return matrix[_0x1824('0x3d')]();};return _0x4504c9;}});});
查看原文前面两篇文章通过扩展EchartsLayer.js这个图层类,实现了使用ArcGIS JS API和eCharts,在二维和三维场景下绘制迁徙图和散点图。这篇文章继续通过绘制网络路径图的例子,再来验证下我们扩展的这个图层类是否可用,先来看下最终效果:
赞 1 收藏 1 评论 4
X北辰北 赞了文章 · 2020-03-26
随着浏览器和前端技术的发展,现在的前端项目越来越大、业务也越来越复杂,前端工程化已经成为一件势在必行的事情。
前端工程化其实就是软件工程在前端方向上的实施,不过篇幅有限,本文只讲解其中的几个点。
如果前端团队只有一两个人,规范的作用微乎其微;但团队人数超过一定数量时,规范的作用就显现出来了。举个例子,拿代码风格规范来说,有些人喜欢用两个空格缩进,有些人喜欢用四个空格缩进,如果这两个人合作写一个项目,即使嘴上不说,心里也会相互吐槽。所以统一规范是非常有必要的,在制定规范前,大家可以相互讨论,提意见;规范制定后,所有人都得遵守,强制执行。
本文说的规范主要包括以下几种:
代码规范的好处
每个程序员都不喜欢修改别人的代码,无论代码好坏,因为第一眼看上去没有熟悉感,下意识就会排斥。
所以当团队的成员都严格按照代码规范来写代码时,可以保证每个人的代码看起来都像是一个人写的,看别人的代码就像是在看自己的代码。
重要的是我们能够认识到规范的重要性,并坚持规范的开发习惯。
UI 规范需要前端、UI、产品沟通,互相商量,最后制定下来,建议使用统一的 UI 组件库。
制定 UI 规范的好处:
项目结构规范包括文件命名、文件目录组织方式,用 Vue 项目举个例子。
├─public
├─src
├─tests
一个项目包含 public(公共资源,不会被 webpack 处理)、src(源码)、tests(测试),其中 src 目录,又可以细分。
├─api (接口)
├─assets (静态资源)
├─components (公共组件)
├─styles (公共样式)
├─router (路由)
├─store (vuex)
├─utils (工具函数)
└─views (页面)
每个前端团队的项目命名及组织方式都可能不一样,以上仅提供参考。
良好的 git commit 规范,让人只看描述就能明白这个 commit 是干什么的,提高解决 BUG 的效率。
推荐阅读: git commit 提交规范。
除了上述几个规范,还有:
...
由于篇幅有限,并且研究不深,就只能到这了。
规范制定下来了,如何保证执行?
基本上都得靠代码审查以及测试人员测试,不过代码规范有一个工具能用得上,那就是 vscode + eslint 自动格式化代码。
推荐阅读: ESlint + VSCode自动格式化代码(2020)。
前端性能优化是一个老生常谈的问题,网上关于性能优化的文章与书籍也有很多。我之前还写过一篇关于 JavaScript 性能优化的文章。
性能优化包括代码优化和非代码优化。
...
...
推荐阅读:
测试是前端工程化建设必不可少的一部分,它的作用就是找出 bug,越早发现 bug,所需要付出的成本就越低。
在前端测试中,单元测试和集成测试一般用得比较多,工具也有很多,例如 Karma + Mocha + PhantomJS / Chai 等。
但是自动化测试工具可以说几乎没有,因为 UI 界面自动化测试太难了,目前只能靠人工测试。
得益于 node 和 webpack 的发展,自动化构建不再是梦。通过 webpack 以及相关配置,一行命令就可以做到下列所有事情:
自动化部署通过 Jenkins、Docker 等工具也可以很方便的实现。
推荐阅读:yumminhuang-如何理解持续集成、持续交付、持续部署?
性能监控
前端页面性能是一个非常核心的用户体验指标,影响到了用户的留存率,如果一个页面性能太差,用户等待时间过长,很有可能就直接离开了。
错误监控
因为测试永远无法做到100%覆盖,用户也不会总是按照我们所预期的进行操作,因此当生产环境出现 bug 时,需要对其进行收集。
监控是前端工程化建设中的最后一环,当项目上线后,通过监控系统可以了解到项目在生产环境中的运行情况,开发团队可以根据监控报告对项目做进一步的调整和优化。
目前市面上有大量成熟的监控产品可以使用,对于没有精力开发监控系统的团队来说,可以算是一个好消息。此前我还针对监控系统进行了一番调查和研究,并写了一篇文章,对监控系统原理有兴趣的可以看一下,前端性能和错误监控。
查看原文如果前端团队只有一两个人,规范的作用微乎其微;但团队人数超过一定数量时,规范的作用就显现出来了。举个例子,拿代码风格规范来说,有些人喜欢用两个空格缩进,有些人喜欢用四个空格缩进,如果这两个人合作写一个项目,即使嘴上不说,心里也会相互吐槽。所以统一...
赞 30 收藏 25 评论 0
X北辰北 赞了文章 · 2020-03-18
本文1454字,阅读大约需要4分钟。
总括: 本文以初学者的角度来阐述Javascript中柯里化的概念以及如何在工作中进行使用。
事亲以敬,美过三牲。
<!-- more -->
函数式编程是一种如今比较流行的编程范式,它主张将函数作为参数进行传递,然后返回一个没有副作用的函数,说白了,就是希望一个函数只做一件事情。
像Javascript,Haskell,Clojure等编程语言都支持函数式编程。
这种编程思想涵盖了三个重要的概念:
而这篇文章主要是想向大家讲清楚柯里化
这个概念。
首先我们先来看一个例子:
function sum(a, b, c) {
return a + b + c;
}
// 调用
sum(1, 2, 3); // 6
上述函数实现的是将a,b,c三个参数相加,改写为柯里化函数如下:
function sum(a) {
return function (b) {
return function(c) {
return a + b + c;
}
}
}
// 调用
let sum1 = sum(1);
let sum2 = sum1(2);
sum2(3); // 6
所谓柯里化就是把具有较多参数的函数转换成具有较少参数的函数的过程。
我们来一步步看上面那个柯里化函数做了什么,首先第一步调用了sum(1),此时变量sum1相当于:
sum1 = function(b) {
return function(c) {
// 注意此时变量a存在于闭包中,可以调用,a = 1
return a + b + c;
}
}
然后调用sum1(2),此时赋值给变量sum2相当于:
sum2 = function(c) {
// 变量a,b皆在闭包中, a = 1, b = 2
return a + b + c;
}
最后调用sum2(3),返回1 + 2 + 3的结果6;
这就是一个最简单的柯里化函数,是不是很简单呢?
那么问题来了,上面改写后的柯里化函数和原函数比起来代码多了不少,而且也不如原函数好理解,柯里化函数到底有什么用呢?
确实,柯里化函数在这里看起来的确是很臃肿,不实用,但在很多场景下他的作用是很大的,甚至很多人在不经意间已经在使用柯里化函数了。举一个简单的例子:
假设我们有一批的长方体,我们需要计算这些长方体的体积,实现一个如下函数:
function volume(length, width, height) {
return length * width * height;
}
volume(200, 100, 200);
volume(200, 150, 100);
volume(200, 50, 80);
volume(100, 50, 60);
如上计算长方体的体积函数会发现存在很多相同长度的长方体,我们再用柯里化函数实现一下:
function volume(length, width, height) {
return function(width) {
return function(height) {
return length * width * height;
}
}
}
let len200 = volume(200);
len200(100)(200);
len200(150)(100);
len200(50)(80);
volume(100)(50)(60);
如上,通过实现一个len200函数我们统一处理长度为200的长方体的体积,这就实现了参数复用。
我们再举一个只执行一次函数的例子:
function execOnce(fun) {
let flag = true;
return function() {
if (flag) {
fun && fun();
flag = false;
}
}
}
let onceConsole = execOnce(function() {
console.log('只打印一次');
});
onceConsole();
onceConsole();
如上,我们实现了一个execOnce函数,该函数接受一个函数参数,然后返回一个函数,变量flag存在闭包中,用来判断返回的函数是否执行过,onceConsole相当于:
let onceConsole = function() {
if (flag) {
(function() {
console.log('只打印一次');
})()
flag = false;
}
}
这也是柯里化函数的一个简单应用。
既然柯里化函数这么实用,那么我们能不能实现一个通用的柯里化函数呢?所谓通用,就是说该函数可以把函数参数转换为柯里化函数,看下第一版实现的代码:
// 第一版
var curry = function (fn) {
var args = [].slice.call(arguments, 1);
return function() {
var newArgs = args.concat([].slice.call(arguments));
return fn.apply(null, newArgs);
};
};
function add(a, b) {
return a + b;
}
var addFun = curry(add, 1, 2);
addFun() // 3
//或者
var addOne = curry(add, 1);
如上代码,我们接受一个函数作为参数,然后收集其它的参数,将这些参数传给这个函数参数去执行。但上面的代码有个问题,参数不够自由,比如我们想这么调用就会报错:
var addFun = curry(function(a, b,c) {
return a + b + c;
}, 1);
addFun(2)(3); // 报错 addFun(...) is not a function
这好像违背了我们参数复用的原则,改进如下:
function curry(fn, args) {
var length = fn.length;
args = args || [];
return function(...rest) {
var _args = [...args, ...rest];
return _args.length < length
? curry.call(this, fn, _args)
: fn.apply(this, _args);
}
}
var fn = curry(function(a, b, c) {
console.log(a + b + c);
});
fn('a', 'b', 'c'); // abc
fn('a', 'b')('c'); // abc
fn('a')('b')('c'); // abc
如上实现就很完善,该工具函数的实现总结起来就一句话:
利用闭包将函数的参数储存起来,等参数达到一定数量时执行函数。
柯里化是以闭包为基础的,不理解闭包可能对柯里化的理解有所阻碍,希望通过这篇文章能让各位了解和理解Javascript的柯里化。
能力有限,水平一般,欢迎勘误,不胜感激。
订阅更多文章可扫码关注我的公众号:
前言 本文1454字,阅读大约需要4分钟。 总括: 本文以初学者的角度来阐述Javascript中柯里化的概念以及如何在工作中进行使用。 原文地址:理解Javascript的柯里化 知乎专栏: 前端进击者 博主博客地址:Damonare的个人博客 事亲以敬,美过三牲。 <!-- more --> ...
赞 9 收藏 5 评论 0
X北辰北 赞了回答 · 2020-03-16
直接return出去是一个变量,而闭包是返回的一个执行环境。。。
直接return出去是一个变量,而闭包是返回的一个执行环境。。。
关注 12 回答 7
查看全部 个人动态 →
地理数据可视化
注册于 2020-03-03
个人主页被 204 人浏览
推荐关注