董懂 发布的文章

1、如何使用

很多场景,比如给ajax请求传递参数时,参数往往是一个对象,对象的属性个数不是固定的,需要动态添加:

let params = {
    id: 1
};

if(name){
    params.name = name;
}

if(address){
    params.address= address;
}

现在ES6提供了扩展运算符,以上的代码可以改成下面这样:

let params = {
    id: 1,
    ...(name && {name}),
    ...(address && {address}),
}

如果nameaddress的值都存在且不是空字符串,结果就会是这样:

{
    id: 1,
    name: 'dong',
    address: '赤水沟子'
}

如果它们的值为,就会不存在nameaddress这两个属性:

{
    id: 1
}

2、原理

下面以name为例,说明它的原理。

如果name不为空,则

name && {name}

相当于

name && {name: name} //上面是简写

相当于

if(name){
    return {
        name: name
    }
}

如果name没有值,那么

...(name && {name})

相当于

...(false)

扩展运算符有以下规定:

1、如果扩展运算符后面是一个空对象,则没有任何效果
2、如果扩展运算符后面不是对象,则会自动将其转为对象

根据第2点,

...(false)

等同于

...Object(false)

等同于

...Boolean {false}

Boolean {false}没有任何属性,所以又等同于

...{}

根据第1点可以知道,这没有任何效果,不会向params添加任何属性。

END

一个程序员经常脖子疼,他每天的工作就是敲键盘写代码和查资料。今天他突然发现大多数网页的主体内容都在屏幕左侧,他坐在屏幕中央时,不得不歪着脖子看屏幕,这导致他的脖子经常酸痛。在写代码的时候他又发现,键盘的主按键区也在键盘的左侧,他把键盘摆在正中央的时候,不得不歪着身子码字,本来酸痛不已的脖子感觉好像滑丝了,摇摇欲坠,不太好控制了。

就在他写这些字的时候,他也是歪着脖子,忍受着脖子疼写的,因为编辑器窗口在屏幕最左侧。

经常看到这样的JavaScript代码:

var o = {
    f: function(){
        console.log(this);
    }
}

(0, o.f)();

这样做的目的是改变f的执行环境,也就是改变this。

直接调用o.f:

o.f();
//{f: ƒ}

再试试间接调用:

(0, o.f)();
//Window...

可以看出this已经是Window了。

逗号运算符会从左往右求值,并返回最后一个表达式的值,(0, o.f)返回的是一个函数,此时这个函数的作用域是全局,相当于:var f = o.f
必须要用0吗?其实任何表达式都可以:

(1, o.f);
(1+1, o.f);
(true, o.f);

()是不是必须的呢?要调用返回的函数,或者将返回的函数赋值给某个变量,括号就是必须的。下面的代码肯定是不能正确运行的:

0, o.f(); //this指向o
var f = 0, o.f //Unexpected token '.'

我们需要()来将语句转换为表达式,这也是经常用到的方法,比如下面的代码,需要将x = 2这条语句转换为表达式:

true && (x = 2)

END

JavaScript获取时间戳有多种方式。这里有一个具有纪念意义的时间:

var now = new Date('2022/02/02 22:22:22:222');
//Wed Feb 02 2022 22:22:22 GMT+0800 (中国标准时间)

我们想看看它的时间戳是不是也如此独特。下面是如何获取它的时间戳。

方法1:Dateparse方法

Date.parse(now);
//1643811742000

Date.parse接受一个表示时间的字符串,所以这里的now先转换成了字符串Wed Feb 02 2022 22:22:22 GMT+0800 (中国标准时间),然后再被解析成时间戳。因为每个浏览器实现它的方式不一致,所以建议不要用Date.parse解析时间字符串。见:Date.parse() - MDN
要注意的是,此方法返回的毫秒数都是0.

方法2:Date实例getTime方法

now.getTime();
//1643811742222

这个方法实际上跟方法1是等价的,方法1是显式调用,方法2是隐式调用,但此方法返回正常的毫秒数。

方法3:Date实例的valueOf方法

now.valueOf();
//1643811742222

这个方法返回一个Date对象的原始值。Date对象的原始值是基于Unix Time Stamp,即自1970年1月1日(UTC)起经过的毫秒数。

方法4:+操作符获取时间戳

+now;
//1643811742222

这个方法实际上调用的是now.valueOf()
+运算符在这里是一元运算符,会将后面的操作数转换成数字,等于Number()函数。数字是一种原始值。在JavaScript内部,会调用toPrimitive把对象转换成原始值,形式如下:

toPrimitive(input,preferedType?)

preferedType可选numberstring+会将preferedType看作是number,将按照下面的规则转换:

  1. 如果input是原始值,直接返回这个值;
  2. 否则,如果input是对象,调用input.valueOf(),如果结果是原始值,返回结果;
  3. 否则,调用input.toString()。如果结果是原始值,返回结果;
  4. 否则,抛出错误。

+now按照规则2调用了now.valueOf()
注意,Date.prototype.valueOf重写了Object.valueOf,所以会返回一个时间戳。

结果证明now的时间戳并不独特:(

END

0、保存Map实例

NOTE:

如何引入天地图SDK请参考以前的文章

首先要做的肯定是保存Map实例,可以保存在Vue实例上:

this.Map = new T.Map(......);

或者保存在window上:

window.Map = new T.Map(......);

1、创建标注

创建标注使用的是工具类。下面都用点(Marker)来作为例子。

在页面中创建一个按钮,使用它的click事件来打开标注工具

<button @click="btnClick">添加点</button>

为了看起来不违和,你可以把这个按钮的样式做成和地图原生控件一样。

click事件中打开标注工具:

btnClick(){
  //如果标注工具已经存在,先关闭它
  window.marktool && window.marktool.close();

  //创建一个Icon实例
  let icon = new T.Icon({
    iconUrl: 'https://x.y.z/1.png'
  });

  //实例化工具
  let marktool = new T.MarkTool(window.Map, {
    icon,
    follow: true
  });

  marktool.open();
  window.marktool = marktool;
}

现在,点击添加点按钮的时候,鼠标指针将变成你定义的icon的样式,点击地图,就会在点击的位置留下一个标记。

2、给标记添加自定义的数据

标记肯定代表了某个实际的东西,需要补充额外的内容。可以在上一步给marktool注册一个mouseup事件,给刚刚创建的标记添加一个click事件,以便我们进行更多的操作:

//别忘了先保存一下Vue实例this:
let _this = this;
//onMarkMarked是Vue的Method
//e.currentMarker代表当前标记
marktool.addEventListener('mouseup', function (e) {
  _this.onMarkMarked(e.currentMarker);
});

VueonMarkMarked方法中:

onMarkMarked(marker){
  let _this = this;
  //直接添加数据
  marker.MyData = {foo: 'bar', baz: 'balabala...'};
  //保存marker,使页面中任何代码都能访问,就能任意赋值了
  //但要注意,currentMark保存的是当前标记,创建新标记时,它代表的是新的标记
  _this.currentMark = marker;
}

Vue的其他方法中给它赋值:

onModelClosed(){
  this.currentMark.myname = this.inputName;
  //......

  //可以在控制台看到marker实例中包含自定义的数据
  console.log(this.currentMark);
}

3、保存标记数据

首先获取到标记:

let markers = window.Map.getOverlays();

返回的是一个标记数组,如果有多个标记,根据需要,可能需要循环处理。这里假设只有一个marker标记。
标记信息含有很多无用的信息,我们需要的一般都是标记的坐标、自定义的数据。标记的样式由于是提前定义好的,所以并不会通过标记获取。

let lnglat = markers[0].getLnglat();
let { myname, MyData } = markers[0];

此时就可以保存到数据库了。


END

堵车是城市里的人几乎每天都要经历的事情,即使是身处经济一般的小县城,每天也要堵那么一段时间,赶上刮风下雨、中秋国庆,那必定会非常堵。

今天看到一则新闻,说是外国有的城市是无车城市,市区不允许开私家车。这样做效果肯定非常好,但是也有很多缺点,比如可能会降低城市运行效率,可能有许多特殊的人群必须要开车……我觉得这样还是太激进了。我有一个想法:城市中只允许开微型车。

每当我在路上看到一个人开着宽大的SUV或者中大型轿车,仅仅是一个人去上班的时候,我就觉得,这是不是太浪费了?即使是小型车,往往车长也有4米多,而大多数车只坐一个人。上下班拼车虽然能提高利用率,但又消耗额外的时间。实际上上下班通勤开微型车就足够了。五菱宏光mini ev就是一款非常好的车型,能够满足绝大多数人的需要。像Polo、飞度之类的,我都觉得有点长了。

说到这里我还有一些感想:为什么很多人对于一些事那么讲究呢?酒店要星级的,车要商务的……一个人真的需要这些东西吗?世界上绝大多数人的价值观是不是被少数人绑架了……

UPDATE:8/16

天地图的JavaScript3.0 API中提供了一些方法,在4.0的文档中没有,但是仍然可用,比如设置地图的样式:

map.setMapType(...)

1、引入天地图API

按需引入,在需要用到天地图API的时候,动态创建script标签:

//某组件
...
mounted() {
    this.initMap();
},
...
//methods
initMap(){
    let script = document.getElementById('MapDivId');
    if (script) {
        return this._initMap();
    }

    const _this = this;
    script = document.createElement('script');
    script.id = 'MapDivId';
    script.src = 'https://tianditu.gov.cn/xxxxxxxx';
    script.onload = function () {
        _this._initMap();
    };

    document.body.appendChild(script);
}

二、初始化地图

...
_initMap() {
    //添加卫星图层,下面这个url是固定的,我只找到这种默认显示卫星图层的办法
    let satelliteLayerUrl = "http://t0.tianditu.gov.cn/img_w/wmts?" + "SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles" + "&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=您的密钥";
    let layer = new T.TileLayer(satelliteLayerUrl, { minZoom: 3, maxZoom: 18 });
    window.GIS.Map = new T.Map('Map', { layers: [layer] });
    // 定位到一个默认的坐标,例如北京。这里的坐标是虚构的
    window.GIS.Map.centerAndZoom(new T.LngLat(123.45678, 30.1234), 12);

    //添加地图类型控件
    let ctrlMapType = new T.Control.MapType([
        {
            title: '卫星混合',
            icon: 'http://api.tianditu.gov.cn/v4.0/image/map/maptype/satellitepoi.png',
            layer: TMAP_HYBRID_MAP
        },
        {
            title: '地图',
            icon: 'http://api.tianditu.gov.cn/v4.0/image/map/maptype/vector.png',
            layer: TMAP_NORMAL_MAP
        }
    ]);
    window.GIS.Map.addControl(ctrlMapType);

    //添加缩放控件
    let ctrlZoom = new T.Control.Zoom();
    window.GIS.Map.addControl(ctrlZoom);
    ctrlZoom.setPosition(T_ANCHOR_BOTTOM_RIGHT);

    //添加比例尺控件
    let ctrlScale = new T.Control.Scale();
    window.GIS.Map.addControl(ctrlScale);
},
...

三、添加各种标注工具

首先编写HTML结构,定位到地图上的某个位置,并绑定一个click事件,这部分很简单,效果如下:

1.png

然后编写click事件代码:

<span class="icon xxxx" @click="openPolygonTool"></span>
openPolygonTool() {
    if (window.GIS.handler) window.GIS.handler.close();
    let handler = new T.PolygonTool(window.GIS.Map);
    handler.open();
    window.GIS.handler = handler;
}

效果如下:

2.png

四、保存标注数据

一般在打开标注工具的时候,注册一个相关事件,这样,在结束标注的时候,就能获取到标注的数据。多边形的结束事件是draw

openPolygonTool() {
    ...
    handler.addEventListener('draw', function(e){
        console.log(e);
    });
    handler.open();
    ...
}

到这里,就可以把需要的数据存储起来了。

END

还没仔细研究antd vue的代码,所以动态菜单图标的显示机制还没弄明白,时间仓促,临时找到一个解决办法。

1、选好所需图标

iconfont网站上选好要用的图标,放到一个项目中,在项目设置中,把图标前缀改成anticon,更新代码后,选择下载到本地或者复制在线地址。

2、在public/index.html文件中引用图标

如果下载到本地,就把资源复制到public/static/下,然后在index.html中引入,或者直接引入复制的在线网址。

3、在菜单中使用图标

在系统设置-菜单管理中,在图标一栏填入图标的名字,注意不要带前缀。图标的名字可以在iconfont网站上,鼠标放到项目中的图标上,直接点击复制
当然,也可以在页面的任意位置用任意标签使用图标:

<span class="anticon anticon-tubiao"></span>

4、在任意全局的地方(比如index.html中)加入下面的css代码:

<style>
  .anticon:before {
    display: block !important;
  }
</style>

为什么要在iconfont的项目设置中修改图标前缀呢?因为antd vue图标的class就是anticon
重置.anticon:before的样式,目前也没有发现任何副作用。

有更好的方法欢迎留言告诉我。

END

在一个宽度100%,也就是宽度不固定的容器中,里面有横向排列并且会换行的列表项,列表项的宽度固定,所以每行列表项的个数随着容器的宽度变化:

<div class="box">
    <div class="item"></div>
    <div class="item"></div>
    <div class="item"></div>
    <div class="item"></div>
    ....
</div>
.box{
    display: flex;
    flex-wrap: wrap;
}
.item{
    width: 200px;
    height: 300px;
    margin-bottom: 20px;
}

在列表项不超过一行的时候表现很正常,但是在出现换行的情况下,每行最后可能会出现比较大的空白,这是因为虽然这行剩余的空间放不下一个.item了,但可能只比.item的宽度小一点点:

1.png

我们的目的是,让列表两端对齐,每个项的边距一样大,最后一行如果有空缺,那么就左对齐:

3.png

如果给.box设置下面的样式会怎样呢:

.box {
    ......
    justify-content: space-between;
    ......

如果一共5个.item,每行显示3个.item,结果会像这样:

2.png

解决办法就是补齐缺少的.item。因为第二行缺少1个.item,我们可以给它补上一个看不见的、高度是0的.item

<div class="box">
    ......
    <div class="item"></div>
    <div class="item placeholder"></div>
    ....
</div>
.placeholder{
    visibility: hidden;
    height: 0;
}

这时候就达到了最初的目的:

3.png

那如果每行显示4个、5个项,最后一行缺少2个、3个……怎么办?
如果你能知道你的列表最多有几个列,就可以补上几个.placeholder。如果你不确定,那么可以补多一些,保证补的.placeholder数量多于可能会出现最多的列数。因为.placeholder的高度是0,所以不会对垂直方向的布局产生影响。

这样有一个缺点:随着改变窗口大小,会发现.item之间的空隙忽大忽小。这是因为随着.box的宽度变化,行剩余的空间随之变化,行中的.item的间隙是flex动态计算的,所以会不断变化。如果你不能接受这一点,可以继续往下看。

我们设置固定的边距,并且让列表看起来两端对齐:

.box {
    ......
    /* 删掉或注释掉下面一行 */
    /* justify-content: space-between; */
    /* 设置负margin是为了让列表看上去两端对齐,两边不出现空白 */
    margin: 0 -10px 0;
    ......
}

.item可以扩展宽度并设置基本宽度:

.item {
    flex: 1 200px;
    /* 删掉或注释掉下面一行 */
    /* width: 200px */
    margin: 0 10px 20px;
}

这样,随着.box宽度的变化,.item之间的边距不再变化。但是每个.item的宽度又会跟着变化了,道理同上。不过,宽度变化视觉上要好于边距变化,个人倾向于这种方法。

下面是第二种方法的完整代码:

<div class="box">
    <div class="item"></div>
    <div class="item"></div>
    <div class="item"></div>
    <div class="item"></div>
    <!-- 个数根据需要增减 -->
    <div class="item placeholder"></div>
    <div class="item placeholder"></div>
    ....
</div>
.box{
    display: flex;
    flex-wrap: wrap;
    margin: 0 -10px;
}
.item{
    flex: 1 200px;
    height: 300px;
    margin: 0 10px 20px;
}

END

现在的各种APP,只要注册,就必须把自己的手机号交给它们,想要使用某些功能,还必须要实名认证,甚至还要刷脸认证,理由是“根据监管……”也不知道是真的还是假托这样的理由。更有甚者,比如支付宝,不仅要你的身份信息、人脸信息,还千方百计引诱你交出车牌号、社保卡号、公积金账户、教育经历、学历、入职公司……等一切有价值的隐私信息,真让人害怕。

现在国内的APP在滥用用户个人信息方面已经不成样子了,用户毫无隐私可言。各大互联网公司互通有无,你在这个平台上浏览了某个商品,另一个APP上马上为你推荐这类商品。这还不算什么,要是有无良公司把你的信息卖给所谓的金融公司、野鸡招聘网……等等,那你就面临被诈骗的风险。

昨天滴滴被国安部查了,事情非常严重。滴滴掌握的这些信息对国家安全都有影响。

既然这样,为什么不让国家牵头,建立一个统一的认证中心呢?这个中心对外提供接口,接受加密的用户验证信息,在此过程中确保互联网公司不能获取到用户信息;验证通过后,只要返回true或者false来告诉互联网公司验证结果。这样一来,互联网公司没理由再收集用户信息了,国家又能提供权威的验证,又掌握了公民的信息安全,技术上难度也不大,这对国家对个人都有好处,唯一不高兴的肯定是那些大公司了。

不过这样做也不能解决所有问题。就比如钉钉,现在小学校都在用它留作业、交作业,家长也需要参与进去。这样,钉钉能获取的信息简直太丰富了:孩子的基本信息、位置信息、家庭信息、父母信息、孩子的学校班级、学习成绩、班级排名,进而分析出学生的学习习惯、生活习惯、性格,学校的基本信息、教学状况等、家长的财务状况、文化水平……,通过关联其他APP还能获得更多的信息,简直取之不尽……

希望国家重视这类信息的监管,这些信息掌握在国家手里我是放心的。