资讯专栏INFORMATION COLUMN

prototype.js 是如何实现JS的类以及类的相关属性和作用

Snailclimb / 2103人阅读

摘要:实现类的步骤第一步是使用新建类,初始化的固定函数是,不能使用其它名称子类也是使用新建,父类放在第一个参数中,如子类中与父类的同名方法,如果需要在父类的同名方法上拓展,在需要在第一个参数中使用,然后在方法体内使用如果需要在类的外面增加方法,可

实现类的步骤

第一步是使用Class.create新建类,初始化的固定函数是initialize,不能使用其它名称

子类也是使用Class.create新建,父类放在第一个参数中,如var Cat = Class.create(Animal,{});

子类中与父类的同名方法,如果需要在父类的同名方法上拓展,在需要在第一个参数中使用$super,然后在方法体内使用$super(args);

如果需要在类的外面增加方法,可以使用addMethods方法

// 使用 Class.create 创建类
    var Person = Class.create({
        // 初始函数固定为 initialize, 如果不设置,会默认创建一个空函数给 initialize
        initialize:function(name) {
            this.name = name;
            this.friends = ["jack", "mark"];
        },
        getName: function(){
            console.log("My name is " + this.name);
        },
        setFriends:function(friend){
            this.friends.push(friend);
        },
        getFriends:function(){
            console.log(this.friends)
        }
    });

    // 使用 addMethods 给在类的初始构建之外添加方法,子类可以继承该方法
    Person.addMethods({
        getAge:function(age){
            console.log("My age is " + age);
        }
    })

    // 子类通过 Class.create 创建类,第一个参数为父类的名字
    var Chinese = Class.create(Person,{
        // 使用 $super 为第一个参数,表示当前函数在父类的同名函数上拓展
        initialize:function($super, name, addr){
            $super(name);
            this.addr = addr;
        },
        getAddr:function(){
            console.log("My address is " + this.addr);
        }
    });

    var Japanese = Class.create(Person, {
        initialize:function($super, name){
            $super(name);
        }
    })

    // 实例化类
    var men = new Chinese("allen", "BeiJing");
    men.getName(); // My name is allen
    men.getAge(23); // My age is 23
    men.getAddr(); // My address is BeiJing

    // 以下验证 - 子类继承父类的属性,修改了之后,其他子类再次继承父类,父类的属性的值为何不会改变
    var allen = new Person();
    allen.getFriends(); // ["jack", "mark"]

    var women = new Japanese();
    women.setFriends("lisa");
    women.getFriends(); // ["jack", "mark", "lisa"]

    var men = new Chinese();
    men.setFriends("peter");
    men.getFriends(); //["jack", "mark", "peter"]

    var wallen = new Person();
    wallen.getFriends(); //["jack", "mark"]

JS是如何实现类的方法,有几个重要的问题需要搞清楚

JS是如何创建类的

子类是如何实现继承父类属性和方法的

子类继承父类的属性,修改了之后,其他子类再次继承父类,父类的属性的值为何不会改变

子类和父类的同名函数,在同名函数中使用$super,是如何做到在子类中共存的

如何实现,不在类中,而是使用addMethods往类中添加方法的

下面来通过prototype.jsclass来具体分析

var Class = (function() {

  function subclass() {};
  function create() {
    // ... 
  }

  function addMethods(source) {
    // ...
  }

  // 暴露给外部的接口
  return {
    create: create,
    Methods: {
      addMethods: addMethods
    }
  };
})();

内部实现其实很简单,Class是一个立即执行函数,里面只有三个函数,而且subclass还是个空函数

/* Based on Alex Arnell"s inheritance implementation. */

/**
 *  Refer to Prototype"s web site for a [tutorial on classes and
 *  inheritance](http://prototypejs.org/learn/class-inheritance).
**/
var Class = (function() {

  function subclass() {};
  function create() {
    // $A 函数就是把参数转化成数组
    var parent = null, properties = $A(arguments);
    // 如果第一个参数是函数,就把他当作父类
    if (Object.isFunction(properties[0]))
      parent = properties.shift();

    function klass() {
      // klass 是新建的类,把传入的参数都绑定到 klass 的 initialize 方法中
      this.initialize.apply(this, arguments);
    }

    // 把通过 extend 方法,把 Class.Methods 的方法添加到 klass 中
    Object.extend(klass, Class.Methods);
    // 这里有指定 klass 的父类是哪一个
    klass.superclass = parent;
    klass.subclasses = [];

    if (parent) {
      // 这里通过把父类的原型方法,都继承到当前类中
      // 通过中间变量 subclass 来传递 prototype 来防止由于子类的修改而导致父类的属性或者方法也被修改
      subclass.prototype = parent.prototype;
      // 每次子类都会创建一个新的中间变量来传递,所以无论子类怎么修改属性,都不会影响到父类
      klass.prototype = new subclass;
      // 把当前类添加到父类的子类中
      parent.subclasses.push(klass);
    }

    for (var i = 0, length = properties.length; i < length; i++)
      // 前面把 addMethods 方法添加到 klass 中,这里就可以使用 addMethods 把传入参数中的方法,添加到 klass 中了
      klass.addMethods(properties[i]);

    // 如果 klass 没有初始化函数,就设置一个空函数
    if (!klass.prototype.initialize)
      klass.prototype.initialize = Prototype.emptyFunction;

    // 把 klass 的构造函数指向自身
    klass.prototype.constructor = klass;
    return klass;
  }

  
  // source 是所有要添加进来方法的集合
  function addMethods(source) {
    var ancestor   = this.superclass && this.superclass.prototype,
        properties = Object.keys(source);

    for (var i = 0, length = properties.length; i < length; i++) {
      // value 就是单个的方法
      var property = properties[i], value = source[property];
      // 如果参数中的第一个参数是 $super,就需要把父类的同名方法,传递进来
      if (ancestor && Object.isFunction(value) &&
          value.argumentNames()[0] == "$super") {
        // 把最初的 value 使用 method 存起来
        var method = value;
        // 继承父类的同名方法,然后把当前参数传进去
        value = (function(m) {
          return function() { return ancestor[m].apply(this, arguments); };
        })(property).wrap(method);
          // wrap 是把父类的同名方法,添加当前类的同名方法中

        // We used to use `bind` to ensure that `toString` and `valueOf`
        // methods were called in the proper context, but now that we"re
        // relying on native bind and/or an existing polyfill, we can"t rely
        // on the nuanced behavior of whatever `bind` implementation is on
        // the page.
        //
        // MDC"s polyfill, for instance, doesn"t like binding functions that
        // haven"t got a `prototype` property defined.
        // 将 valueOf 的方法绑定到 method 中
        value.valueOf = (function(method) {
          return function() { return method.valueOf.call(method); };
        })(method);

          // 将 toString 的方法绑定到 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
    }
  };
})();

上面使用到的wrap函数,摘抄在下面

function wrap(wrapper) {
    var __method = this;
    return function() {
      var a = update([__method.bind(this)], arguments);
      return wrapper.apply(this, a);
    }
  }

// 这里就是把 args 中的属性,都添加到 array 中
function update(array, args) {
    var arrayLength = array.length, length = args.length;
    while (length--) array[arrayLength + length] = args[length];
    return array;
  }

JS面向对象系列

《javascript高级程序设计》 继承实现方式

Mootools.js 是如何实现类,以及类的相关属性和作用

klass 是如何实现JS的类以及类的相关属性和作用

总结:prototype.js,Mootools.js和klass.js 实现类的方法的异同与优劣

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/84535.html

相关文章

  • klass 如何实现JS的类以及类的相关属性作用

    摘要:前面介绍了和是如何实现类,及其类的属性和作用的。今天介绍的就是单纯的实现面向对象的库,只有多行,也照例分析吧。 前面介绍了prototype.js和Mootools.js是如何实现类,及其类的属性和作用的。今天介绍的klass.js就是单纯的实现面向对象的库,只有90多行,也照例分析吧。 实现类的步骤 第一步是使用klass新建类,初始化的固定函数是initialize,不能使用其它...

    Kross 评论0 收藏0
  • Mootools.js 如何实现类,以及类的相关属性作用

    摘要:实现类的步骤第一步是使用新建类,初始化的固定函数是,不能使用其它名称子类也是使用新建,父类在子类中,使用来继承,与子类的方法名,同一级别子类中与父类的同名方法,如果需要在父类的同名方法上拓展,需要在子类的同名方法内,使用如果需要在类的外面增 实现类的步骤 第一步是使用new Class新建类,初始化的固定函数是initialize,不能使用其它名称 子类也是使用new Class新建...

    gitmilk 评论0 收藏0
  • 总结:prototype.js,Mootools.jsklass.js 实现类的方法的异同与优劣

    摘要:构建类的方法使用来构建类使用来构建类使用来构建类继承父类的方法使用子类方法构建子类,继承父类,在与父类同名的方法中,第一个参数为,方法体内使用来拓展父类的同名方法使用正常构建类后,第一个方法使用来继承父类,在子类的方法体中,使用来拓展父类的 构建类的方法 Prototype.js使用Class.create来构建类 Mootools.js使用new Class来构建类 klass.j...

    jeffrey_up 评论0 收藏0
  • 《javascript高级程序设计》 继承实现方式

    摘要:寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部已某种方式来增强对象,最后再像真的是它做了所有工作一样返回对象。 这篇本来应该是作为写JS 面向对象的前奏,只是作为《javascript高级程序设计》继承一章的笔记 原型链 code 实现 function SuperType() { this.colors = [red,blu...

    cppprimer 评论0 收藏0
  • ES7-Decorator-装饰者模式

    摘要:装饰者要实现这些相同的方法继承自装饰器对象创建具体的装饰器,也是接收作对参数接下来我们要为每一个功能创建一个装饰者对象,重写父级方法,添加我们想要的功能。 装饰模式 仅仅包装现有的模块,使之 更加华丽 ,并不会影响原有接口的功能 —— 好比你给手机添加一个外壳罢了,并不影响手机原有的通话、充电等功能; 使用 ES7 的 decorator ES7 中增加了一个 decorator 属性...

    zhangxiangliang 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<