初识ext了解js的潮流
开篇,原本想大量引用网上对Ext-base.js文件的分析。但是经过大量的阅读国外的书籍发现,原来那人根本不懂什么是js面向对象程序设计。
参考书:
Apress出版的Pro.JavaScript.Design.Patterns
Professional.JavaScript.for.Web.Developers.2nd.Edition.2009
本文想通过一步一步的对javascript语法的进化来最后达到最ext-base.js的解析过程。
1.Javascript中的类
利用对javascript的常识,我认为js的设计应该如下:
/* Anim class. */var Anim = function() {...};Anim.prototype.start = function() {...};Anim.prototype.stop = function() {...};/* Usage. */var myAnim = new Anim();myAnim.start();...myAnim.stop();/* Anim class, with a slightly different syntax for declaring methods. */var Anim = function() {...};Anim.prototype = {start: function() {...},stop: function() {...}};<html> <head> <title>String Example</title> <script type="text/javascript"> //在javascript中,函数都最好的对象//函数的大部分概念和C++没有什么区别。值得注意的是匿名函数//示例如下: /* An anonymous function, executed immediately. */ (function(){ var foo = 10; var bar = 2; alert(foo * bar); })();//这个函数的定义和执行并没有分配给任何变量。最后的()执行了这个函数。//他们是空的,但是并不是这种情况 /* An anonymous function with arguments. */ (function(foo, bar){ alert(foo * bar); })(10, 2);//下例同第一个例子等价。不过将值给了一个内在的变量 /* An anonymous function that returns a value. */ var baz = (function(foo, bar){ return foo * bar; })(10, 2); alert(baz);//匿名函数最有趣的应用时建立闭包。//闭包是一个受保护的变量空间,这就是说闭包函数可以在他们所定义的任意地方执行。不受函数体影响 /* An anonymous function used as a closure. */ var baz; (function(){ var foo = 10; var bar = 2; baz = function(){ return foo * bar; }; })();baz(); // baz can access foo and bar, even though it is executed outside of the class </script> </head> <body> <!-- Nothing in the body -->//在javascript中,函数都最好的对象//函数的大部分概念和C++没有什么区别。值得注意的是匿名函数//示例如下: /* An anonymous function, executed immediately. */ (function(){ var foo = 10; var bar = 2; alert(foo * bar); })();//这个函数的定义和执行并没有分配给任何变量。最后的()执行了这个函数。//他们是空的,但是并不是这种情况 /* An anonymous function with arguments. */ (function(foo, bar){ alert(foo * bar); })(10, 2);//下例同第一个例子等价。不过将值给了一个内在的变量 /* An anonymous function that returns a value. */ var baz = (function(foo, bar){ return foo * bar; })(10, 2); alert(baz);//匿名函数最有趣的应用时建立闭包。//闭包是一个受保护的变量空间,这就是说闭包函数可以在他们所定义的任意地方执行。不受函数体影响 /* An anonymous function used as a closure. */ var baz; (function(){ var foo = 10; var bar = 2; baz = function(){ return foo * bar; }; })();baz(); // baz can access foo and bar, even though it is executed outside of the class </body></html>/* Add a method to the Function object that can be used to declare methods. */Function.prototype.method = function(name, fn) {this.prototype[name] = fn;};/* Anim class, with methods created using a convenience method. */var Anim = function() {...};Anim.method('start', function() {...});Anim.method('stop', function() {...});并修改method,让指针只记住类中各种方法的地址。即只给出链接。/* This version allows the calls to be chained. */Function.prototype.method = function(name, fn) {this.prototype[name] = fn;return this;};/* Anim class, with methods created using a convenience method and chaining. */var Anim = function() {...};Anim.method('start', function() {...}).method('stop', function() {...});<html> <head> <title>String Example</title> <script type="text/javascript"> //在javascript中,所有的东西都是对象 //所有的对象都是可变的。 //这就意味着你可以在类定义好,并实例化以后改变它。 /* Class Person. */ function Person(name, age){ this.name = name; this.age = age; } Person.prototype = { getName: function(){ return this.name; }, getAge: function(){ return this.age; } } /* Instantiate the class. */ var alice = new Person('Alice', 93); var bill = new Person('Bill', 30);document.write("/* Instantiate the class. */<br/>");document.write("name: "+alice.name+";her age: "+alice.age+"<br/>");document.write("name: "+bill.name+";his age: "+bill.age+"<br/>"); /* Modify the class. */ Person.prototype.getGreeting = function(){ return 'Hi ' + this.getName() + '!'; };document.write("/* Modify the class. */<br/>");document.write("alice.getGreeting:"+alice.getGreeting()+"<br/>"); /* Modify a specific instance. */ alice.displayGreeting = function(){ alert(this.getGreeting()); }document.write("/* Modify a specific instance. */<br/>alert(getGreeting)");alice.displayGreeting(); </script> </head> <body> <!-- Nothing in the body --> </body> </html><html> <head> <title>String Example</title> <script type="text/javascript"> function foo(){ var a = 10; function bar(){ a *= 2; } bar(); return a; } document.writeln(foo()); </script> </head> <body> <!-- Nothing in the body --> </body> </html> <html> <head> <title>String Example</title> <script type="text/javascript"> var Book = function(newIsbn, newTitle, newAuthor){ // implements Publication // Private attributes. var isbn, title, author; // Privileged methods. this.getIsbn = function(){ return isbn; }; this.setIsbn = function(newIsbn){ isbn = newIsbn; }; this.getTitle = function(){ return title; }; this.setTitle = function(newTitle){ title = newTitle || 'No title specified'; }; this.getAuthor = function(){ return author; }; this.setAuthor = function(newAuthor){ author = newAuthor || 'No author specified'; }; // Constructor code. this.setIsbn(newIsbn); this.setTitle(newTitle); this.setAuthor(newAuthor); }; // Public, non-privileged methods. Book.prototype = { display: function(){ return "ISBN:"+this.getIsbn() + "<br/>"+"Title:" + this.getTitle() + "<br/>"+"Author:" + this.getAuthor(); } };var book = new Book('978-0261103283','pro javascript design pattern','Apress');document.write(book.display());//使用了更多的内存//很难被继承 </script> </head> <body> <!-- Nothing in the body --> </body> </html> <html> <head> <title>String Example</title> <script type="text/javascript"> var Book = (function(){ // Private static attributes. var numOfBooks = 0; // Return the constructor. return function(newIsbn, newTitle, newAuthor){ // implements Publication // Private attributes. var isbn, title, author; // Private static method. function checkIsbn(isbn){ if (isbn) { return true; } else { return false; } } // Privileged methods. this.getIsbn = function(){ return isbn; }; this.setIsbn = function(newIsbn){ if (!checkIsbn(newIsbn)) throw new Error('Book: Invalid ISBN.'); isbn = newIsbn; }; this.getTitle = function(){ return title; }; this.setTitle = function(newTitle){ title = newTitle || 'No title specified'; }; this.getAuthor = function(){ return author; }; this.setAuthor = function(newAuthor){ author = newAuthor || 'No author specified'; }; // Constructor code. numOfBooks++; // Keep track of how many Books have been instantiated // with the private static attribute. if (numOfBooks > 50) throw new Error('Book: Only 50 instances of Book can be ' + 'created.'); this.setIsbn(newIsbn); this.setTitle(newTitle); this.setAuthor(newAuthor); } })(); // Public static method. Book.convertToTitle = function(inputString){ return inputString + "[us]"; }; // Public, non-privileged methods. Book.prototype = { display: function(){ return "ISBN:" + this.getIsbn() + "<br/>" + "Title:" + this.getTitle() + "<br/>" + "Author:" + this.getAuthor(); } }; var book = new Book('978-0261103283', Book.convertToTitle('pro javascript design pattern'), 'Apress'); document.write(book.display()); </script> </head> <body> <!-- Nothing in the body --> </body> </html> <html> <head> <title>String Example</title> <script type="text/javascript"> /* Class Person. */ function Person(name){ this.name = name; } Person.prototype.getName = function(){ return this.name; } /* Class Author. *///建立一个类,继承于其他类 function Author(name, books){//首先,建立构造器函数,call父类的构造函数,传递参数 Person.call(this, name); // Call the superclass's constructor in the scope of this. this.books = books; // Add an attribute to Author. } //当你调用new操作符,很多事已经为你准备好。首先一个空的object对象已经建立。//构造器函数调用空对象的scope chain Author.prototype = new Person(); // Set up the prototype chain. Author.prototype.constructor = Author; // Set the constructor attribute to Author. Author.prototype.getBooks = function(){ // Add a method to Author. return this.books; }; //实例测试 var author = []; author[0] = new Author('Dustin Diaz', ['JavaScript Design Patterns']); author[1] = new Author('Ross Harmes', ['JavaScript Design Patterns']); document.write(author[0].getName()+' ' +author[0].getBooks()+"<br/>");document.write(author[1].getName()+' ' +author[1].getBooks()); </script> </head> <body> <!-- Nothing in the body --> </body> </html> <html> <head> <title>String Example</title> <script type="text/javascript"> /* Extend function, improved. */ function extend(subClass, superClass){ var F = function(){ }; F.prototype = superClass.prototype; subClass.prototype = new F(); subClass.prototype.constructor = subClass; subClass.superclass = superClass.prototype; if (superClass.prototype.constructor == Object.prototype.constructor) { superClass.prototype.constructor = superClass; } } /* Class Person. */ function Person(name){ this.name = name; } Person.prototype.getName = function(){ return this.name; } /* Class Author. */ function Author(name, books){ Author.superclass.constructor.call(this, name); this.books = books; } extend(Author, Person); Author.prototype.getBooks = function(){ return this.books; }; //改变Author类 Author.prototype.getName = function(){ var name = Author.superclass.getName.call(this); return name + ', Author of ' + this.getBooks().join(', '); }; //实例测试 var author = []; author[0] = new Author('Dustin Diaz', ['JavaScript Design Patterns']); author[1] = new Author('Ross Harmes', ['JavaScript Design Patterns']); document.write(author[0].getName() + "<br/>"); document.write(author[1].getName()); </script> </head> <body> <!-- Nothing in the body --> </body> </html> <html> <head> <title>String Example</title> <script type="text/javascript"> //函数与函数间指针的传递 /* Clone function. */ function clone(object){ function F(){ } F.prototype = object; return new F; } /* * 指针型继承Prototypal Inheritance * 指针型继承非常不同。我们发现最好的去思考它的方法是忘掉一切关于类和实例化的方法。只考虑对象本身 * 典型创建对象有两种方法 * a.定义对象结构,使用类声明 * b.实例化类,并创建对象 * 通过实例化类来创建的对象是拷贝所有实例化的属性,连接所有实例化方法的地址 * 在指针型继承中,你能很简单的建一个类。这个类可以被其他类重用 */ /* Person Prototype Object. */ var Person = { name: 'default name', getName: function(){ return this.name; } }; //简单对象的继承和测试 var reader = clone(Person);document.write("<h5 style='color:red'>simple object inherited and testing</h5>"); document.write(reader.getName() + "<br/>"); // This will output 'default name'. reader.name = 'John Smith'; document.write(reader.getName() + "<br/>"); // This will now output 'John Smith'. /* Author Prototype Object. */ var Author = clone(Person); Author.books = []; // Default value. Author.getBooks = function(){ return this.books; } var author = []; author[0] = clone(Author); author[0].name = 'Dustin Diaz'; author[0].books = ['JavaScript Design Patterns']; author[1] = clone(Author); author[1].name = 'Ross Harmes'; author[1].books = ['JavaScript Design Patterns'];document.write("<h5 style='color:red'>Author object inherited and testing</h5>"); document.write(author[0].getName() + ' ' + author[0].getBooks() + "<br/>"); document.write(author[1].getName() + ' ' + author[1].getBooks() + "<br/>"); /* * 异步读写父类成员 * 为了有效的使用指针型继承,我们必须忘掉所有传统的继承方式。这就是所要忘记的方式之一。 * 在传统的继承里,每个Author类都拷贝了books数组。你可以通过author[1].books.push('New Book Title')添加到数组 * 这种方式不能用于指针型继承。因为prototype链起了作用 * clone函数并不是object对象的prototype完全独立的拷贝。 * 当你读取author[1].name,你只是得到从prototype那里保存的连接。并没有得到实例的值 * 当你写一个author[1].name,实际是为author[1]对象添加了一个新的属性 */ var authorClone = clone(Author);document.write("<h5 style='color:red'>Symmetrical Reading and Writing of Inherited Members</h5>"); document.write("Name="+authorClone.getName() + "<br/>"); // Linked to the primative Person.name, which is the string 'default name'. document.write("Books="+authorClone.getBooks() + "<br/>"); authorClone.name = 'new name'; // A new primative is created and added to the // authorClone object itself. document.write("Name="+authorClone.getName() + "<br/>"); // Now linked to the primative authorClone.name, which // is the string 'new name'. authorClone.books.push('new book'); // authorClone.books is linked to the array document.write("Books="+authorClone.getBooks() + "<br/>"); // Author.books. We just modified the // prototype object's default value, and all // other objects that link to it will now // have a new default value there. authorClone.books = []; // A new array is created and added to the authorClone // object itself. authorClone.books.push('a new book to array'); // We are now modifying that new array. document.write("<h5 style='color:red'>Asymmetrical Reading and Writing of Inherited Members</h5>"); document.write(authorClone.getName() + ' ' + authorClone.getBooks() + "<br/>"); </script> </head> <body> <!-- Nothing in the body --> </body> </html> <html> <head> <title>String Example</title> <script type="text/javascript"> /* Clone function. */ function clone(object){ function F(){ } F.prototype = object; return new F; } /* * 有时候,指针对象会有子对象包含其中。 * 如果你想重写子对象某一个单一的方法,你必须重建整个对象 * 这可以通过将子对象复制给空对象实现 * 但是这要求被克隆的对象必须明确子对象的结构和默认值 * 为了保持各对象之间的低耦合度,任何复杂的子对象都应该用固定的方法创建 * 工厂模式因此而产生 */ var CompoundObject = {}; CompoundObject.string1 = 'default value'; CompoundObject.createChildObject = function(){ return { bool: true, num: 10 } }; CompoundObject.childObject = CompoundObject.createChildObject(); var compoundObjectClone = clone(CompoundObject); compoundObjectClone.childObject = CompoundObject.createChildObject(); compoundObjectClone.childObject.num = 5;document.write(compoundObjectClone.string1 + "<br/>");document.write(compoundObjectClone.childObject.num); </script> </head> <body> <!-- Nothing in the body --> </body> </html> <html> <head> <title>Extend Example</title> <script type="text/javascript"> //Ext类的第一个细胞原型//骨架模式。采用Prototypal Inheritance的指针继承方式 //ext类是用隐私的实例化类开始进行构造的。 Ext = { version: '3.0' };//Ext类之对象拷贝函数。实现对象与对象之间的直接复制。/* * in这个操作符在IE中无法识别非枚举属性 * 以下非枚举属性toString(),hasOwnProperty() ,propertyIsEnumerable() , toLocaleString() , valueOf() * Ext的解决方法是用overrides重新写了apply这个函数,其中所有toString的方法全部由object类的方法覆盖。 * apply的另一变形是applyif。但是如果某属性存在,则不会被覆盖。 */ Ext.apply = function(o, c, defaults){ // no "this" reference for friendly out of scope calls if (defaults) { Ext.apply(o, defaults); } if (o && c && typeof c == 'object') { for (var p in c) { o[p] = c[p]; } } return o; }; Ext.apply(Ext, {//判断类型v是否是object类 isObject: function(v){ return v && typeof v == "object"; },//Ext类之继承属性/* * 闭包: * 1、作为一个函数变量的一个引用 - 当函数返回时,其处于激活状态。 * 2、一个闭包就是当一个函数返回时,一个没有释放资源的栈区。 *///Ext是个匿名函数 extend: function(){ // inline overrides//这句是将所有的supperclass,subclass,overrides方法拷贝到io类备用。 var io = function(o){ for (var m in o) { this[m] = o[m]; } }; var oc = Object.prototype.constructor; return function(sb, sp, overrides){//如果传递进来的第二个参数是object。//拷贝子类到overrides类,如果overrides里面有个constructor属性,//就用overrides的constructor当作子类的构造函数。//否则,创建个新的function,里面包含一句话,就是"sp.apply(this, arguments);",//这个又是闭包的一个应用,在退出extend方法之后并没有释放局部变量sp的内存空间。//子类拷贝完成 if (Ext.isObject(sp)) { overrides = sp; sp = sb; sb = overrides.constructor != oc ? overrides.constructor : function(){ sp.apply(this, arguments); }; }//var F=function(){},定义一个空函数,里面没有属性。//F.prototype=sp.prototype//sbp=new F()把F.prototype也就是sp.prototype里面的东西拷贝到sb.prototype//同时,因为F是个没有任何属性的函数,所以不需要再delete任何东西。//将超类拷贝到了sbp。//以父类为骨架,并将更多的方法和属性添加到子类中,代码值得借鉴 var F = function(){ }, sbp, spp = sp.prototype; F.prototype = spp; sbp = sb.prototype = new F();//sb.prototype.constructor是F(),所以sbp.constructor=sb sbp.constructor = sb; sb.superclass = spp; if (spp.constructor == oc) { spp.constructor = sp; } sb.override = function(o){ Ext.override(sb, o); }; sbp.superclass = sbp.supr = (function(){ return spp; }); sbp.override = io; Ext.override(sb, overrides); sb.extend = function(o){ Ext.extend(sb, o); }; return sb; }; }(),//和apply一样的功能,复制对象。如果子类重写了toString,则覆盖父类。 override: function(origclass, overrides){ if (overrides) { var p = origclass.prototype; Ext.apply(p, overrides); if (Ext.isIE && overrides.toString != origclass.toString) { p.toString = overrides.toString; } } } });//实例测试//申请一个新的类S function S(){ } //S中两个变量s,s1 S.prototype.s = "s"; S.prototype.s1 = "s1";//申请一个类C//C中两个变量c,c1 function C(){ this.c = "c"; this.c1 = "c1"; } //C继承S基类,并重写s1 Ext.extend(C, S, { s1: "by c overload" });//实例化C var c = new C(); alert(c.s); //s alert(c.s1); //by c overload </script> </head> <body> <!-- Nothing in the body --> </body> </html>