持续学习JavaScript

学习JavaScript,就从MDN开始,其中包含了大量的值得深入学习和理解的内容。在这篇博客我介绍一些最近看到的。

1,class expression

首先就是类表达式https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/class 说实话这是我最近才知道的用法。它的语法如下,非常类似正常的类定义语句(class declaration statement),但是可以赋值给一个变量。如果使用typeof查一下就知道,MyClass类型还是function

const MyClass = class [className] [extends otherClassName] {
    // class body
};

类表达式可以重复定义,这个和类定义方式不同。另外className可以省略,如果命名了,那这个名字也只在class body有效,这个和function expression是一样的 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function

2, this

this在JavaScript面试中几乎是必考的点,https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this 这里值得注意的几点,一个是call和apply怎么记忆呢?最简单的办法就是apply第二个参数接受的是数组类型,也就是array,a对a就能容易记住了。

下面这段代码,其中有个知识点就是如果第一个参数不是object类型,就会调用内部的ToObject函数,比如7就会变成Number,而字符串字面值‘foo’就会转成String。

function bar() {
  console.log(Object.prototype.toString.call(this));
}
bar.call(7);     // [object Number]
bar.call('foo'); // [object String]

而bind需要注意的是只会绑定一次

function f() {
  return this.a;
}
var g = f.bind({a: 'azerty'});
console.log(g()); // azerty
var h = g.bind({a: 'yoo'}); // bind only works once!
console.log(h()); // azerty

3,Destructuring assignment解构赋值

这个词有点难理解,多看看里面的示例会好很多,大致上分成两类数组解构和对象解构 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment

下面这个用法有点费解,最后得到的两个变量不是a和b,而是aa和bb。

const {a: aa = 10, b: bb = 5} = {a: 3};
console.log(aa); // 3
console.log(bb); // 5

继续加深理解

const user = {
  id: 42,
  displayName: 'jdoe',
  fullName: {
    firstName: 'John',
    lastName: 'Doe'
  }
};
function userId({id}) {
  return id;
}
function whois({displayName, fullName: {firstName: name}}) {
  return `${displayName} is ${name}`;
}
console.log(userId(user)); // 42
console.log(whois(user));  // "jdoe is John"

注意与下面的例子的区别,主要在参数的声明格式上。一个用的等号,一个用的冒号。

function drawChart({size = 'big', coords = {x: 0, y: 0}, radius = 25} = {}) {
  console.log(size, coords, radius);
}
drawChart({
  coords: {x: 18, y: 30},
  radius: 30
});

这个例子更复杂些,属性是计算得来

let key = 'z';
let {[key]: foo} = {z: 'bar'};
console.log(foo); // "bar"

还可以用rest 属性… 顺序也没有关系,只要能和属性名对上就行

let {c, a, ...rest} = {a: 10, b: 20, c: 30, d: 40}
a; // 10
c; // 30
rest; // { b: 20, d: 40 }
const foo = { 'fizz-buzz': true };
const { 'fizz-buzz': fizzBuzz } = foo;
console.log(fizzBuzz); // "true"

数组解构和对象解构组合起来

const props = [
  { id: 1, name: 'Fizz'},
  { id: 2, name: 'Buzz'},
  { id: 3, name: 'FizzBuzz'}
];
const [,, { name }] = props;
console.log(name); // "FizzBuzz"

在JavaScript中应用Mixin模式

编程中Mixin是什么?我最近看到这个词,有点感兴趣起来。我以前也见过这个词,依稀记得是在介绍Ruby的文章当中,但是JavaScript中的Mixin干什么用呢?有些公众号提到express.js和vue.js的实现应用了mixin模式,我最近主要也是做这些方面,所以得关注了解啊。

最权威的还是先看看wiki怎么说:https://en.wikipedia.org/wiki/Mixin 头一段说明有点绕,不是很容易理解,那就看看JS代码吧。Wiki列出了三种实现,分别是extend,Object.assign以及Flight-Mixin。

第一种extend方式的参考实现,就是把源对象上面的key,逐个赋值到目标对象上。其它的两种大致也都是如此,Flight-Mixin模式其实就是IIFE直接执行的变形。

剥掉了神秘感,剩下的就很简单了。有什么限制呢?首先,mixin的源对象操作this的属性,一定要在目标对象上存在,否则就会出现undefined问题了。其次mixin在JS这种动态类型的编程语言是合适的,如果用在c#这样的静态类型语言,就可以用扩展方法来实现,更灵活的就得用dynamic来定义this了。

那mixin的用处,大致就是mixin源对象可以定义behavior,然后可以动态的绑定到其它对象上,只要这些对象满足mixin的constraints就可以了,把行为抽象出来。就此推论,mixin也很容易实现成decorator。mixin还要注意的一点是,它实质是一种浅拷贝,浅拷贝可能有的问题它也会有。

在core-decorators项目中的代码更加完整 https://github.com/jayphelps/core-decorators/blob/5b754256a30c23a0aef846c1b45f261e0c7b21a2/src/mixin.js,其中使用了getOwnPropertyDescriptors以及defineProperty这样更为特定的函数处理不同的情况。

mixin有什么问题么?当然有,什么设计模式都有局限性和适用的场景。比如react就提到https://reactjs.org/blog/2016/07/13/mixins-considered-harmful.html 简单说,mixin在大的codebase中增加了隐形依赖复杂度(Mixins introduce implicit dependencies),命名冲突问题(Mixins cause name clashes),滚雪球复杂度(Mixins cause snowballing complexity)。所以在express.js这样的轻量级框架中使用mixin说的通,但是复杂的企业级应用,就不得不考虑复杂度带来的各种问题。

我的建议是如果软件超过7个人开发,那就考虑angular/TypeScript/rxjs这一套。如果是小项目,可以考虑react或者vue.js,但是最好也配上babel加ES最新标准。

感兴趣的可以留言讨论。

通过awesome和alternative学技术

编程技术学习到某个程度,就需要扩展眼界,这样才能把自己锻造成T字形人才,有深度有广度。

我这里推荐两个方法可以扩展某个领域的知识面,一个是Awesome,一个是alternative。

比如我想了解更多关于angular方方面面的知识,可以直接在github敲入awesome angular搜索,就可以找到这个repo https://github.com/PatrickJS/awesome-angular 如果感兴趣rust,就能找到https://github.com/rust-unofficial/awesome-rust 。Awesome类知识汇集有点类似百科全书,或者是以前的dmoz(还有人知道这个么?),有空就可以瞅瞅,对于某个项目/库感兴趣了就深挖一下。

alternative的用法又不一样了。我经常收到领导的要求,其实也是架构师常见的工作,就是比较不同产品或者框架的优缺点,做一些比较或者雷达图什么的。这时候就可以搜索关键字加“alternative”,比如想了解aws的竞争者就在谷歌搜“aws alternative”,然后点开结果页面看看。还有一个网站https://alternativeto.net/ 就是专门做同类型软件比较打分的,还算客观。

周末玩玩技术

我很久以前玩过一段时间的google appengine,用它来连接rss填补google reader的空白。前两天趁着周末又捡出来把玩了一下,现在的感觉却不怎么好用了,难怪google的云计算赶了个大早,却被后来者azure居上。

简单的crud应用,比如todo或者记事本甚至是proxy,appengine还是可以用一用的。但是它有几个问题,一个是standand环境的编程比较复杂,开发者要了解一大堆东西,总算把google datastore在nodejs上跑了起来,但是运行起来才发现javascript版本的rss parser不给力,不如python的feedparser成熟。而python想找一个在appengine稍微能用的blog或者cms几乎是不可能的,大多数的github项目都是11、12年甚至零几年的。另外的问题是如果不付费很多功能都阉割了,即使google cloud赠送了300美元一年的试用,可是我不想浪费时间在这个没落的平台上了。

于是又想到了新型的平台zeitnetlify,它俩都支持连接到github直接拽代码部署,命令行直接搞定,试用了一下部署一个coreui的angular demo棒棒哒。如果是纯js项目可以考虑这两个平台。

做toy project的开发者还可以考虑heroku,这个老牌平台虽然有着google cloud同样的缺点,但是它上面一些开源项目却很实用,比如部署了一个下载youtube视频的应用在上面,还可以支持v2,前面挂上cloudflare那就可以特殊情况下应应急。

TypeScript+Babel+Webpack企业级应用开发的感想

所谓的企业级应用,从技术是说要支持极为广泛的用户浏览器,比如IE11,Edge,以及对ES6支持更好的Chrome/Safari等等。

旧的浏览器只能支持ES5是一个极为糟糕的痛点,不得不通过各种办法翻译代码到ES5。我的选型就是TS+Babel+Webpack,基本上也是前端的标配了。比较坑的是这里面配置文件的设定要花一些时间,消耗了我不少脑细胞和时间去搜索和尝试,感兴趣的可以留言交流具体的问题。有一个小窍门,是参考其它框架的工程实践,比如vue的https://cli.vuejs.org/zh/guide/browser-compatibility.html 这里面就列出了一些可以借鉴的要点,包括参考vue-cli的源代码,也可以借鉴(copy)那些babel插件可以用,如何配置config文件等等。

关于production环境发布部署,可以参考这篇文章https://philipwalton.com/articles/deploying-es2015-code-in-production-today/ 主要是利用浏览器对于“module”关键字识别与否,条件加载转译到ES5的或者直接就是ES6的代码。现在还没有用在当前项目,不过以后如果遇到性能问题的话,就要考虑这个点了。

最后要注意的是如果考虑浏览器兼容性,一些新的特性可能就无法使用,比如Proxy这样的根本就没有比较好的polyfill方案,用了也许就支持不了IE11。当然,我的建议是用户也要与时俱进,把这些老旧浏览器赶紧的丢进垃圾桶吧。