• 从Prototype学习JavaScript面向对象编程



    概述

    JavaScript是一种基于对象的编程语言。它是灵活的,既有面向过程(也就是面向函数)的编程,也有面向对象的编程。因此我称它是基于对象的编程语言。

    对于JavaScript的面向过程的编程特性,就不用多说了,学过C语言的,就很容易理解什么是面向函数的编程。我看Prototype源码主要就是来理解在JavaScript中的面向对象编程的。

    我在学习Prototype源码时,将结合CC#Java来学习和比较JavaScript中的面向对象编程。我阅读的Prototype的版本是1.7.1 

    学习Prototype需要的基础知识

    1、什么是JavaScript对象?

    JavaScript中,对象很简单,就是一对花括号 { } 

    2、了解JavaScript编程的语法和句法

    了解JavaScript编程的语法和句法,说白了就是了解JavaScript中编程中常用的符合、关键字、变量定义、控制语句、循环语句、数组等等。这些学过C语言的人都知道。

     

    阅读Prototype源码需要的辅助工具

    在学校宿舍,用的是路由器上网,联通老是拦截导致打不开网页。这样对学习是个很大的障碍,只能从官方网站上下载离线的APIDoc 。通过离线文档学习了。如果可以上网,就不必下载离线APIDoc了,并且还可以直接看官方提供的Demo的。

     

     

     

    面向对象编程

    从官方的文档上来看,将Prototype分为3大块,分别是Ajax SectionDOM SectionLanguage Section。我对这三块的理解是:Language是讲语言的,是基础。DOM Section是对HTML文档的操作。Ajax Section是使用JavaScript和后台通信。

    先学习语言部分。

     

    Language

    Class对象

     

    var Class = (function() {

     

      var IS_DONTENUM_BUGGY = (function(){

        for (var p in { toString: 1 }) {

          if (p === 'toString') return false;

        }

        return true;

      })();

     

      function subclass() {};

    function create() {

        var parent = null, properties = $A(arguments);

        if (Object.isFunction(properties[0]))

          parent = properties.shift();

     

        function klass() {

          this.initialize.apply(this, arguments);

        }

     

        Object.extend(klass, Class.Methods);

        klass.superclass = parent;

        klass.subclasses = [];

     

        if (parent) {

          subclass.prototype = parent.prototype;

          klass.prototype = new subclass;

          parent.subclasses.push(klass);

        }

     

        for (var i = 0, length = properties.length; i < length; i++)

          klass.addMethods(properties[i]);

     

        if (!klass.prototype.initialize)

          klass.prototype.initialize = Prototype.emptyFunction;

     

        klass.prototype.constructor = klass;

        return klass;

      }

     

      function addMethods(source) {

        var ancestor = this.superclass && this.superclass.prototype,

        properties = Object.keys(source);

     

        if (IS_DONTENUM_BUGGY) {

          if (source.toString != Object.prototype.toString)

            properties.push("toString");

          if (source.valueOf != Object.prototype.valueOf)

            properties.push("valueOf");

        }

     

        for (var i = 0, length = properties.length; i < length; i++) {

          var property = properties[i], value = source[property];

          if (ancestor && Object.isFunction(value) &&

              value.argumentNames()[0] == "$super") {

            var method = value;

            value = (function(m) {

              return function() { return ancestor[m].apply(this, arguments); };

            })(property).wrap(method);

     

            value.valueOf = (function(method) {

              return function() { return method.valueOf.call(method); };

            })(method);

     

            value.toString = (function(method) {

              return function() { return method.toString.call(method); };

            })(method);

          }

          this.prototype[property] = value;

        }

     

        return this;

      }

     

      return {

        create: create,

        Methods: {

          addMethods: addMethods

        }

      };

    })();

    上面是Class对象的源码。

     

    为什么说是Class对象呢?

    从上面的代码的结构是var Class=(function(){})(); 

    这个结构在JavaScript中,意思是:先声明一个匿名函数,然后就执行这个匿名函数。

    在这个匿名函数的内部,有个return 语句,就是函数执行后返回的内容。返回的内容是一个对象:

    {

    create: create,

        Methods: {

          addMethods: addMethods

        }

    }

    也就是说,最后代码的样子是:

    var Calss={

    create: create,

        Methods: {

          addMethods: addMethods

        }

    }

     

    所以我说Class是个对象。

     

    学习过程中,要按照浏览器处理的思维来学习Prototype框架。

    浏览器预处理Class对象的过程是:

    1)先知道了产生Class对象的匿名函数的整体结构:

     

    然后执行该匿名函数。执行后返回一个对象,将这个返回的对象赋值给Class

    自定义定义工具类的2种方式

    从这个匿名函数(也可以称之为匿名类)中,知道在这个函数中声明了3个函数。但是返回的对象中,却只有2个。

    create方法的访问方式是Class.create,

    addMethods方法的访问方式是Class.Methods.addMethods。但是继续往下看,addMethods并不这样去用。这个就先不管了。

    Class.create 就是执行create方法。从这个写法上来看,让我们有种感觉,感觉这种写法和Java中的访问类的静态方法一样。

    所以我可以暂且这样认为:JavaScript中,函数也是类,不过是实例类。对象也可以是类,对象中的函数。因此在自己写工具类的时候,就可以这么做。

     

    例如下面这个例子:将一个字符串倒序。

    写了一个js文件StringUtil.js,写了这么一段代码

     

    在下面的HTML文件中引用:

     

     

    这个小程序,证明了,我的想法的可行性。但是如果写的工具类中,方法比较多的话,其中某一个或者一些方法只是在这个工具类中的其他方法中使用,也就是说不会开发使用,这样该如何做呢?

    java中,这样的方法,我们一般用private关键字来修饰的,但是JavaScript中没有privatepublic这样的关键字,怎么做呢?

     

    其实我们可以像Class对象那样去定义自己的工具类。在Class对象中有个方法function subclass(){}

    但是return中并没有这个方法。也就是说subclass()是不能直接访问的。这样无形中就相当于给subclass(){}加了个private关键字一样。

     

     

     

     

     

     

     

     

     

     

    两种方式比较,个人认为第二种方式更好一些。

     

     

    UI框架中组件初始化的原理

    看了PrototypeClss的源码后,我理解了很多UI框架进行初始化的原理了。

    譬如ligerUIzTreeeasyUI等。

     

     

    var Class = (function() {

     

      var IS_DONTENUM_BUGGY = (function(){

        for (var p in { toString: 1 }) {

          if (p === 'toString') return false;

        }

        return true;

      })();

     

     

      function subclass() {};

     

      function create() {

     

    // 如果在使用Class.create()时,传递的参数中,第一个是个方法(也就是类),那么就是要继承这个类

        var parent = null, properties = $A(arguments);

        if (Object.isFunction(properties[0]))

          parent = properties.shift();

     

    // klass代表你要创建的类

        function klass() {

          this.initialize.apply(this, arguments);

        }

     

    // Class.Methods给了klassClass.Methods{addMethods},如此就将addMethods方法给了要创建的类:klass

    //  也就是说,这句话是让你要创建的类的对象klass也具备了addMethods方法。

    // 之所以让klassaddMethods方法,是想让klass对象也具备你在调用Class.create()中传递的参数作为属性。

        Object.extend(klass, Class.Methods);

        klass.superclass = parent;

        klass.subclasses = [];

     

        if (parent) {

          subclass.prototype = parent.prototype;

          klass.prototype = new subclass;

          parent.subclasses.push(klass);

        }

     

    // 将传递的参数都变为klass对象的属性

        for (var i = 0, length = properties.length; i < length; i++)

          klass.addMethods(properties[i]);

     

    // 判断你要创建的类klass有没有initialize方法,如果没有,就指定一个空方法给它

    // 如此一来,你要创建的类中必定有initialize方法。如果你在使用Class.create来创建你的类的时候,传递了

    // initialze方法就是表示你自定义了一个initialize方法

        if (!klass.prototype.initialize)

          klass.prototype.initialize = Prototype.emptyFunction;

     

        klass.prototype.constructor = klass;

        return klass;

      }

     

    // 在调用Class.create方法时,你可能传递多个参数,每个参数又是一个自定义比较复杂的对象,这种情况是很有可能的。

      function addMethods(source) {

        var ancestor   = this.superclass && this.superclass.prototype,

    // 将每一个参数中所有的属性名取出赋值给properties

            properties = Object.keys(source);

     

        if (IS_DONTENUM_BUGGY) {

    // 如果你重写了toStringvalueOf方法,那么就使用你自定义的toStringvalueOf方法

          if (source.toString != Object.prototype.toString)

            properties.push("toString");

          if (source.valueOf != Object.prototype.valueOf)

            properties.push("valueOf");

        }

     

    // Class.create中每一个参数对象,都要取得他们所有属性,然后再对每个属性进行遍历

     

        for (var i = 0, length = properties.length; i < length; i++) {

          var property = properties[i], value = source[property];

      // 如果属性是个函数,并且函数的第一个参数名是$super,那么就代表要调用父类的对应方法

      // 例如子类继承父类,传递的参数中,有个参数中有一个方法和父类中的方法是同名的,并且

      // 这个方法的第一个参数是$super,那么就是代表这个方式是使用父类中的这个方法。

          if (ancestor && Object.isFunction(value) &&

              value.argumentNames()[0] == "$super") {

            var method = value;

            value = (function(m) {

              return function() { return ancestor[m].apply(this, arguments); };

            })(property).wrap(method);

     

            value.valueOf = (function(method) {

              return function() { return method.valueOf.call(method); };

            })(method);

     

            value.toString = (function(method) {

              return function() { return method.toString.call(method); };

            })(method);

          }

          this.prototype[property] = value;

        }

     

        return this;

      }

     

      return {

        create: create,

        Methods: {

          addMethods: addMethods

        }

      };

    })();

     

    官方的demo可以验证理解的正确性,也可用通过官方的demo来辅助自己理解:

     

  • 相关阅读:
    Windbg命令学习11(.dump)
    Windbg命令学习13(ln和伪寄存器)
    Windbg命令学习15(bp bm bu bl bc ba断点)
    Windbg命令学习16(!gle和g和p)
    Windbg命令学习0 (.symfix和.cls和设置Log文件)
    API拦截方法一:PE简介
    Windbg命令学习12(.lastevent和!analyze)
    Windbg命令学习14(dv)
    Davinci DM6446开发攻略——LINUX GPIO驱动源码移植
    Centos 编译安装高版本Python方法
  • 原文地址:https://www.cnblogs.com/f1194361820/p/3271799.html
Copyright © 2020-2023  润新知