继承类javascript面向对象包装类Class的类库解析

在改章节中,我们重要介绍继承类的内容,自我感觉有个不错的建议和大家分享下

    javascript是个入门槛门很低的语言,甚至一个从来没有触接过javascript的技术人员,几小时内就能够写出一个简略有效的程序代码。

    但是如果因此你就下论断:javascript是门简略的语言。那你就大错特错了。想写出高性能的代码,一样须要具有一个高等程序员的基本素养。

    一个java或者c++程序员,纷歧定能写出高性能的javascript代码,但更易容写出高性能的javascript代码。

    javascript的简略是基于它“襟胸辽阔”的包容性。它声明时,不须要指定型类,甚至可以意任的换转型类。它面向对象,却没有类(Class)的制限。它是一门崇尚由自又非常谨严的语言,如果你是一个由自主义者,那么,拥抱javascript吧!

    面向对象编程 (OOP) 是一种风行的编程方法。但javascript的OOP,较之JAVA、c++有很大的同,重要表现它的继承式方不同。javascript是基于原型PROTOTYPE继承的。有所对象都是基于原型链,终最追述到Object对象。

    这里不想论讨过量的关于javascript的继承式方和其它语言的继承式方的不同之处。重要论讨如何封装javascript的Class,以便更好的理管和维护础基代码,增加重复代码,以及更好的模块化编程。

 

    面下是几个github上找到的较比好的Class封装类库:

      一、MY-CLASS 

  目项址地:https://github.com/jiem/my-class

    先看基本用法:

    a、建新一个类

(function() {
  // 建新类
  var Person = my.Class({
    // 加添静态方法
    STATIC: {
      AGE_OF_MAJORITY: 18
    },
    // 结构函数
    constructor: function(name, age) {
      this.name = name;
      this.age = age;
    },
    // 实例方法
    sayHello: function() {
      console.log('Hello from ' + this.name + '!');
    },
    // 实例方法
    drinkAlcohol: function() {
      this.age < Person.AGE_OF_MAJORITY ?
        console.log('Too young! Drink milk instead!') :
        console.log('Whiskey or beer?');
    }

  });
  // 暴露给命名空间
  myLib.Person = Person;

})();

var john = new myLib.Person('John', 16);
john.sayHello(); //log "Hello from John!"
john.drinkAlcohol(); //log "Too young! Drink milk instead!"
b、继承一个类
(function() {

  //Dreamer 继承 Person
  var Dreamer = my.Class(Person, {
    // 结构方法
    constructor: function(name, age, dream) {
      Dreamer.Super.call(this, name, age);
      this.dream = dream;
    },
    // 实例方法
    sayHello: function() {
      superSayHello.call(this);
      console.log('I dream of ' + this.dream + '!');
    },
    // 实例方法
    wakeUp: function() {
      console.log('Wake up!');
    }

  });
  // Super拜访类父
  var superSayHello = Dreamer.Super.prototype.sayHello;
  // 暴露给全局命名空间
  myLib.Dreamer = Dreamer;

})();

var sylvester = new myLib.Dreamer('Sylvester', 30, 'eating Tweety');
sylvester.sayHello(); //log "Hello from Sylvester! I dream of eating Tweety!"
sylvester.wakeUp(); //log "Wake up!"
c、给类加添新方法
// 给myLib.Dreamer加添新方法
my.extendClass(myLib.Dreamer, {
  // 加添静态方法
  STATIC : {
    s_dongSomeThing : function(){
        console.log("do some thing!");    
    }
  },
  // 加添实例方法
  touchTheSky: function() {
    console.log('Touching the sky');
  },
  // 加添实例方法
  reachTheStars: function() {
    console.log('She is so pretty!');
  }

});
  d、现实一个类的方法
// 声明一个新类
myLib.ImaginaryTraveler = my.Class({
  travel: function() { console.log('Traveling on a carpet!'); },
  crossOceans: function() { console.log('Saying hi to Moby Dick!'); }
});

(function() {

  //Dreamer 继承 Person 现实 ImaginaryTraveler的方法
  var Dreamer = my.Class(Person, ImaginaryTraveler, {
    // 结构方法
    constructor: function(name, age, dream) {
      Dreamer.Super.call(this, name, age);
      this.dream = dream;
    }

   // ...

  });
  // 暴露给全局命名空间
  myLib.Dreamer = Dreamer;

})();

var aladdin = new Dreamer('Aladdin');
aladdin instanceof Person; //true
aladdin instanceof ImaginaryTraveler; //false
aladdin.travel();
aladdin.wakeUp();
aladdin.sayHello();
如果怕记忘new操作符
var Person = my.Class({

  //you can now call the constructor with or without new
  constructor: function(name, city) {
    if (!(this instanceof Person))
      return new Person(name, city);
    this.name = name;
    this.city = citye;
  }

});
面下看一下my.class的源代码剖析:

    my.Class现实路思基本是这样的,如果只有一个参数,那么声明的是一个础基类,这个参数是用来声明新类的方法和属以及结构函数。它不是继承而来,但它可以被继承。

    继承的路思,就是如果有两个参数,第一个参数做为类父被继承,第二参数用来声明新类的方法和性属以及结构函数,它一样可以被继承。

    如果有三个以上参数那么,除出第一个参数做为继承的类父,最后一个参数用声明新类的方法和性属以及结构函数。间中的参数是用类来扩展新类的方法。当然也可以通过my.extendClass扩展新方法。

    同时,类库为commonJS和览浏环境都供提了持支!

/*globals define:true, window:true, module:true*/
(function () {
  // Namespace object
  var my = {};
  // 保障AMD分模块可用
  if (typeof define !== 'undefined')
    define([], function () {
      return my;
    });
  else if (typeof window !== 'undefined')
    // 保障客户端可用
    window.my = my;
  else
    // 保障后台可用
    module.exports = my;

  //============================================================================
  // @method my.Class
  // @params body:Object
  // @params SuperClass:function, ImplementClasses:function..., body:Object
  // @return function
  my.Class = function () {

    var len = arguments.length;
    var body = arguments[len - 1];    // 最后一个参数是指定本身的方法
    var SuperClass = len > 1 ? arguments[0] : null;     // 第一个参数是指继承的方法,实例和静态分部均继承
    var hasImplementClasses = len > 2;    // 如果有第三个参数,那么第二个就是implementClass,这里其实只继承实例对象
    var Class, SuperClassEmpty;
    
    // 保障结构方法
    if (body.constructor === Object) {
      Class = function() {};
    } else {
      Class = body.constructor;
      // 保障面后不盖覆constructor
      delete body.constructor;
    }
    // 处置superClass分部
    if (SuperClass) {
      // 间中件现实实例性属的继承
      SuperClassEmpty = function() {};
      SuperClassEmpty.prototype = SuperClass.prototype;
      Class.prototype = new SuperClassEmpty();    // 原型继承,消除引用
      Class.prototype.constructor = Class;    // 保障constructor
      Class.Super = SuperClass;    // 父对象拜访接口
      
      // 静态方法继承,重载superClass方法
      extend(Class, SuperClass, false);
    }
    
    // 处置ImplementClass分部,其实只继承实例性属分部,除SuperClass #arguments[0]# 和 body #arguments[length-1]#
    if (hasImplementClasses)
      for (var i = 1; i < len - 1; i++)
        // implement是继承的实例性属分部, 重载父对象implementClass方法
        extend(Class.prototype, arguments[i].prototype, false);    
    
    // 处置本身声明body分部,静态要STATIC指定,实例分部要删除STATIC分部
    extendClass(Class, body);
    
    return Class;

  };

  //============================================================================
  // @method my.extendClass
  // @params Class:function, extension:Object, ?override:boolean=true
  var extendClass = my.extendClass = function (Class, extension, override) {
    // 静态分部继承静态分部
    if (extension.STATIC) {
      extend(Class, extension.STATIC, override);
      // 保障实例分部不继承静态方法
      delete extension.STATIC;
    }
    // 实例性属继继承实例部
    extend(Class.prototype, extension, override);
  };

  //============================================================================
  var extend = function (obj, extension, override) {
    var prop;
    // 其实这里的flase是标明,盖覆父对象的方法
    if (override === false) {
      for (prop in extension)
        if (!(prop in obj))
          obj[prop] = extension[prop];
    } else {
       // 这里其实不盖覆父对象的方法,括包toString
      for (prop in extension)
        obj[prop] = extension[prop];
      if (extension.toString !== Object.prototype.toString)
        obj.toString = extension.toString;
    }
  };

})();
 

     二、KLASS

 目项址地:https://github.com/ded/klass

    先看应用方法:

    a、建新一个类

// 声明一个类
var Person = klass(function (name) {
  this.name = name
})
  .statics({//静态方法
    head: ':)',
    feet: '_|_'
  })
  .methods({//实例方法
    walk: function () {}
  })
b、继承一个类
// SuperHuman 继承 Person
var SuperHuman = Person.extend(function (name) {
  // 动自调用类父的结构方法
})
  .methods({
    walk: function() {
      // 显式声明调用类父的walk方法
      this.supr()
      this.fly()
    },

    fly: function() {}

  })

new SuperHuman('Zelda').walk()
c、字面量式方声明一个类
var Foo = klass({
  foo: 0,
  initialize: function() {
    this.foo = 1
  },
  getFoo: function () {
    return this.foo
  },
  setFoo: function (x) {
    this.foo = x
    return this.getFoo()
  }
})
d、现实一个类的方法

    因为有时候你可能望希覆写或者混合一个实例方法,可以这样:

// 可以传递一个字面量去继承
var Alien = SuperHuman.extend({
  beam: function() {
    this.supr()
    // beam into space
  }
})

var Spazoid = new Alien('Zoopo')

if (beamIsDown) {
  // 覆写beam方法
  Spazoid.implement({
    beam: function() {
      this.supr()
      // fallback to jets
      this.jets()
    }
  })
}
面下看一下klass源代码剖析:

    klass的基本计划路思很确明,极力的模拟其它语言的继承式方。比如:子类结构方法调用类父的结构方法,还可以显式的声明调用类父的方法。

    这类断判都是基于正则匹配:fnTest = /xyz/.test(function () {xyz;}) ? /\bsupr\b/ : /.*/;关键字"super"

    如果示显的声明白要调用类父的方法,那么声明方法的时候,就包装成一个部内调用类父方法且回返同相值的函数,给前当类的方法。

    另一方面,结构方法,也是较比活灵的。如果示显的声明白initialize,那么这就是结构方法。否则如果参数是个function那么它就做为结构方法,否则就用类父的结构方法。

    通过statics式方加添静态方法,通过实例的implements和静态方法methods加添实例方法。

    通过类父的extend现实继承。

    同时,类库为commonJS和览浏环境都供提了持支!

    每日一道理
如果人类不好好保护我们这个赖以生存的地球,终有一天,风沙的肆虐与垃圾的堆积会吞没我们美丽的家园。我向全世界的人们呼吁:让我们从现在开始,从我做起,手挽手,肩并肩共同保护建设我们的家园吧!
/**
  * Klass.js - copyright @dedfat
  * version 1.0
  * https://github.com/ded/klass
  * Follow our software http://twitter.com/dedfat :)
  * MIT License
  */
!function (context, f) {
  // fnTest用来验证是不是可能通过正则找出调用super类父方法的方法
  var fnTest = /xyz/.test(function () {xyz;}) ? /\bsupr\b/ : /.*/,
      noop = function (){},
      proto = 'prototype',
      isFn = function (o) {
        return typeof o === f;
      };
  // 础基类
  function klass(o) {
    return extend.call(typeof o == f ? o : noop, o, 1);
  }

  // 包装成一个借用super同名方法的函数
  function wrap(k, fn, supr) {
    return function () {
      // 缓存原this.super
      var tmp = this.supr;
      // 暂把this.super改成造借用super的同名方法above
      // 供o里显式的声明(fnTest.text(fn)==true)要借用super的同名方法应用
      this.supr = supr[proto][k];
      // 借用行执并保存回返值
      var ret = fn.apply(this, arguments);
      // 恢恢复this.super
      this.supr = tmp;
      // 回返回返值,保障wrap后的回返值跟来原分歧
      return ret;
    };
  }
   // 如果o和super有同名方法,且o显式声明借用super的同名方法,就wrap成一个待行执函数供应用
   // 如果没有显式的声明借用super的同名方法,或者是o有独的方法,或者不是方法就直接用
  function process(what, o, supr) {
    for (var k in o) {
      // 如果非是继承方法,按方法注释规矩行执,终最都放进what
      if (o.hasOwnProperty(k)) {
        what[k] = typeof o[k] == f
          && typeof supr[proto][k] == f
          && fnTest.test(o[k])
          ? wrap(k, o[k], supr) : o[k];
      }
    }
  }
  // 继承方法的现实,fromSub是用来制控是不是继承而来,面下的klass面里fromSub是1,标明非继承而来,结构函数不借用super行执
  function extend(o, fromSub) {
    // noop做为介媒类现实原型继承的消除引用
    noop[proto] = this[proto];
    
    var supr = this,
        prototype = new noop(), // 创立实例对象供原型继承应用,消除引用
        isFunction = typeof o == f,
        _constructor = isFunction ? o : this,// 如果o是一个结构方法就用,否则由this来决议结构函数
        _methods = isFunction ? {} : o,    // 如果o是一个{...}应该用methods放到fn原型里,如果面里有initialize就是结构函数,如果o是函数就由面下_constructor决议o是结构函数
        fn = function () { // 因为kclass借助了kclass,所以终最实际上回返的就是fn,fn其实就新类的结构函数
          
          //1 如果o是{...}就会被methods直接过滤并加添到fn的原型里,如果o面里有initialize,那么fn的原型里就有initialize,那么它就是结构方法
          //2 如果o是function,methods什么也加添不到fn的原型里,但是_constructor会受接o当结构函数
          //3 如果o是{....},同时面里也没有initialize,那么就是this当结构函数,如果在klass里由call决议,明显结构函数是noop,如果在非础基类里,结构函数就是类父的结构函数
          //  由于o不是函数不会动自调用类父的结构函数,只是把类父的结构函数做当前当类的结构函数----这都是由于this的指向决议的
          console.log(this);
          if (this.initialize) {
            this.initialize.apply(this, arguments);
          } else {
            // 调用类父结构方法
            // 如面下3,o不是函数,不会调用类父的结构方法
            // 础基类无类父,不会调用类父结构方法
            fromSub || isFn(o) && supr.apply(this, arguments);
            // 调用本类结构方法
            // 参考面下2,3要么是noop要么是o
            console.log(_constructor==noop);
            _constructor.apply(this, arguments);
          }
        };
    // 结构原型方法的接口
    fn.methods = function (o) {
      process(prototype, o, supr);
      fn[proto] = prototype;
      return this;
    };
    // 行执现实新类原型,保障新类的constructor
    fn.methods.call(fn, _methods).prototype.constructor = fn;
    // 保障新类可以被继承
    fn.extend = arguments.callee;
    // 加添实例方法或者静态方法,statics:静态方法,implement实例方法
    fn[proto].implement = fn.statics = function (o, optFn) {
      // 保障o是一个object对象,如果o是一个字符串,那么就是添一个方法的情况,如果o是一个object对象说明是批量加添的
      // 因为要从o面里贝拷
      o = typeof o == 'string' ? (function () {
        var obj = {};
        obj[o] = optFn;
        return obj;
      }()) : o;
      // 加添实例方法或者静态方法,statics:静态方法,implement实例方法
      process(this, o, supr);
      return this;
    };

    return fn;
  }

  // 后台用,nodejs
  if (typeof module !== 'undefined' && module.exports) {
    module.exports = klass;
  } else {
    
    var old = context.klass;
    // 防突冲
    klass.noConflict = function () {
      context.klass = old;
      return this;
    };
    // 前台览浏器用
    //window.kclass = kclass;
    context.klass = klass;
  }

}(this, 'function');
 三、还有一种简略现实

    现实路思很简略,就是利用ECMAScript 5 原型式继承Object.create方法,封装成一个方法,如果不持支ECMAScript5的环境,就平移退化到

function F() {};  
F.prototype = superCtor.prototype;  
ctor.prototype = new F();  
ctor.prototype.constructor = ctor;
一样的,除最后一个参数是前当类的方法声明,其它参数均做为继承类父,须要循环继承,但当这里处置的绝对较比简略,没涉及到盖覆。你可以自己着手加添。
var Class = (function() {  
      
    /**  
     * Inherits function.(node.js)  
     *   
     * @param ctor subclass's constructor.  
     * @param superctor superclass's constructor.  
     */  
    var inherits = function(ctor, superCtor) { 
        // 显式的指定类父
        ctor.super_ = superCtor;  
          
        // ECMAScript 5  原型式继承并消除引用
        if (Object.create) {  
            ctor.prototype = Object.create(superCtor.prototype, {  
                constructor: {  
                    value: ctor,  
                    enumerable: false,  
                    writable: true,  
                    configurable: true  
                }  
            });  
        } else {  
            // 无Object.create方法的稳平退化
            function F() {};  
            F.prototype = superCtor.prototype;  
            ctor.prototype = new F();  
            ctor.prototype.constructor = ctor;  
        }  
    };  
      
    /**  
     * Class function.  
     */  
    return function() {  
        // 最后一个参数是新类方法、性属和结构函数声明
        var subClazz = arguments[arguments.length - 1] || function() {};  
        // initialize是结构函数,否结构函数就是一个空函数
        var fn = subClazz.initialize == null ? function() {} : subClazz.initialize;  
        // 继承除最一个参数以的类,多继承,也可以用作扩展方法 
        for (var index = 0; index < arguments.length - 1; index++) {  
            inherits(fn, arguments[index]);  
        }  
        // 现实新类的方法
        for (var prop in subClazz) {  
              
            if (prop == "initialize") {  
                continue;  
            }  
              
            fn.prototype[prop] = subClazz[prop];  
        }  
        
        return fn;  
    }  
      
})(); 
看面下实例:
/**  
 * The definition of Cat Class.  
 */  
var Cat = Class({  
      
    /**  
     * Constructor.  
     *   
     * @param name Cat's name  
     */  
    initialize: function(name) {
        this.name = name;  
    },  
      
    /**  
     * Eat function.  
     */  
    eat: function() {  
        alert(this.name + " is eating fish.");  
    }  
});  
  
/**  
 * The definition of Black Cat Class.  
 */  
var BlackCat = Class(Cat, {  
      
    /**  
     * Constructor.  
     *   
     * @param name Cat's name.  
     * @param age Cat's age.  
     */  
    initialize: function(name, age) {  
        // call the constructor of super class.  
        BlackCat.super_.call(this, name);  
        this.age = age;  
          
    },  
      
    /**  
     * Eat function.  
     */  
    eat: function() {  
        alert(this.name + "(" + this.age + ") is eating dog.");  
    }  
});  
  
/**  
 * The definition of Black Fat Cat Class.  
 */  
var BlackFatCat = Class(BlackCat, {  
      
    /**  
     * Constructor.  
     *   
     * @param name Cat's name.  
     * @param age Cat's age.  
     * @param weight Cat's weight.  
     */  
    initialize: function(name, age, weight) {  
        // call the constructor of super class.  
        BlackFatCat.super_.call(this, name, age);  
        this.weight = weight;  
    },  
      
    /**  
     * Eat function.  
     */  
    eat: function() {  
        alert(this.name + "(" + this.age + ") is eating dog. My weight: " + this.weight);  
    }  
});  
  
  
/**  
 * The definition of Dog Class.  
 */  
var Dog = Class({});  
  
var cat = new BlackFatCat("John", 24, "100kg");  
cat.eat();  
  
// true  
alert(cat instanceof Cat);  
  
// true  
alert(cat instanceof BlackCat);  
  
// true  
alert(cat instanceof BlackFatCat);  
  
// true  
alert(cat.constructor === BlackFatCat);  
  
// false  
alert(cat instanceof Dog);  
 四、mootools类库的Class

    码源剖析可以看这里:http://www.cnblogs.com/hmking/archive/2011/09/30/2196504.html

    看体具用法:

    a、建新一个类

var Cat = new Class({
    initialize: function(name){
        this.name = name;
    }
});
var myCat = new Cat('Micia');
alert(myCat.name); // alerts 'Micia'
 
var Cow = new Class({
    initialize: function(){
        alert('moooo');
    }
});
b、继承的现实
var Animal = new Class({
    initialize: function(age){
        this.age = age;
    }
});
var Cat = new Class({
    Extends: Animal,
    initialize: function(name, age){
        this.parent(age); // calls initalize method of Animal class
        this.name = name;
    }
});
var myCat = new Cat('Micia', 20);
alert(myCat.name); // alerts 'Micia'.
alert(myCat.age); // alerts 20.
c、扩充类的现实
var Animal = new Class({
    initialize: function(age){
        this.age = age;
    }
});
var Cat = new Class({
    Implements: Animal,
    setName: function(name){
        this.name = name
    }
});
var myAnimal = new Cat(20);
myAnimal.setName('Micia');
alert(myAnimal.name); // alerts 'Micia'.
五、悟透javascript:法语甘露

    先看用法实例

    a、创立类

// 创立类Person
var Person = Class(object, {
    Create : function(name, age) {
        this.name = name;
        this.age = age;
    },
    SayHello : function() {
        alert("Hello, I'm " + this.name + ", " + this.age + " years old.");
    }
});

var BillGates = New(Person, ["Bill Gates", 53]);
BillGates.SayHello();
b、继承类
// Employee继承Person
var Employee = Class(Person, {
    Create : function(name, age, salary) {
        Person.Create.call(this, name, age);
        //调用基类的结构函数
        this.salary = salary;
    },
    ShowMeTheMoney : function() {
        alert(this.name + " $" + this.salary);
    }
});

var SteveJobs = New(Employee, ["Steve Jobs", 53, 1234]);
SteveJobs.SayHello();
SteveJobs.ShowMeTheMoney();
面下是码源析分:明显,多了一个New方法,创立类和建新类的实例都被妙巧的封装了。形成了一个有意义的团体!还有一点不同的地方,有所的类都基于字面量,而不是基于函数。代码很短简,但其中道理却很丰硕也很妙巧,可以细细品味一番!
//创立类的函数,用于声明类及继承关系
function Class(aBaseClass, aClassDefine) {
    //创立类的临时函数壳
    function class_() {
        this.Type = aBaseClass;
        //我们给每个类约定一个Type性属,引用其继承的类
        for (var member in aClassDefine)
        this[member] = aClassDefine[member];
        //制复类的全体义定到前当创立的类
    };
    class_.prototype = aBaseClass;
    return new class_();
};

//创立对象的函数,用于意任类的对象创立
function New(aClass, aParams) {
    //创立对象的临时函数壳
    function new_() {
        this.Type = aClass;
        //我们也给每个对象约定一个Type性属,据此可以拜访到对象所属的类
        if (aClass.Create)
            aClass.Create.apply(this, aParams);
        //我们约定有所类的结构函数都叫Create,这和DELPHI较比相似
    };
    new_.prototype = aClass;
    return new new_();
};
 

    由于写的较比统笼,可能有很多地方没有剖析到,也可能有不确准的地方,还望正指。

 看完面下几种剖析,相信息自己也可以写出一个自己的封装类库出来,至于,怎么现实看个人喜好了。但基本的思都是一样的基于原型的继承式方和循环贝拷新方法。

 

文章结束给大家分享下程序员的一些笑话语录: 程序语言综述
CLIPPER 程序员不去真的猎捕大象,他们只是购买大象部分的库然后花几年的时间试图综合它们。
DBASE 程序员只在夜间猎捕大象,因为那时没人会注意到他们还在使用石弓。
FOXPRO 程序员开始使用更新更好的步枪,这使他们花掉比实际狩猎更多的时间学习新的射击技术。
C 程序员拒绝直接购买步枪,宁可带着钢管和一个移动式机器车间到非洲,意欲从零开始造一枝完美的步枪。
PARADOX 程序员去非洲时带着好莱坞关于猎捕大象的电影剧本,他们认为照剧本行事就会逮到一头大象。
ACCESS 程序员在没有任何猎象经验的经验下就出发了,他们穿着华丽的猎装、带着全部装备,用漂亮的望远镜找到了大象,然后发觉忘了带扳机。
RBASE 程序员比大象还要稀少,事实上,如果一头大象看到了一个RBASE程序员,对他是个幸运日。
VISUAL ACCESS 程序员装上子弹、举起步枪、瞄准大象,这使大象感到可笑,究竟谁逃跑。他们无法抓住大象,因为由于他们对多重控制的偏爱,他们的吉普车有太多的方向盘因而无法驾驶。
ADA、APL和FORTRAN 程序员与圣诞老人和仙女一样是虚构的。
COBOL 程序员对和自己一样濒临灭绝的大象寄予了深切的同情。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值