判断是否为移动端浏览器
判断是否为移动端浏览器123456const flag = navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i);if(flag){ // 移动端} else { // PC端}
Mixin
MixinJavaScript 语言的设计是单一继承,即子类只能继承一个父类,不允许继承多个父类。这种设计保证了对象继承的层次结构是树状的,而不是复杂的网状结构。
但是,这大大降低了编程的灵活性。因为实际开发中,有时不可避免,子类需要继承多个父类。举例来说,“猫”可以继承“哺乳类动物”,也可以继承“宠物”。
各种单一继承的编程语言,有不同的多重继承解决方案。比如,Java 语言也是子类只能继承一个父类,但是还允许继承多个界面(interface),这样就间接实现了多重继承。Interface 与父类一样,也是一个类,只不过它只定义接口(method signature),不定义实现,因此又被称为“抽象类”。凡是继承于 Interface 的方法,都必须自己定义实现,否则就会报错。这样就避免了多重继承的最大问题:多个父类的同名方法的碰撞(naming collision)。
JavaScript 语言没有采用 Interface 的方案,而是通过代理(delegation)实现了从其他类引入方法。
123456789var Enumerable_first = function () ...
函数式编程
函数式编程JavaScript 语言从一诞生,就具有函数式编程的烙印。它将函数作为一种独立的数据类型,与其他数据类型处于完全平等的地位。在 JavaScript 语言中,你可以采用面向对象编程,也可以采用函数式编程。有人甚至说,JavaScript 是有史以来第一种被大规模采用的函数式编程语言。
ES6 的种种新增功能,使得函数式编程变得更方便、更强大。本章介绍 ES6 如何进行函数式编程。
柯里化柯里化(currying)指的是将一个多参数的函数拆分成一系列函数,每个拆分后的函数都只接受一个参数(unary)。
12345function add (a, b) { return a + b;}add(1, 1) // 2
上面代码中,函数add接受两个参数a和b。
柯里化就是将上面的函数拆分成两个函数,每个函数都只接受一个参数。
12345678910function add (a) { return function (b) { return a + b; }}// 或者采用箭头函数写法const add ...
装饰器
装饰器[说明] Decorator 提案经过了大幅修改,目前还没有定案,不知道语法会不会再变。下面的内容完全依据以前的提案,已经有点过时了。等待定案以后,需要完全重写。
装饰器(Decorator)是一种与类(class)相关的语法,用来注释或修改类和类方法。许多面向对象的语言都有这项功能,目前有一个提案将其引入了 ECMAScript。
装饰器是一种函数,写成@ + 函数名。它可以放在类和类方法的定义前面。
12345678@frozen class Foo { @configurable(false) @enumerable(true) method() {} @throttle(500) expensiveMethod() {}}
上面代码一共使用了四个装饰器,一个用在类本身,另外三个用在类方法。它们不仅增加了代码的可读性,清晰地表达了意图,而且提供一种方便的手段,增加或修改类的功能。
类的装饰装饰器可以用来装饰整个类。
12345678910@testableclass MyTestableClass & ...
异步遍历器
异步遍历器同步遍历器的问题《遍历器》一章说过,Iterator 接口是一种数据遍历的协议,只要调用遍历器对象的next方法,就会得到一个对象,表示当前遍历指针所在的那个位置的信息。next方法返回的对象的结构是{value, done},其中value表示当前的数据的值,done是一个布尔值,表示遍历是否结束。
12345678910111213141516function idMaker() { let index = 0; return { next: function() { return { value: index++, done: false }; } };}const it = idMaker();it.next().value // 0it.next().value // 1it.next().value // 2// ...
上面代码中,变量it是一个遍历器(iterator)。每次调用it.next()方法,就返回一个对象,表示当前遍历 ...
读懂 ECMAScript 规格
读懂 ECMAScript 规格概述规格文件是计算机语言的官方标准,详细描述语法规则和实现方法。
一般来说,没有必要阅读规格,除非你要写编译器。因为规格写得非常抽象和精炼,又缺乏实例,不容易理解,而且对于解决实际的应用问题,帮助不大。但是,如果你遇到疑难的语法问题,实在找不到答案,这时可以去查看规格文件,了解语言标准是怎么说的。规格是解决问题的“最后一招”。
这对 JavaScript 语言很有必要。因为它的使用场景复杂,语法规则不统一,例外很多,各种运行环境的行为不一致,导致奇怪的语法问题层出不穷,任何语法书都不可能囊括所有情况。查看规格,不失为一种解决语法问题的最可靠、最权威的终极方法。
本章介绍如何读懂 ECMAScript 6 的规格文件。
ECMAScript 6 的规格,可以在 ECMA 国际标准组织的官方网站(www.ecma-international.org/ecma-262/6.0/)免费下载和在线阅读。
这个规格文件相当庞大,一共有 26 章,A4 打印的话,足足有 545 页。它的特点就是规定得非常细致,每一个语法行为、每一个函数的实现都做了详尽的清晰的描述。 ...
编程风格
编程风格本章探讨如何将 ES6 的新语法,运用到编码实践之中,与传统的 JavaScript 语法结合在一起,写出合理的、易于阅读和维护的代码。
多家公司和组织已经公开了它们的风格规范,下面的内容主要参考了 Airbnb 公司的 JavaScript 风格规范。
块级作用域(1)let 取代 var
ES6 提出了两个新的声明变量的命令:let和const。其中,let完全可以取代var,因为两者语义相同,而且let没有副作用。
123456789'use strict';if (true) { let x = 'hello';}for (let i = 0; i < 10; i++) { console.log(i);}
上面代码如果用var替代let,实际上就声明了两个全局变量,这显然不是本意。变量应该只在其声明的代码块内有效,var命令做不到这一点。
var命令存在变量提升效用,let命令没有这个问题。
123456'use strict';if (true) ...
Module 的加载实现
Module 的加载实现上一章介绍了模块的语法,本章介绍如何在浏览器和 Node.js 之中加载 ES6 模块,以及实际开发中经常遇到的一些问题(比如循环加载)。
浏览器加载传统方法HTML 网页中,浏览器通过<script>标签加载 JavaScript 脚本。
12345678<!-- 页面内嵌的脚本 --><script type="application/javascript"> // module code</script><!-- 外部脚本 --><script type="application/javascript" src="path/to/myModule.js"></script>
上面代码中,由于浏览器脚本的默认语言是 JavaScript,因此type="application/javascript"可以省略。
默认情况下,浏览器是同步加载 JavaScript 脚本,即渲染引擎遇到<s ...
Module 的语法
Module 的语法概述历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能,比如 Ruby 的require、Python 的import,甚至就连 CSS 都有@import,但是 JavaScript 任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍。
在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。
ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。
12345678// CommonJS模块let { stat, exists, readFile } ...
Class 的继承
Class 的继承简介Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。
12345class Point {}class ColorPoint extends Point {}
上面代码定义了一个ColorPoint类,该类通过extends关键字,继承了Point类的所有属性和方法。但是由于没有部署任何代码,所以这两个类完全一样,等于复制了一个Point类。下面,我们在ColorPoint内部加上代码。
12345678910class ColorPoint extends Point { constructor(x, y, color) { super(x, y); // 调用父类的constructor(x, y) this.color = color; } toString() { return this.color + ' ' + super.toString(); // 调用父类的toStr ...