ES6项目实战-解析彩票项目-ES6基础语法(3) 太过爱你忘了你带给我的痛 2022-04-02 08:49 229阅读 0赞 **1.类与对象** 基本语法、类的继承、静态方法、静态属性、getter、setter class Parent{ //构造函数 constructor(name='mukewang'){ this.name = name; } } //生成实例 let v_parent = new Parent('v'); console.log(v_parent); // Parent{name:"v"} //继承 class Parent{ //构造函数 constructor(name='mukewang'){ this.name = name; } } class Child extends Parent{ //继承传递参数 constructor(name='child'){ super(name); this.type='child'; } } new Child('hello'); //Child {name:"hello",type:"child"} // getter、setter class Parent{ //构造函数 constructor(name='mukewang'){ this.name = name; } get longName(){ return 'mk'+this.name; } set longName(value){ this.name = value; } } let v= new Parent(); console.log('getter',v.longName); //v.longName就是get操作 v.longName = 'hello'; //赋值就是set操作,set和时是属性 console.log('setter',v.longName); //静态方法(通过类去调用,不是通过实例去调用) class Parent{ //构造函数 constructor(name='mukewang'){ this.name = name; } static tell(){ console.log(''tell''); } } Parent.tell(); //静态属性 class Parent{ //构造函数 constructor(name='mukewang'){ this.name = name; } static tell(){ console.log(''tell''); } } //静态属性(直接定义) Parent.type = 'test'; Parent.type; //调用 JavaScript 语言中,生成实例对象的传统方法是通过构造函数。下面是一个例子。 function Point(x, y) { this.x = x; this.y = y; } Point.prototype.toString = function () { return '(' + this.x + ', ' + this.y + ')'; }; var p = new Point(1, 2); 上面这种写法跟传统的面向对象语言(比如 C++ 和 Java)差异很大,很容易让新学习这门语言的程序员感到困惑。 ES6 提供了更接近传统语言的写法,引入了(类)这个概念,作为对象的模板。通过class关键字,可以定义类。 基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用 ES6 的class改写,就是下面这样。 class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; } } 上面代码定义了一个“类”,可以看到里面有一个constructor方法,这就是构造方法,而this关键字则代表实例对象。也就是说,ES5 的构造函数Point,对应 ES6 的Point类的构造方法。 Point类除了构造方法,还定义了一个toString方法。注意,定义“类”的方法的时候,前面不需要加上function这个关键字,直接把函数定义放进去了就可以了。另外,方法之间不需要逗号分隔,加了会报错。 ES6 的类,完全可以看作构造函数的另一种写法。 class Point { // ... } typeof Point // "function" Point === Point.prototype.constructor // true 上面代码表明,类的数据类型就是函数,类本身就指向构造函数。 使用的时候,也是直接对类使用new命令,跟构造函数的用法完全一致。 class Bar { doStuff() { console.log('stuff'); } } var b = new Bar(); b.doStuff() // "stuff" 构造函数的prototype属性,在 ES6 的“类”上面继续存在。事实上,类的所有方法都定义在类的prototype属性上面。 class Point { constructor() { // ... } toString() { // ... } toValue() { // ... } } // 等同于 Point.prototype = { constructor() {}, toString() {}, toValue() {}, }; 在类的实例上面调用方法,其实就是调用原型上的方法。 class B {} let b = new B(); b.constructor === B.prototype.constructor // true 上面代码中,b是B类的实例,它的constructor方法就是B类原型的constructor方法。 由于类的方法都定义在prototype对象上面,所以类的新方法可以添加在prototype对象上面。Object.assign方法可以很方便地一次向类添加多个方法。 class Point { constructor(){ // ... } } Object.assign(Point.prototype, { toString(){}, toValue(){} }); prototype对象的constructor属性,直接指向“类”的本身,这与 ES5 的行为是一致的。 Point.prototype.constructor === Point // true **2.Promise** Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。 所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。 ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ0NjU5MzQ_size_16_color_FFFFFF_t_70] ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ0NjU5MzQ_size_16_color_FFFFFF_t_70 1] **3.Iterator** JavaScript 原有的表示“集合”的数据结构,主要是数组(Array)和对象(Object),ES6 又添加了Map和Set。这样就有了四种数据集合,用户还可以组合使用它们,定义自己的数据结构,比如数组的成员是Map,Map的成员是对象。这样就需要一种统一的接口机制,来处理所有不同的数据结构。 遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。 Iterator 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创造了一种新的遍历命令for…of循环,Iterator 接口主要供for…of消费。 Iterator 的遍历过程是这样的。 (1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。 (2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。 (3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。 (4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。 每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。 let arr = ['hello','world']; let map = arr[Symbol.iterator](); map.next(); map.next(); map.next(): ![在这里插入图片描述][20181226122651412.png] let obj = { strat:[1,3,2], end:[7,9,8], [Symbol.iterator](){ let self = this; let index =0; let arr = self.start.concat(self.end); let len = arr.length; return { next(){ if(index<len){ return { value:arr[index++], done:false } }else{ return{ value:arr[index++], done:true } } } } } } for(let key of obj){ console.log(key); } **4.Generator** Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。 Generator就是一个Iterator 遍历器生成函数。 Generator 函数有多种理解角度。语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。 执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。 形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。 function* helloWorldGenerator() { yield 'hello'; yield 'world'; return 'ending'; } var hw = helloWorldGenerator(); 上面代码定义了一个 Generator 函数helloWorldGenerator,它内部有两个yield表达式(hello和world),即该函数有三个状态:hello,world 和 return 语句(结束执行)。 然后,Generator 函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是上一章介绍的遍历器对象(Iterator Object)。 下一步,必须调用遍历器对象的next方法,使得指针移向下一个状态。也就是说,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield表达式(或return语句)为止。换言之,Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。 hw.next() // { value: 'hello', done: false } hw.next() // { value: 'world', done: false } hw.next() // { value: 'ending', done: true } hw.next() // { value: undefined, done: true } 上面代码一共调用了四次next方法。 第一次调用,Generator 函数开始执行,直到遇到第一个yield表达式为止。next方法返回一个对象,它的value属性就是当前yield表达式的值hello,done属性的值false,表示遍历还没有结束。 第二次调用,Generator 函数从上次yield表达式停下的地方,一直执行到下一个yield表达式。next方法返回的对象的value属性就是当前yield表达式的值world,done属性的值false,表示遍历还没有结束。 第三次调用,Generator 函数从上次yield表达式停下的地方,一直执行到return语句(如果没有return语句,就执行到函数结束)。next方法返回的对象的value属性,就是紧跟在return语句后面的表达式的值(如果没有return语句,则value属性的值为undefined),done属性的值true,表示遍历已经结束。 第四次调用,此时 Generator 函数已经运行完毕,next方法返回对象的value属性为undefined,done属性为true。以后再调用next方法,返回的都是这个值。 总结一下,调用 Generator 函数,返回一个遍历器对象,代表 Generator 函数的内部指针。以后,每次调用遍历器对象的next方法,就会返回一个有着value和done两个属性的对象。value属性表示当前的内部状态的值,是yield表达式后面那个表达式的值;done属性是一个布尔值,表示是否遍历结束。 **5.Decorators** 修饰器是一个函数。 修改行为。 修改类的行为。 许多面向对象的语言都有修饰器(Decorator)函数,用来修改类的行为。目前,有一个提案将这项功能,引入了 ECMAScript。 @testable class MyTestableClass { // ... } function testable(target) { target.isTestable = true; } MyTestableClass.isTestable // true 上面代码中,@testable就是一个修饰器。它修改了MyTestableClass这个类的行为,为它加上了静态属性isTestable。testable函数的参数target是MyTestableClass类本身。 基本上,修饰器的行为就是下面这样。 @decorator class A {} // 等同于 class A {} A = decorator(A) || A; 也就是说,修饰器是一个对类进行处理的函数。修饰器函数的第一个参数,就是所要修饰的目标类。 function testable(target) { // ... } 上面代码中,testable函数的参数target,就是会被修饰的类。 如果觉得一个参数不够用,可以在修饰器外面再封装一层函数。 function testable(isTestable) { return function(target) { target.isTestable = isTestable; } } @testable(true) class MyTestableClass {} MyTestableClass.isTestable // true @testable(false) class MyClass {} MyClass.isTestable // false 上面代码中,修饰器testable可以接受参数,这就等于可以修改修饰器的行为。 注意,修饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。这意味着,修饰器能在编译阶段运行代码。也就是说,修饰器本质就是编译时执行的函数。 **6.Module模块化** //导出变量、函数和类 export let A = 123; export function test(){ } export class Hello{ test(){ } } //导出变量、函数和类的另一种方法 let A = 123; let test = function(){ } class Hello{ } export default { A, test, Hello } //引入导出的变量、函数和类 import {A,test,Hello} from 'lesson17'; // from 路径 console.log(A,test,Hello); import * as lesson from 'lesson17'; // from 路径 console.log(lesson.A) 历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能,比如 Ruby 的require、Python 的import,甚至就连 CSS 都有@import,但是 JavaScript 任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍。 在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。 ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。 // CommonJS模块 let { stat, exists, readFile } = require('fs'); // 等同于 let _fs = require('fs'); let stat = _fs.stat; let exists = _fs.exists; let readfile = _fs.readfile; 上面代码的实质是整体加载fs模块(即加载fs的所有方法),生成一个对象(\_fs),然后再从这个对象上面读取 3 个方法。这种加载称为“运行时加载”,因为只有运行时才能得到这个对象,导致完全没办法在编译时做“静态优化”。 ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。 // ES6模块 import { stat, exists, readFile } from 'fs'; 上面代码的实质是从fs模块加载 3 个方法,其他方法不加载。这种加载称为“编译时加载”或者静态加载,即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。当然,这也导致了没法引用 ES6 模块本身,因为它不是对象。 由于 ES6 模块是编译时加载,使得静态分析成为可能。有了它,就能进一步拓宽 JavaScript 的语法,比如引入宏(macro)和类型检验(type system)这些只能靠静态分析实现的功能。 除了静态加载带来的各种好处,ES6 模块还有以下好处。 * 不再需要UMD模块格式了,将来服务器和浏览器都会支持 ES6 模块格式。目前,通过各种工具库,其实已经做到了这一点。 * 将来浏览器的新 API 就能用模块格式提供,不再必须做成全局变量或者navigator对象的属性。 * 不再需要对象作为命名空间(比如Math对象),未来这些功能可以通过模块提供。 async 函数 ES2017 标准引入了 async 函数,使得异步操作变得更加方便。 async 函数是什么?一句话,它就是 Generator 函数的语法糖。 前文有一个 Generator 函数,依次读取两个文件。 const fs = require('fs'); const readFile = function (fileName) { return new Promise(function (resolve, reject) { fs.readFile(fileName, function(error, data) { if (error) return reject(error); resolve(data); }); }); }; const gen = function* () { const f1 = yield readFile('/etc/fstab'); const f2 = yield readFile('/etc/shells'); console.log(f1.toString()); console.log(f2.toString()); }; 上面代码的函数gen可以写成async函数,就是下面这样。 const asyncReadFile = async function () { const f1 = await readFile('/etc/fstab'); const f2 = await readFile('/etc/shells'); console.log(f1.toString()); console.log(f2.toString()); }; 一比较就会发现,async函数就是将 Generator 函数的星号(\*)替换成async,将yield替换成await,仅此而已。 async函数对 Generator 函数的改进,体现在以下四点。 (1)内置执行器。 Generator 函数的执行必须靠执行器,所以才有了co模块,而async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样,只要一行。 asyncReadFile(); 上面的代码调用了asyncReadFile函数,然后它就会自动执行,输出最后结果。这完全不像 Generator 函数,需要调用next方法,或者用co模块,才能真正执行,得到最后结果。 (2)更好的语义。 async和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。 (3)更广的适用性。 co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象,而async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。 (4)返回值是 Promise。 async函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用then方法指定下一步的操作。 进一步说,async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖。 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ0NjU5MzQ_size_16_color_FFFFFF_t_70]: /images/20220402/5d66c5d026324d6eb95885f57746c323.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ0NjU5MzQ_size_16_color_FFFFFF_t_70 1]: /images/20220402/a81ed7f707624dcea58fd83815bf71ec.png [20181226122651412.png]: /images/20220402/753e26a9c3ff4108a640f34147ae456f.png
还没有评论,来说两句吧...