X北辰北

X北辰北 查看完整档案

成都编辑中国矿业大学  |  地理信息科学 编辑易智瑞信息技术有限公司成都分公司  |  技术工程师 编辑 www.xbeichenbei.com 编辑
编辑

视野决定广度,技术决定深度

个人动态

X北辰北 关注了专栏 · 2020-06-26

全栈修仙之路

聚焦全栈,专注分享 Angular、TypeScript、Node.js/Java 、Spring 技术栈等全栈干货。 欢迎小伙伴们关注公众号全栈修仙之路,一起升级打怪。

关注 7075

X北辰北 关注了专栏 · 2020-06-03

JAVA技术知识集合

主要介绍JAVA中的涉及的技术栈

关注 17

X北辰北 收藏了文章 · 2020-04-07

2020年前端面试复习必读精选文章【赠复习导图】

转载请联系,原文链接

前言

之前写过一篇 一年半经验如何准备阿里巴巴前端面试,给大家分享了一个面试复习导图,有很多朋友说希望能够针对每个 case 提供一个参考答案。

写答案就算了,一是精力有限,二是我觉得大家还是需要自己理解总结会比较好

给大家整理了一下每个 case 一些还算不错的文章吧(还包括一些躺在我收藏夹里的好文章),大家可以自己看文章总结一下答案,这样也会理解更深刻。

并不是所有文章都需要看,希望是一个抛砖引玉的作用,大家也可以锻炼一下自己寻找有效资料的能力 ~

( 文章排序不分前后,随机排序 ~


建议收藏文章,结合复习导图食用,效果更佳。

完整复习导图全展开太大了,可关注公众号「前端试炼」回复【面试】获取。

1. JavaScript 基础

1.1 执行上下文/作用域链/闭包

1.2 this/call/apply/bind

1.3 原型/继承

1.4 Promise

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');
  })
}

1.5 深浅拷贝

1.6 事件机制/Event Loop

这个知识点真的是重在理解,一定要理解彻底

for (const macroTask of macroTaskQueue) {
  handleMacroTask();
  
  for (const microTask of microTaskQueue) {
    handleMicroTask(microTask);
  }
}

1.7 函数式编程

1.8 Service Worker / PWA

1.9 Web Worker

1.10 常用方法

太多了... 总的来说就是 API 一定要熟悉...

2. CSS 基础

3. 框架(Vue 为主)

3.1 MVVM

3.2 生命周期

3.3 数据绑定

3.4 状态管理

3.5 组件通信

3.6 Virtual DOM

3.7 Diff

3.8 Vue 计算属性 VS 侦听属性

3.9 React Hooks

3.10 React Hoc/Vue mixin

3.11 Vue 和 React 有什么不同

从思想、生态、语法、数据、通信、diff等角度自己总结一下吧。

4. 工程化

4.1 Webpack

4.2 Babel

4.3 模板引擎

4.4 前端发布

4.5 weex

4.6 前端监控

5. 性能优化

5.1 打包阶段

5.2 其它优化

6. TypeScript

7. 网络

7.1 HTTP

7.2 HTTPS/HTTP2

7.3 DNS

7.4 TCP

7.5 CDN

7.6 经典题

8. 设计模式

9. 数据结构/算法

10. 安全

11. Node

12. 项目/业务

思考题,自由发挥

13. 其它

14. 面试

15. 书单

推荐一些值得看的书,基本都是我看完或者有翻过几页觉得不错但是还没时间看的书。

15.1 JavaScript

  • JavaScript 高级程序设计(高程就不多说了,第四版有英文版)
  • JavaScript 设计模式
  • 你不知道的 JavaScript
  • JavaScript 语言精粹
  • 高性能 JavaScript
  • Learning TypeScript 中文版
  • 深入理解 ES6
  • ES6 标准入门
  • 深入理解 JavaScript 特性

15.2 CSS

  • CSS 权威指南(建议看英文版)
  • 精通 CSS 高级 Web 标准解决方案
  • CSS 世界(张鑫旭老师的大作,但是建议需要有一定 CSS 实践后再看)

15.3 Node

  • Node.js 实战
  • 了不起的 Node.js

15.4 计算机基础

  • 大话数据结构
  • 图解 HTTP
  • 计算机/程序是怎样跑起来的
  • 学习 JavaScript 数据结构与算法

15.5 工程化/浏览器/软技能

  • 前端工程化体系设计与实践
  • webpack 实战:入门、进阶与优化(了解一下 webpack 的所有会涉及到的知识点)
  • WebKit 技术内幕(讲浏览器的,挺好的)
  • 重构:改善既有代码的涉及
  • 码农翻身
  • 程序员思维修炼
  • 编码:隐匿在计算机软硬件背后的语言
  • 写给大家看的设计书
  • 技术之瞳:阿里巴巴技术笔试心得

结束语

上文整理了网上的一些相关文章和躺在我收藏夹里精选文章,有一些文章还没看,还需要持续学习呀 ~

放弃了假期快落的岛上生活(动森),吐血整理这份资料,希望对大家有所帮助~

欢迎关注公众号「前端试炼」,回复【面试】获取完整复习导图。公众号平时会分享一些实用或者有意思的东西,发现代码之美。专注深度和最佳实践,希望打造一个高质量的公众号。偶尔还会分享一些摄影 ~

扫码_搜索联合传播样式-标准色版.png

也可以扫码加我微信,拉你进交流划水聊天群,有看到好文章/代码都会发在群里。

查看原文

X北辰北 关注了专栏 · 2020-03-31

前端进阶

前端进阶

关注 7839

X北辰北 发布了文章 · 2020-03-31

【ArcGIS JS API + eCharts系列】实现二、三维网络路径图的绘制

概述

前面两篇文章通过扩展EchartsLayer.js这个图层类,实现了使用ArcGIS JS API和eCharts,在二维和三维场景下绘制迁徙图和散点图。这篇文章继续通过绘制网络路径图的例子,再来验证下我们扩展的这个图层类是否可用,先来看下最终效果:
01.png
10.png

实现思路

迁徙图、散点图和网络路径图这种图表跟地理坐标关系紧密,所以仅仅通过第一篇二维普通图表绘制的方式是无法实现这类图表绘制的,所以就需要我们来扩展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;}});});
查看原文

赞 1 收藏 1 评论 4

X北辰北 关注了用户 · 2020-03-30

谭光志 @woai3c

公众号:前端编程技术分享

知乎:https://www.zhihu.com/people/...

github: https://github.com/woai3c

关注 6545

X北辰北 赞了文章 · 2020-03-26

前端工程化建设

更多文章

随着浏览器和前端技术的发展,现在的前端项目越来越大、业务也越来越复杂,前端工程化已经成为一件势在必行的事情。

前端工程化其实就是软件工程在前端方向上的实施,不过篇幅有限,本文只讲解其中的几个点。

团队规范

如果前端团队只有一两个人,规范的作用微乎其微;但团队人数超过一定数量时,规范的作用就显现出来了。举个例子,拿代码风格规范来说,有些人喜欢用两个空格缩进,有些人喜欢用四个空格缩进,如果这两个人合作写一个项目,即使嘴上不说,心里也会相互吐槽。所以统一规范是非常有必要的,在制定规范前,大家可以相互讨论,提意见;规范制定后,所有人都得遵守,强制执行。

本文说的规范主要包括以下几种:

  • 代码规范
  • UI 规范
  • 项目结构规范
  • git commit 规范

代码规范

代码规范的好处

  • 规范的代码可以促进团队合作
  • 规范的代码可以降低维护成本
  • 规范的代码有助于代码审查
  • 养成代码规范的习惯,有助于程序员自身的成长

每个程序员都不喜欢修改别人的代码,无论代码好坏,因为第一眼看上去没有熟悉感,下意识就会排斥。

所以当团队的成员都严格按照代码规范来写代码时,可以保证每个人的代码看起来都像是一个人写的,看别人的代码就像是在看自己的代码。

重要的是我们能够认识到规范的重要性,并坚持规范的开发习惯。

UI 规范

UI 规范需要前端、UI、产品沟通,互相商量,最后制定下来,建议使用统一的 UI 组件库。

制定 UI 规范的好处:

  • 统一页面 UI 标准,节省 UI 设计时间
  • 提高前端开发效率

项目结构规范

项目结构规范包括文件命名、文件目录组织方式,用 Vue 项目举个例子。

├─public
├─src
├─tests

一个项目包含 public(公共资源,不会被 webpack 处理)、src(源码)、tests(测试),其中 src 目录,又可以细分。

├─api (接口)
├─assets (静态资源)
├─components (公共组件)
├─styles (公共样式)
├─router (路由)
├─store (vuex)
├─utils (工具函数)
└─views (页面)

每个前端团队的项目命名及组织方式都可能不一样,以上仅提供参考。

git commit 规范

良好的 git commit 规范,让人只看描述就能明白这个 commit 是干什么的,提高解决 BUG 的效率。

推荐阅读: git commit 提交规范

其他规范

除了上述几个规范,还有:

  • 前后端接口规范
  • 文档规范
  • 代码分支规范

...

由于篇幅有限,并且研究不深,就只能到这了。

执行

规范制定下来了,如何保证执行?

基本上都得靠代码审查以及测试人员测试,不过代码规范有一个工具能用得上,那就是 vscode + eslint 自动格式化代码。

推荐阅读: ESlint + VSCode自动格式化代码(2020)

性能优化

前端性能优化是一个老生常谈的问题,网上关于性能优化的文章与书籍也有很多。我之前还写过一篇关于 JavaScript 性能优化的文章

性能优化包括代码优化和非代码优化。

代码优化
  • 复用代码
  • 避免全局变量
  • 使用事件委托
  • 使用Object/Array字面量
  • 位操作在JavaScript中性能非常快,可以使用位运算来代替纯数学操作

...

非代码优化
  • 减少HTTP请求次数
  • 使用CDN
  • 使用缓存
  • 压缩资源
  • css 放头部,js 放底部
  • 减少 DOM 操作

...

推荐阅读:

测试

测试是前端工程化建设必不可少的一部分,它的作用就是找出 bug,越早发现 bug,所需要付出的成本就越低。

在前端测试中,单元测试和集成测试一般用得比较多,工具也有很多,例如 Karma + Mocha + PhantomJS / Chai 等。

但是自动化测试工具可以说几乎没有,因为 UI 界面自动化测试太难了,目前只能靠人工测试。

构建、部署

得益于 node 和 webpack 的发展,自动化构建不再是梦。通过 webpack 以及相关配置,一行命令就可以做到下列所有事情:

  • 代码检查
  • 单元测试、集成测试
  • 语言编译
  • 依赖分析、打包、替换等
  • 代码压缩、图片压缩等

自动化部署通过 Jenkins、Docker 等工具也可以很方便的实现。

推荐阅读:yumminhuang-如何理解持续集成、持续交付、持续部署?

性能和错误监控

性能监控
前端页面性能是一个非常核心的用户体验指标,影响到了用户的留存率,如果一个页面性能太差,用户等待时间过长,很有可能就直接离开了。

错误监控
因为测试永远无法做到100%覆盖,用户也不会总是按照我们所预期的进行操作,因此当生产环境出现 bug 时,需要对其进行收集。

监控是前端工程化建设中的最后一环,当项目上线后,通过监控系统可以了解到项目在生产环境中的运行情况,开发团队可以根据监控报告对项目做进一步的调整和优化。

目前市面上有大量成熟的监控产品可以使用,对于没有精力开发监控系统的团队来说,可以算是一个好消息。此前我还针对监控系统进行了一番调查和研究,并写了一篇文章,对监控系统原理有兴趣的可以看一下,前端性能和错误监控

查看原文

赞 30 收藏 25 评论 0

X北辰北 赞了文章 · 2020-03-18

理解Javascript的柯里化

前言

本文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的柯里化。

能力有限,水平一般,欢迎勘误,不胜感激。


订阅更多文章可扫码关注我的公众号:

  • 回复「666」,可领取一揽子前端技术书籍;

https://image.damonare.cn/cainiaoxueqianduan.png

查看原文

赞 9 收藏 5 评论 0

X北辰北 赞了回答 · 2020-03-16

解决javascript 闭包问题,为什么要写成闭包的形式?

直接return出去是一个变量,而闭包是返回的一个执行环境。。。

关注 12 回答 7

认证与成就

  • 获得 5 次点赞
  • 获得 3 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 3 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

  • GeoV

    地理数据可视化

注册于 2020-03-03
个人主页被 204 人浏览