作为前端开发工程师,盲目追逐框架似乎有点舍本逐末,要知道基本功才是硬核。JavaScript 的语法这几年一直在更新,不管我们是框架的核心开发者还是业务重塑者,学习下最新的 JavaScript 语法和能力是非常有好处的。下面我们通过几个小示例来看下新语法的强大之处:
“初始化一个数组,要求数组的长度是 5,每个元素的默认值是 0”
这道题看似非常简单,我们可能会这样来写代码:
const arr = []
for(let i = 0; i < 5; i++){arr.push(0)
}
可是如果你学习过 ES6 的语法,就会知道 Array 新增的原型对象方法上有个 fill 的 API,它可以轻松实现这个题目,代码如下:
const arr = Array(5).fill(0)
这就是新的语法赋予 JavaScript 新的能力,如果我们不持续学习新的语法,写出来的代码很难是最简、最优雅、性能最好。当然,阅读其他同学或者开源代码的时候也不一定能看懂。那么 ES6 到底新增或者增强了哪些能力呢?我们来看下图谱:
从这个图谱不能看出 ES6 增加了很多新的语法,比如 Class、Generator、Proxy、Iterator 等。它们可以解决类、异步、代理、自定义遍历等功能。不如我们再来看个小示例:
实现类与继承。
在 ES6 之前实现类与继承都是借助函数来实现的,在继承方面也是利用原型链。代码如下:
function Component () {this.id = Math.random().toString(36).slice(-5)
Object.defineProperty(this, 'id', {writable: false})
}
const com = new Component()
com.id = 3
console.log(com.id) // jklls
这段代码的含义是定义一个组件类,类定义了一个属性 id,这个 id 是随机、只读的。ES6 有了专门的语法来定义类。
class Component {constructor () {this.id = Math.random().toString(36).slice(-5)
Object.defineProperty(this, 'id', {writable: false})
}
}
const com = new Component()
com.id = 3
console.log(com.id)
在语义上看 ES6 的写法更容易读懂。不信,我们在看下继承的写法:
function Component () {this.id = Math.random().toString(36).slice(-5)
Object.defineProperty(this, 'id', {writable: false})
}
function SubComponent () {Component.call(this)
}
SubComponent.prototype = Component.prototype
const com = new SubComponent()
com.id = 3
console.log(com.id)
class Component {constructor () {this.id = Math.random().toString(36).slice(-5)
Object.defineProperty(this, 'id', {writable: false})
}
}
class SubComponent extends Component {
}
const com = new SubComponent()
com.id = 3
console.log(com.id)
上下代码对比可以看出来 ES6 的方式要舒服很多,也更容易阅读。借助这个题我们再来思考 ES6 这个写法还能继续优化吗?比如 Object.defineProperty 方法在构造函数里显得那么格格不入。有没有更优雅的写法呢?不妨试试 ES6 新的语法 Proxy?
class Component {constructor () {
this.proxy = new Proxy({id: Math.random().toString(36).slice(-5)
}, {})
}
get id () {return this.proxy.id}
}
const com = new Component()
com.id = 3
console.log(com.id)
利用 Proxy 和 Class getter 方式就能保证 id 是只读的,在 proxy 实例化的时候也能保证 id“随机”、“唯一”。有同学会说这个代码有漏洞,proxy 还可以修改会导致 id 也可以被修改。说的没错,但是低估了 proxy 的能力,你再看:
class Component {constructor () {
this.proxy = new Proxy({id: Math.random().toString(36).slice(-5)
}, {set (target, key, value) {return false}
})
}
get id () {return this.proxy.id}
}
const com = new Component()
com.proxy.id = 4
com.id = 3
console.log(com.id)
要知道 proxy 下面可以放很多跟 id 一样的内容,这样我们就不会一个一个用 Object.defineProperty 去显示的定义“只读”。用 class getter + proxy 的方式写起来“不露痕迹”,大家是否享受这种写法呢?当然,proxy 还有很多用武之地,比如把保护数据、数据校验等等。
如果大家没过瘾,我们再看一个更强大的功能:自定义遍历。
“我们数据库里存放着很多图书的作者,这些作者按照图书的类别进行分类,现在想遍历所有作者该怎么办?”
let authors = {
allAuthors: {
fiction: [
'Agatha Christie',
'J. K. Rowling',
'Dr. Seuss'
],
scienceFiction: [
'Neal Stephenson',
'Arthur Clarke',
'Isaac Asimov',
'Robert Heinlein'
],
fantasy: [
'J. R. R. Tolkien',
'J. K. Rowling',
'Terry Pratchett'
]
}
}
我们希望可以对 authors 进行遍历并得到所有作者的名单。
for (let author of authors) {console.log(author)
}
本希望可以这做可是浏览器报错了,告诉我们 authors 是不可遍历的。那我们只能通过遍历所有 key 的方式来实现:
for (let key in authors) {let r = []
for (let k in authors[key]) {r = r.concat(authors[key][k])
}
console.log(r)
// ["Agatha Christie", "J. K. Rowling", "Dr. Seuss", "Neal Stephenson", "Arthur Clarke", "Isaac Asimov", "Robert Heinlein", "J. R. R. Tolkien", "J. K. Rowling", "Terry Pratchett"]
}
虽然用 ES5 的方式实现了,可是我们仍希望用 for…of 的方式来实现,简单便捷。ES6 增加了 Iterator 让任意数据结构可以实现自定义遍历器。直接上代码:
authors[Symbol.iterator] = function () {
let allAuthors = this.allAuthors
let keys = Reflect.ownKeys(allAuthors)
let values = []
return {next () {if (!values.length) {if (keys.length) {values = allAuthors[keys[0]]
keys.shift()}
}
return {
done: !values.length,
value: values.shift()}
}
}
}
我们只需要对 authors 这个数据结构增加 Iterator 遍历器接口即可用 for…of 的方式来遍历了,浏览器不再报错了。有没有很惊艳?其实 ES6 之后 ES7、ES8、ES9、ES10 相继诞生,它们让原生 JavaScript 的能力再次提升。
不学习原生 JavaScript 新技能注定会让我们错过更多魅力之城,一起结对学 ES6~ES10。