当前位置: 首页 > Web前端 > JavaScript

一个JSer的Dart学习日志(三):

时间:2023-03-27 12:23:33 JavaScript

类本文是“一个JSer的Dart学习日志”系列文章的第三篇。本系列文章主要通过探究JS与Dart的异同,对JS进行回顾和巩固。同时顺利过渡到Dart语言。由于笔者还是Dart的初学者,所以理解可能是肤浅的、片面的。如果你有慧眼,还望指正。如无特殊说明,本文JS包含ES5到ES2021的所有特性,Dart版本为2.0及以上版本。在ES6出现之前,广泛流行的JS面向对象编程使用原型链而不是类。开发人员需要对相关特性有足够的了解,并遵循一些默认规则,才能勉强模拟出一个大致可用的“类”。尽管ES6引入了class关键字来弥补,但是class作为新一代的JS基础设施仍然有待完善。相比之下,Dart对类的支持要完善和强大得多。1.整体结构相似在两种语言中,用于定义类的语法结构高度相似,主要包括类关键字、类名和用花括号{}包裹的成员。>/*JS和Dart*/>classClassName{>attrA;>attrB=1;>>methodA(a,b){>//dosomething>this.attrA=a;>this.attrB=b;>}>}2。构造函数同实例化类时调用构造函数,处理实例化参数,初始化实例属性等;使用super访问超类构造函数;没有超类或超类构造函数时,当没有参数时,可以省略构造函数,当实例化省略构造函数的子类时,会隐式调用超类的构造函数。区别1.constructorvsSameNameJSconstructor是构造函数;Dart中的构造函数是一个与类名一致的函数。>/*JS*/|/*Dart*/>类点{|类点{>构造函数(){|点(){>}|}>}|}Dart构造函数的特殊属性1.命名构造函数在Dart中,可以为一个类声明多个命名构造函数来表达更具体的意图,比如将一个Map对象映射到一个实例:>classPointCanBeEncode{>intx=0;>>//调用`eval``>Point.eval(Mapmap){>x=map['x'];>}>>encode():Map{>return{>'x':this.x>}>}>}当然,由于JS是一门非常灵活的语言,我们也可以将JS的静态方法包装成类似于Dart的具名构造函数。2.属性赋值的语法糖大多数情况下,构造函数的作用包括将给定的值作为实例的一个属性。Dart为这种情况提供了一个非常方便的语法糖:>/*Dart*/|/*也是Dart*/>classPoint{|类Point{>Point(this.x,this.y);|点(x,y){>}|这个.x=x;>|这个.y=y;>|}>|}↑可以看到左边的代码明显要简洁很多。3.初始化列表Dart可以在构造器执行之前初始化实例变量:classPoint{finaldoublex,y,distanceFromOrigin;Point(doublex,doubley):x=x,y=y,distanceFromOrigin=sqrt(x*x+y*y){print('还不错');}}初始化列表的执行其实比父类构造函数的执行时机还要早。4、重定向构造函数Dart可以有多个构造函数,可以将一个构造函数重定向到另一个构造函数:classPoint{doublex,y;点(this.x,this.y);Point.alongXAxis(doublex):this(x,0);}除了默认参数,没看到任何使用场景。我试了alongXAxis,好像不能有函数体。..5.常量构造函数如果类生成的对象是不变的,那么在生成这些对象时就可以把它们变成编译期常量。您可以通过在类构造函数前加上const关键字并确保所有实例变量都是最终变量来做到这一点。classImmutablePoint{//所有变量都是finalfinaldoublex,y;//构造函数是constconstImmutablePoint(this.x,this.y);}6.工厂构造函数JS是一门非常灵活的语言,构造一个函数没有返回值的时候,可以认为是返回了一个新的实例,但是同时,构造函数也可以将任意值作为一个新的实例返回;在Dart中,工厂关键字可用于声明具有返回值的构造函数。>/***************两种语言实现单例模式******************/>/*JS*/|/*飞镖*/>类A{|A类{>静态单;|静态变量单;>|A();>构造函数(){|工厂A.single(){>if(!A.single){|if(single==null){>A.single=this;|单=A();>}|}>返回A.single;|返回单个;>}|}>}|}>var实例=newA();|varinstance=A.single();这不能在工厂构造函数中访问。7、抽象类使用abstract关键字来声明抽象类。抽象类常用于声明接口方法,有时也有具体的方法实现。下面会提到抽象方法,抽象方法只能在抽象类中。3、new关键字可以在同一个地方实例化类;使用。访问成员;使用extends关键字扩展类并继承其属性。>/*JS和Dart*/>varinstance=newClassName('propA',42);>instance.attrA;//'propA'的区别1.Dart可以省略new关键字Dart实例化新类关键字可以省略,像函数一样初始化类:>varinstance=ClassName('propA',42);;ES5类也是函数,省略new关键字相当于执行这个函数,而ES6类不再是函数,省略new关键字会报错。2.Dart命名构造函数有了“命名构造函数”,你可以用更灵活的方式创建实例,比如快速将一个Map的属性映射到一个实例中:>varinstance=PointCanBeEncode.eval({'x':42});如果需要存储和传递实例,可以通过instance->Map/List->JSONstring的方案序列化一个实例,然后通过JSONstring->Map/List->new实例的方法“restores”它。3、Dart的编译时常量实例常量构造函数可以实例化编译时常量,减轻运行时负担:vara=constImmutablePoint(1,1);而JS根本没有编译时常量。侧面说明原生类型的构造函数是常量构造函数。4.覆盖超类的成员在JS中,子类的静态方法可以通过super.xxx访问超类的静态方法,子类成员会覆盖超类的同名成员,但是子类可以使用super在成员函数.xxx中调用超类的一个成员(必须是非静态成员,并且super必须先在构造函数中调用);在Dart中,使用@override注解来标记重写父类的方法(实际发现不写注解也可以编译,Lint会提示,应该和环境配置有关)。>/*JS*/|/*Dart*/>超级类{|超级类{>测试(){}|测试(){}>}|}>子类{|classSub{>/*只是覆盖它*/|@override>测试(){|测试(){>超级测试();|super.test()';>}|}>}|}Dart的mixin在Dart中声明一个类时,使用with关键字来混入一个没有构造函数的类,可以通过mixin关键字声明:mixinPosition{top:0;left:0;}classSidewithPosition{}4、成员属性和成员方法在成员函数内部是一样的用this访问当前实例,用点(.)访问成员;使用static关键字定义静态成员;定义getter和setter:>/*JS*/|/*Dart*/>类测试{|类测试{>#a=0;|私人a=0;>获取b(){|得到b(){>返回这个。#a;|返回this.a;>}|}>设置b(val){|设置b(val){>this.#a=a;|this.a=val;>}|}>}|}区别1.类闭作用域JS类没有作用域,必须用this来访问成员;Dart类具有封闭作用域,您可以在成员函数成员中直接访问其他成员而无需指定this。>/*JS*/|/*飞镖*/>consta=3;|consta=3;>类测试{|类测试{>a=1;|a=1;>测试(){|测试(){>console.log(a===this.a);|print('${a==this.a}');>/*'false'*/|//'真'>}|}>}|}2。私有变量/属性JS中类实例的私有成员声明时带有#前缀,访问时也必须使用此前缀;可以访问。>/*JS*/|/*Dart*/>类测试{|类测试{>#secret=1234;|_secret=1234;>测试(){|测试(){>console.log(this.#secret);|打印('${_secret}');>}|}>}|}JS私有成员是一个非常“年轻”的属性,在此之前,使用下划线来命名私有成员是被JS社区广泛接受的约定。最终ES并没有指定_作为private成员声明方案,也没有采用Java和TS中使用的private,而是采用了#语法。在Dart中,#语法用于符号文字。3.运算符重载Dart支持运算符重载。开发者可以自定义实例间的操作逻辑,比如向量操作:classVector{finaldoublex,y;constVector(this.x,this.y);向量运算符+(Vectorobj)=>Vector(obj.x+x,obj.y+y);}向量加法可以写成constc=a+b+c。借助运算符重载,可以定义一些非常直观的语法,例如使用&&、||求集和图的交集和并集。JS不支持运算符重载,所以在类似于上述的vector运算场景下,我们需要自定义一些方法来进行实例间的运算。例如,多个向量的加法可以写成:constd=a.add(b)。添加(c);constd=向量。添加(a,b,c)。4.抽象方法Dart中抽象类的Instance方法、Getter方法和Setter方法可以是抽象的,定义一个接口方法而不做具体的实现,让实现它的类去实现该方法,抽象方法只能存在于抽象类中。5.调用方法和可调用类实例在JS中,只能调用函数。当我们期望实现“有状态”的函数(例如增量ID生成器)时,我们通常使用函数的外部变量或函数返回内部函数(使用封闭包的性质);在Dart中,用call方法部署的类的实例可以作为一个函数来调用,调用实例的call方法:>/*JS*/|//Dart>constIdGen=()=>{|类IdGen{>让种子=0;|int_seed=0;>返回()=>{|调用(){>返回++种子;|返回++_seed;>}|}>};|}>constidGen=IdGen();|最终idGen=IdGen();>控制台。日志(idGen());|打印(idGen());>//1|//1.