【学习】ES6版本下JavaScript的编程风格

写在前面

ES62015年已经大刀阔斧地进入前端领域了,很多前沿的框架比如ReactJS、AngularJS、NodeJS和IOJS新版本都支持ES6语法。还是那句话,前端就是要走在最前端,技术革新速度相当快速,如果你还因循守旧,那就OUT出局吧。当然,ES6新特性很多,建议去极客学院WiFi上看看阮一峰老师的文档,今天不讲具体的语法,而是学习ES6 的新语法,运用到编码实践之中,与传统的 JavaScript 语法结合在一起,以及如何形成良好的编码风格。

块级作用域

let取代var

ES6提出了两个新的变量的命令:let和const。其中,let完全可以取代var,因为两者语义相同,而且let没有副作用。

"use strict";
if(true){
    let x='hello';
}
for(let i=0;i<10;i++){
    console.log(i);
}

上面代码如果用var替代let,实际上就声明了一个全局变量,这显然不是本意。变量应该是在其声明的代码块内有效,var命令则做不到这一点。

var命令存在变量提升效用,let命令没有这个问题。

"use strict";
if(true){
    console.log(x);  //ReferenceError
    let x='Hello';
}

上面代码如果使用var替代let,console.log那一行就不会报错,而是会输出undefined,因为变量声明提升到代码块的头部。就违反了变量先声明后使用的原则。所以建议不要再使用var命名声明变量,而是使用let命令取代。

全局常量和线程安全

在let和const之间,建议优先使用const,尤其是在全局环境,不应该设置变量,只应设置常亮。这符合函数式编程思想,有利于将来的分布式计算。

// bad
var a=1,b=2,c=3;

// good
const a=1;
const b=2;
const c=3;

// best
const [a,b,c]=[1,2,3];

const声明常量有两个好处,一是阅读代码的人立即会意识到不应该去修改这个值,而是防止了无意间修改变量值所导致的错误。

注意:所有的函数都应该设置为常量。let表示的变量,只应该出现在单线程运行的代码中,不能是多线程共享的,这样有利于保证多线程安全。

严格模式

v8引擎只在严格模式之下支持let和const。结合前两点,这实际上意味着将来所有的编程都是针对严格模式的。

字符串

静态字符串一律使用单引号或反引号,不使用双引号。动态字符串使用反引号。

// bad
const a="foobar";
const b='foo'+a+'bar';

// acceptable
const c='foobar';

// good
const a='foobar';
const b='foo${a}bar';
const c='foobar';

对象

单行定义的对象,最后一个成员不以逗号结尾。多行定义的对象,最后一个成员以逗号结尾。

// bad
const a={k1:v1,k2:v2,};
const b={
    k1:v1,
    k2:v2
};

// good
const a={k1:v1,k2:v2};
const b={
    k1:v1,
    k2:v2,
};

对象尽量静态化,一旦定义好,就不得随意添加新的属性。如果添加属性不可避免,要使用Object.assign方法。

// bad
const a={};
a.x=3;

// if reshape unavoidable
const a={};
Object.assign(a,{x:3});

// good
const a={x:null};
a.x=3;

函数

使用匿名函数的场合,一律改为使用箭头函数。

// bad
arr.reduce(function(x,y){return x+y;},0);

// good
arr.reduce((x,y)=>x+y,0);

箭头函数取代Function.prototype.bind,不应该再用 self / _this / that 绑定 this。

// bad
const self=this;
const boundMethod=function(...params){
    return method.apply(self.params);
}

// acceptable
const boundMethod=method.bind(this);

// best
const boundMethod=(...params)=>method.apply(this,params);

所有配置项都应该集中在一个对象,放在最后一个参数,布尔值不可以直接作为参数。

// bad
function divide(a,b,option=false){
    //...
}

// good
function divide(a,b,{option=false}={}){
    //...
}

Map结构

注意却分Object和Map,只有模拟实体对象时,才使用Object。如果只是需要key:value的数据结构,使用Map。因为Map有内建的遍历机制。

let map=new Map(arr);

for(let key of map.keys()){
    console.log(key);
}

for(let value of map.values()){
    console.log(value);
}

for(let item of map.entries()){
    console.log(item[0],item[1]);
}

模块

ES6的Class只是面向对象的语法糖,升级了ES5的对象定义的写法,并没有解决模块化问题。Module功能就是为了解决这个问题而提出的。历史上JavaScript一直没有模块体系,无法将一个大程序拆成互相依赖的小组件,再用简单的方法拼装起来。其他语言都有这项功能,比如Ruby的require、Python的import,甚至就连css都有@import,但是JavaScript没有,这对开发大型的、复杂的项目形成了巨大障碍。

当然,在ES6之前社区制定了一些模块加载方案,最主要的有CommonJS和AMD两种(目前还有seaJS的CMD)。前者用于服务器,后者用于浏览器。ES6在语言规定的层面上,实现了模块功能,而且实现地相当简单,完全可以取代现有的CommonJS和AMD规范,成为浏览器和服务器通用的模块解决方案。哈哈,这里恰巧验证了之前的猜想,原文请 戳这里。

当然,ES6模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS和AMD模块,都只能在运行时确定这些东西。比如,CommonJS模块就是对象,输入时必须查找对象属性。

var {stat,exists,readFile}=require('fs');

ES6模块不是对象,而是通过expert命令显式指定输出的代码,输入时也采用静态命令的形式。

import {stat,exists,readFile} from 'fs';

所以,ES6可以在编译时就完成模块编译,效率要比CommonJS模块高。

使用import取代require

// bad 
const moduleA=require('moduleA');
const func1=moduleA.func1;
const func2=moduleA.func2;

// good
import{func1,func2} from 'moduleA';

使用export取代module.exports

// commonJS的写法
var React=require('react');
var Breadcrumbs=React.createClass({
    render:function(){
        return 

你可能感兴趣的:(web前端,js)