翻译 - Classy JavaScript with dojo/_base/declare

原文:Classy JavaScript with dojo/_base/declare

dojo/_base/declare模块是在Dojo Toolkit中创建类的基石。declare允许多重继承,允许开发者创建灵活的代码避免重复造轮子。Dojo,Dijit和Dojox中的模块都使用了declare;在本教程中,你将学到为什么你也应该使用它。

开始

首先要确保你复习了在讲解模块的教程中提出的概念。

在Dojo中基于Dojo的类来创建类

declare函数定义在dojo/_base/declare模块中,declare有三个参数:className(类名),superClass(超类/父类)和properties(属性)。

类名

className参数标识要创建的类的名称,包括命名空间。命名的类呈现在全局作用域内。className也可以通过命名空间标识继承链。

1 // create a new class named "mynamespace.MyClass"
2 declare("mynamespace.MyClass", {
3
4     // Custom properties and methods here
5
6 });

一个名称为mynamespace.MyClass的类在应用的全局作用域内诞生了。

如果以后要给Dojo的parser使用,才应创建命名类。其他情况下,都应该省略className参数。

1 var MyClass = declare(null, {
2
3 });

MyClass只在给定的作用域内容有效。

超类

超类参数可以为null,也可以为一个存在的类或类的数组。如果一个新的类继承了多个类,以列表中的首个类为原型,其余的混入其中。

没有任何继承的类

1 var MyClass = declare(null, {
2
3 });

null标识该类没有继承自任何类。

继承另一个类

var MySubClass = declare(MyClass, {

});

MySubClass继承MyClass的属性与方法。父类中的属性或方法将会被第三个参数中定义的键所覆盖,很快就会讲到第三个参数。

继承自多个类

1 var MyMultiSubClass = declare([
2     MySubClass,
3     MyOtherClass,
4     MyMixinClass
5 ], {
6
7
8
9 });

一个类的数组多继承。属性与方法从左至右继承过来。第一个来作为基础原型,剩下的混合如其中。

如果不同的类中有同名的属性或方法,靠后面的类中的属性与方法将被使用。

属性与方法对象(declare的第三个参数)

declare的最后一个参数是一个对象包含了类的属性与方法。参数的中的属性与方法将会覆盖超类中的属性与方法。

自定义属性与方法

 1 // Class with custom properties and methods
 2 var MyClass = declare(MyParentClass, {
 3     // Any property
 4     myProperty1: 12,
 5     // Another
 6     myOtherProperty: ‘Hello‘,
 7     // A method
 8     myMethod: function() {
 9
10         // Perform any functionality here
11
12         return result;
13     }
14 });

例子:基础的类构建与继承

下面的代码通过继承dijit/form/Button创建一个挂件。

 1 define([
 2     ‘dojo/_base/declare‘,
 3     ‘dijit/form/Button‘
 4 ], function(declare, Button) {
 5     return declare(‘mynamespace.Button‘, Button, {
 6         label: ‘My Button‘,
 7         onClick: function(evt) {
 8             console.log(‘I was clicked‘);
 9             this.inherited(arguments);
10         }
11     });
12 });

从上面这段代码可以看出:

  • 该类类名为mynamespace.Button
  • 该类可以作为mynamespace.Button全局引用或通过模块返回
  • 该类继承自dijit/form/Button
  • 该类集合了一些属性与方法

让我们通过学习关于constructor方法的知识类深入理解使用Dojo构建类。

构造方法

类有个特殊的方法是constructor。这个方法在类实例化的时候被调用,在这个新对象的作用域内执行。这意味着constructor方法内容的this代表这个实例,不是原来的类。constructor方法也可以接受一些实例化时需要的参数。

 1 // Create a new class
 2 var Twitter = declare(null, {
 3     // the default username
 4     username: ‘defaultUser‘,
 5
 6     // the constructor
 7     constructor: function(args) {
 8         declare.safeMixin(this, args);
 9     }
10 });

接下来创建一个实例:

1 var myInstance = new Twitter();

username使用默认的defaultUser,除非为实例提供了指定的设置。为了利用safeMixin方法,传递一个username参数:

1 var myInstance = new Twitter({
2     username: ‘sitepen‘
3 });

这样myInstance的username就被设置为sitepen了。

declare的safeMixin方法构建与继承类时是很有用的。API文档是这样描述的:

这个方法用于混合属性像lang._mixin做的那样,但是会跳过构造方法与装饰方法就想dojo/_base/declare做的那样。它用于类与对象在dojo/_base/declare中。使用declare.safeMixin混合的方法可以使用this.inherited。这个函数用于实现使用declare产生的构造器的extend方法。

当使用很多选项构建类时declare.sageMixin是很有用的。

继承

如上述,继承是使用declare的第二个参数定义的。超类们从左至右混合类的属性与方法,后面的会覆盖已定义的属性与方法。如下:

 1 // Define class A
 2 var A = declare(null, {
 3     // A few properties ...
 4     propertyA: "yes",
 5     propertyB: 2
 6 });
 7
 8 // Define class B
 9 var B = declare(null, {
10     // A few properties ...
11     propertyA: "Maybe",
12     propertyB: 1,
13     propertyC: true
14 });
15
16 // Define class C
17 var C = declare([mynamespace.A, mynamespace.B], {
18     // A few properties...
19     propertyA: "No",
20     propertyB: 99,
21     propertyD: false
22 });

对于继承类属性的结果如下:

1 // Create an instance
2 var instance = new C();
3
4 // instance.propertyA = "No" // overridden by B, the by C
5 // instance.propertyB = 99    // overridden by B, the by C
6 // instance.propertyC = true  // kept from B
7 // instance.propertyD = false // create by C

清晰的理解原型继承非常重要。当一个属性从一个实例对象中读取时,实例首先检查自己是否定义该属性。如果没有,则顺原型链查找知道找到最近的属性值返回。当给一个属性赋值时,都是给实例本身的属性赋值,从不会影响原型。这样做的结果就是所有对象都共享一个原型将会返回定义在原型上的属性值,除非实例自己就有这个属性。这就让在类中定义原始数据类型的默认值,根据需要在实例上更新他们变得容易。然而如果你指派的属性值是一个对象或数组,每个实例将维护共享同一个值。考虑一下下面的代码:

 1 var MyClass = declare(null, {
 2     primitiveVal: 5,
 3     objectVal: [1, 2, 3]
 4 });
 5
 6 var obj1 = new MyClass();
 7 var obj2 = new MyClass();
 8
 9 // both return the same value from the prototype
10 obj1.primitiveVal === 5; // true
11 obj2.primitiveVal === 5; // true
12
13 // obj2 get its own property (prototype remains unchanged)
14 obj2.primitiveVal = 10;
15
16 // obj1 still gets its value from the prototype
17 obj1.primitiveVal === 5; // true
18 obj2.primitiveVal === 10; // true
19
20 // both point to the array on the prototype,
21 // neither instance has its own array at this point
22 obj1.objectVal === obj2.objectVal; // true
23
24 // obj2 manipulates the prototype‘s array
25 obj2.objectVal.push(4);
26
27 // obj2‘s manipulation is reflected in obj1 since the array
28 // is shared by all instances from the prototype
29 obj1.objectVal.length === 4; // true
30 obj1.objectVal[3] === 4; // true
31
32 // only assignment of the property itself (no maniplation of object
33 // properties) creates an instance-specific property
34 obj2.objectVal = [];
35 obj2.objectVal === obj2.objectVal; // false

为了避免在实例之间无心的共享数组与对象属性,对象属性应该声明为null值且在构造函数中初始化:

 1 declare(null, {
 2     // not strictly necessary, but good pratice
 3     // for readability to declare all properties
 4     memberList: null,
 5     roomMap: null,
 6
 7     constructor: function() {
 8         // initializing these properties with values in the constructor
 9         // ensures that they ready for use by other methods
10         // (and are not null or undefined)
11         this.memberList = [];
12         this.roomMap = {};
13     }
14 });

查看dojo/_base/declare文档了解更多信息。

this.inherited

我们完全覆盖方法当然是有用的,但是有时需要保留原型链上同名方法的功能,这个时候this.inherited(arguments)就派上用场了。this.inherited(arguments)将调用父类的同名方法。考虑下面的代码:

 1 // Define class A
 2 var A = declare(null, {
 3     myMethod: function() {
 4         console.log("hello!");
 5     }
 6 });
 7
 8 // Define class B
 9 var B = declare(A, {
10     myMethod: function() {
11         // Call A‘s myMethod
12         this.inherited(arguments);
13         console.log("World!");
14     }
15 });
16
17 // create an instance of B
18 var myB = new B();
19 myB.myMethod();
20
21 // would ouput:
22 //    hello!
23 //    World!

this.inherited方法可以在任何地方调用。有时候甚至想要子子函数或尾部调用它。这就是说,你不应在构造函数中调用它。

总结

declare函数是在Dojo Toolkit中构建类,重用类的关键。declare允许使用多继承构建复杂的类与多个属性和方法。所幸的是declare很好理解可以帮助开发者避免重复代码。

dojo/_base/declare资源

想学习关于delcare与构建类的更多细节?查看下面这些优秀的资源:

时间: 2024-11-05 12:14:57

翻译 - Classy JavaScript with dojo/_base/declare的相关文章

dojo 官方翻译 dojo/_base/lang 版本1.10

官方地址:http://dojotoolkit.org/reference-guide/1.10/dojo/_base/lang.html#dojo-base-lang 应用加载声明: require(["dojo/_base/lang"], function(lang){ // lang now contains the module features }); clone() 克隆任何对象或者元素节点,返回:一个新的对象. require(["dojo/_base/lang

dojo 官方翻译 dojo/_base/array

官方地址:http://dojotoolkit.org/reference-guide/1.10/dojo/_base/array.html#dojo-base-array array模块dojo进行了很好的封装,如果想要调用必须先加载该模块: require(["dojo/_base/array"], function(array){ // array contains the features }); indexOf() 返回值:字符串在数组中第一次出现的位置,如果没有找到默认返回

DOJO官方API翻译或解读-dojo/_base/lang --hitch()

hitch() hitch() 是一个函数,会在给定的上下中执行给定一个执行函数.hitch允许你去控制一个函数如何执行,往往在异步操作中起作用. 我们常常会写出这样的代码:(博主:这个代码意图在"click"事件触发时,执行此时定义的"processEvent".) 1 require(["dojo/on"], function(on){ 2 var processEvent = function(e){ 3 this.something =

dojo/_base/lang源码分析

dojo/_base/lang模块是一个工具模块,但几乎用dojo开发的app都会用到这个模块.模块中的方法能够在某些开发场景中避免繁冗的代码,接下来我们一起看看这些工具函数的使用和原理(仅仅是原理的实现,并非是dojo中的源码). lang.mixin(dest, sources...),这个函数的作用是将所有source中的属性拷贝到dest中,并返回dest.例子如下: var flattened = lang.mixin( { name: "Frylock", braces:

[书籍翻译] 《JavaScript并发编程》 第二章 JavaScript运行模型

本文是我翻译<JavaScript Concurrency>书籍的第二章 JavaScript运行模型,该书主要以Promises.Generator.Web workers等技术来讲解JavaScript并发编程方面的实践. 完整书籍翻译地址:https://github.com/yzsunlei/javascript_concurrency_translation .由于能力有限,肯定存在翻译不清楚甚至翻译错误的地方,欢迎朋友们提issue指出,感谢. 本书第一章我们探讨了JavaScri

[书籍翻译] 《JavaScript并发编程》第五章 使用Web Workers

本文是我翻译<JavaScript Concurrency>书籍的第五章 使用Web Workers,该书主要以Promises.Generator.Web workers等技术来讲解JavaScript并发编程方面的实践. 完整书籍翻译地址:https://github.com/yzsunlei/javascript_concurrency_translation .由于能力有限,肯定存在翻译不清楚甚至翻译错误的地方,欢迎朋友们提issue指出,感谢. Web workers在Web浏览器中

[书籍翻译] 《JavaScript并发编程》第六章 实用的并发

本文是我翻译<JavaScript Concurrency>书籍的第六章 实用的并发,该书主要以Promises.Generator.Web workers等技术来讲解JavaScript并发编程方面的实践. 完整书籍翻译地址:https://github.com/yzsunlei/javascript_concurrency_translation .由于能力有限,肯定存在翻译不清楚甚至翻译错误的地方,欢迎朋友们提issue指出,感谢. 在上一章中,我们大致学习了Web workers的基本

[书籍翻译] 《JavaScript并发编程》第七章 抽取并发逻辑

本文是我翻译<JavaScript Concurrency>书籍的第七章 抽取并发逻辑,该书主要以Promises.Generator.Web workers等技术来讲解JavaScript并发编程方面的实践. 完整书籍翻译地址:https://github.com/yzsunlei/javascript_concurrency_translation .由于能力有限,肯定存在翻译不清楚甚至翻译错误的地方,欢迎朋友们提issue指出,感谢. 到本书这里,我们已经在代码中明确地模拟了并发问题.使

[javascript][翻译]使用javascript添加css rule

来杭一周,收获很多,成长很多. 周六在搞一个插件的时候碰到需要动态添加伪元素的需求,搜了一下解决方案,有人用正则写出了读取伪元素的函数:我觉得倒是可以通过注入css rule的方式,来让预留有某些类的标签动态产生伪元素. 然后在google上搜到这样一篇文章,讲了一下之前没了解到的api函数:addrule等,翻译一下,分享一下好了. 原博文网址:http://davidwalsh.name/add-rules-stylesheets 译文如下: 因为我们在开发中javascript使用的越来越