首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 网站开发 > JavaScript >

初识ext懂得js的潮流

2012-10-26 
初识ext了解js的潮流开篇,原本想大量引用网上对Ext-base.js文件的分析。但是经过大量的阅读国外的书籍发现,

初识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,委派了两个方法给prototype属性。所谓的prototype,我的理解是指针。实际上它和c++,c中指针的运行方法有微小的差别,但大致一样。详情请参考上面的两本书。
如果你喜欢把所有的属性和方法都装在类中。如下:
/* Anim class, with a slightly different syntax for declaring methods. */var Anim = function() {...};Anim.prototype = {start: function() {...},stop: function() {...}};

通过以上的操作,产生一个新的概念:Encapsulation。我的理解:装箱。就是把所有的属性和方法放到类中。
值得谈到的是Javascript中的函数。在javascript中函数就是类。
函数的大部分概念和C++没有什么区别。值得注意的是匿名函数.
<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() {...});

在javascript中,所有的东西都是对象,所有的对象都是可变的。这就意味着你可以在类定义好,并实例化以后改变它。
<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>

这种对象的可变性叫:自我检查introspection.你能实时的检查任何的属性和方法。这正因为对象可以实时改变,javascript才很少进行类型检查。
在javascript中,还有个根深蒂固的概念。闭包Closures。闭包就是在类中定义的函数。由于对象具有可变性,因此我们可以在类体意外去调用和改变类中定义的函数。
<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> 

在javascript中,能够想到的创建对象的样式有三种。完全显示的对象(最简单但只提供公有成员)。使用括号标识方法和属性来区别私有成员(只是伪代码),详情请参考上面的书籍。最后是通过闭包的方法建立真正的私有成员。通过闭包实现私有成员。
有了闭包的特性,是可以实现真实的私有成员和公有成员的。在javascript中没有私有,公有的特性的。但是在实际的程序当中,我们往往要用到这些特性。这也是面向对象的重要部分之一。
<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> 

但是这么写的类,虽然是完成了类成员的私有,公有特性。但是这样的方法很难被继承,同时也造成大量的内存冗余。因为javascript中没有资源回收的机制。在类使用完毕之后,存储空间不会随之消失。我们还需要静态方法和真正的动态空间存储的方法。
<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> 

静态方法的实现,实际上是利用匿名函数,在内存的堆中申请一片空间。类中只记录这片存储区域的地址。类调用完成,匿名函数随之消失。同时类中申请的静态变量保留了下来。
有了上面面向对象的公有私有成员和方法,对象可变性的概念。来看看js中继承的实现
Javascript的语法提供使用基于对象的继承。我们可以用它来模拟基于类的继承。
<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> 

尽管代码相当简单,利用prototype这个指针进行地址的链接。但是它却是个很复杂的话题。javascript没有继承关键字。与之替代的,每个object对象都有prototype属性。这个属性指向另一个对象或者null。当对象成员被读取,javascript通过prototype读取该对象。如果它在现在的对象中不存在,javascript会读取每个对象的prototype。直到读到该对象或者null,这和其他的面向对象语言完全不一样。这种方法使得多个子类被实例化的同时,父类也同样被实例化很多次。浪费了内存。在子类实例化的情况下,实例化改变父类,子类会受到很大影响。
基于以上原因,必须要改变继承的方式。
<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> 

继承方式已经实现。但是离ext-base.js的目标还差得远。在此,先提出一个概念:Prototypal Inheritance,我的理解为:指针型继承。指针型继承非常不同。我们发现最好的去思考它的方法是忘掉一切关于类和实例化的方法。只考虑对象本身,典型创建对象有两种方法
a.定义对象结构,使用类声明
b.实例化类,并创建对象(网上把它叫做对象直接量)
通过实例化类来创建的对象是拷贝所有实例化的属性,连接所有实例化方法的地址在指针型继承中,这样做的优点是你能很简单的建一个类。这个类可以被其他类重用。
<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> 

通过Prototypal Inheritance的指针继承,我们体会到一种设计模式:骨架模式。骨架模式就是基类设置好所有的属性和方法,并赋默认值。子类需要自己的方法可以任意的添加。这里引出一个概念:异步读写父类成员。为了有效的使用指针型继承,我们必须忘掉所有传统的继承方式。这就是所要忘记的方式之一。在传统的继承里,每个Author类都拷贝了books数组。你可以通过author[1].books.push('New Book Title')添加到数组。这种方式不能用于指针型继承。因为prototype链起了作用。 clone函数并不是object对象的prototype完全独立的拷贝。当你读取author[1].name,你只是得到从prototype那里保存的连接。并没有得到实例的值。当你写一个author[1].name,实际是为author[1]对象添加了一个新的属性。通俗的讲,父类给出了骨架,子类去加肉。
但是有时候,指针对象会有子对象包含其中。如果你想重写子对象某一个单一的方法,你必须重建整个对象。这可以通过将子对象复制给空对象实现。但是这要求被克隆的对象必须明确子对象的结构和默认值。为了保持各对象之间的低耦合度,任何复杂的子对象都应该用固定的方法创建。工厂模式因此而产生。
<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> 

到此,ext-base.js的所有概念都有了。下面开始具体分析extend类
<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> 


1 楼 laitaogood 2009-09-29   太长了,没看完,但是注释写的非常好,哈哈

热点排行