上一篇文章大概的介绍了一下关于javascript组件的开发方式,这篇文章主要详细记一下基类的编写,这个基类主要是实现继承的功能

为什么要封装基类?

由于这次重构项目需要对各种组件进行封装,并且这些组件的实现方式都差不多,为了便于管理,让代码尽量统一,所以到对组件封装一个base基类(javascript没有类的概念,暂且这样叫吧),关于javascript的oo实现:可以参考这篇文章javascript oo实现;写得很赞,膜拜,我改写的这个基于John Resig的实现方式。

基类的封装方式以及继承有哪些方法?

这个在各大库的源码中都有对应的实现,比如jq里面$.extend()实现浅拷贝和深拷贝,prototype.js里面的Object.extend的实现,以及Ext.extend等,他们的实现各不相同,在这里简单的看看prototype.js的extend实现方案:

 //Prptotype库的extend,为Object类添加静态方法,
Object.extend=function(destination,source) {
for(property in source) {
destination[property]=source[property];
}
return destination;
}
//通过Object类,为每个对象,添加方法
Object.prototype.extend = function(object) {
return Object.extend.apply(this, [this, object]);
}

第一个函数Object.extend,可以简单的理解为对象复制.目标对象将拥有源对象的所有属性和方法

第二个函数Object.prototype.extend,在prototype对象实例化对象中加上一个extend函数,

其中精华语句Object.extend.apply(this, [this, object]); 将Object对象中的extend作为静态方法调用,

第一个参数this,指向调用对象本身,第二个参数,为一个数组,为调用对象本身和传递进来的对象参数object(一般为方法,下面有例子)

例子:

 //定义父类father
function father(){}
father.prototype={
//各种方法
}
//定义子类son
function son(){}
son.prototype=(new father()).extend({
//新添加方法
....
})

上面的方法有一些不好的地方,比如污染Object对象的属性,比如不可以(new father()).extend({},{});不可以扩展多个对象

下面看jq的实现方式:

 jQuery.extend = jQuery.fn.extend = function() {
var options, name, src, copy, copyIsArray, clone,
target = arguments[0] || {},
i = 1,
length = arguments.length,
deep = false; // Handle a deep copy situation处理深拷贝的情况
if ( typeof target === "boolean" ) {
deep = target;
target = arguments[1] || {};
// skip the boolean and the target跳过深拷贝boolean参数和目标参数(源对象)
i = 2;
} // Handle case when target is a string or something (possible in deep copy)处理目标参数(源对象)不是Object或者function的情况
if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
target = {};
} // extend jQuery itself if only one argument is passed如果只有目标参数(源对象)和boolean参数的处理
if ( length === i ) {
target = this;
--i;
} for ( ; i < length; i++ ) {
// Only deal with non-null/undefined values处理不为空和不为undefined的值
if ( (options = arguments[ i ]) != null ) {
// Extend the base object开始向源对象扩展
for ( name in options ) {
src = target[ name ];
copy = options[ name ]; // Prevent never-ending loop如果源对象中map到拷贝对象key的value则跳出此次循环
if ( target === copy ) {
continue;
} // Recurse if we're merging plain objects or arrays 处理copy是对象和数组的情况并且是深拷贝
if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
if ( copyIsArray ) {
copyIsArray = false;
clone = src && jQuery.isArray(src) ? src : []; } else {
clone = src && jQuery.isPlainObject(src) ? src : {};
} // Never move original objects, clone them
target[ name ] = jQuery.extend( deep, clone, copy ); // Don't bring in undefined values处理value不是对象和数组的情况
} else if ( copy !== undefined ) {
target[ name ] = copy;
}
}
}
} // Return the modified object
return target;
};

jq的实现就避免了prototype实现的缺点,但是在我们的组件开发中不可能依赖于jq,所以下面根据John Resig的实现方式有了下面的Class超级父类,具体看注释

 //javascript简单的实现类和继承
var Class = (function() {
//模拟extend继承方式
var _extend = function() {
//属性混入函数,不混入原型上的属性,原型上的属性就多了哈
var _mixProto = function(base, extend) {
for (var key in extend) {
if (extend.hasOwnProperty(key)) {
base[key] = extend[key];
}
}
};
//这里是一个开关,目的是为了在我们继承的时候不调用父类的init方法渲染,而把渲染放在子类
//试想没这个开关,如果在继承的时候父类有init函数就会直接渲染,而我们要的效果是继承后的子类做渲染工作
this.initializing = true;
//原型赋值
var prototype = new this();
//开关打开
this.initializing = false;
//for循环是为了实现多个继承,例如Base.extend(events,addLog)
for (var i = 0,len = arguments.length; i < len; i++) {
//把需要继承的属性混入到父类原型
_mixProto(prototype, arguments[i].prototype||arguments[i]);
} /*也可以这样去循环
var items = [].slice.call(arguments) || [];
var item;
//支持混入多个属性,并且支持{}也支持 Function
while (item = items.shift()) {
_mixProto(prototype, item.prototype || item);
}
*/ //继承后返回的子类
function SonClass() {
//检测开关和init函数的状态,看是否做渲染动作
if (!SonClass.initializing && this.init)
//这里相当于是一个虚函数,--在传统面向对象语言中,抽象类中的虚方法必须先被声明,但可以在其他方法中被调用。而在JavaScript中,虚方法 就可以看作该类中没有定义的方法,但已经通过this指针使用了。和传统面向对象不同的是,这里虚方法不需经过声明,而直接使用了。这些方法将在派生类(子类)中 实现调用返回的子类的init方法渲染,把传给组件的配置参数传给init方法,这里的apply主要是为了传参而非改变this指针
this.init.apply(this, arguments);
}
//把混入之后的属性和方法赋值给子类完成继承
SonClass.prototype = prototype;
//改变constructor引用,不认子类的构造函数将永远是Class超级父类
SonClass.prototype.constructor = SonClass;
//给子类页也添加继承方法,子类也可以继续继承
SonClass.extend = arguments.callee;//也可以是_extend
//返回子类
return SonClass
};
//超级父类
var Class = function() {}; Class.extend = _extend;
//返回超级父类
return Class })();

附John Resig类的实现:

 /* Simple JavaScript Inheritance
* By John Resig http://ejohn.org/
* MIT Licensed.
*/
// Inspired by base2 and Prototype
(function(){
//initializing是为了解决我们之前说的继承导致原型有多余参数的问题。当我们直接将父类的实例赋值给子类原型时。是会调用一次父类的构造函数的。所以这边会把真正的构造流程放到init函数里面,通过initializing来表示当前是不是处于构造原型阶段,为true的话就不会调用init。
//fnTest用来匹配代码里面有没有使用super关键字。对于一些浏览器`function(){xyz;}`会生成个字符串,并且会把里面的代码弄出来,有的浏览器就不会。`/xyz/.test(function(){xyz;})`为true代表浏览器支持看到函数的内部代码,所以用`/\b_super\b/`来匹配。如果不行,就不管三七二十一。所有的函数都算有super关键字,于是就是个必定匹配的正则。
var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; // The base Class implementation (does nothing)
// 超级父类
this.Class = function(){}; // Create a new Class that inherits from this class
// 生成一个类,这个类会具有extend方法用于继续继承下去
Class.extend = function(prop) {
//保留当前类,一般是父类的原型
//this指向父类。初次时指向Class超级父类
var _super = this.prototype; // Instantiate a base class (but only create the instance,
// don't run the init constructor)
//开关 用来使原型赋值时不调用真正的构成流程
initializing = true;
var prototype = new this();
initializing = false; // Copy the properties over onto the new prototype
for (var name in prop) {
// Check if we're overwriting an existing function
//这边其实就是很简单的将prop的属性混入到子类的原型上。如果是函数我们就要做一些特殊处理
prototype[name] = typeof prop[name] == "function" &&
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
(function(name, fn){
//通过闭包,返回一个新的操作函数.在外面包一层,这样我们可以做些额外的处理
return function() {
var tmp = this._super; // Add a new ._super() method that is the same method
// but on the super-class
// 调用一个函数时,会给this注入一个_super方法用来调用父类的同名方法
this._super = _super[name]; // The method only need to be bound temporarily, so we
// remove it when we're done executing
//因为上面的赋值,是的这边的fn里面可以通过_super调用到父类同名方法
var ret = fn.apply(this, arguments);
//离开时 保存现场环境,恢复值。
this._super = tmp; return ret;
};
})(name, prop[name]) :
prop[name];
} // 这边是返回的类,其实就是我们返回的子类
function Class() {
// All construction is actually done in the init method
if ( !initializing && this.init )
this.init.apply(this, arguments);
} // 赋值原型链,完成继承
Class.prototype = prototype; // 改变constructor引用
Class.prototype.constructor = Class; // 为子类也添加extend方法
Class.extend = arguments.callee; return Class;
};
})();

javascript组件开发之基类继承实现的更多相关文章

  1. javascript组件开发

    最近忙于重构项目,今天周末把在重构中的一些思想记记: 一.javascript的组件开发:基类的封装 由于这次重构项目需要对各种组件进行封装,并且这些组件的实现方式都差不多,所以想到对组件封装一个ba ...

  2. PythonI/O进阶学习笔记_4.自定义序列类(序列基类继承关系/可切片对象/推导式)

    前言: 本文代码基于python3 Content: 1.python中的序列类分类 2. python序列中abc基类继承关系 3. 由list的extend等方法来看序列类的一些特定方法 4. l ...

  3. C#虚基类继承与接口的区别

    类:定义新的数据类型以及这些新的数据类型进行相互操作的方法 定义方式: class Cat { } class Cat:object { } C#中所有的类都是默认由object类派生来的,显示指定或 ...

  4. c#基类继承

    [ 塔 · 第 三 条 约 定 ] 编写一个多边形作为基类(成员:定点数)抽象方法(子类实现):体积.边长 正三角形类:成员 边长 长方形类:成员 长宽 using System; using Sys ...

  5. odoo开发笔记 -- 模型(类)继承的几种机制

    1. 类继承 2. 原型继承 3. 委托继承 待完善 https://www.cnblogs.com/chenshuquan/p/10523626.html

  6. C++学习之路—继承与派生(一):基本概念与基类成员的访问属性

    (本文根据<c++程序设计>(谭浩强)总结而成,整理者:华科小涛@http://www.cnblogs.com/hust-ghtao,转载请注明) 1   基本思想与概念 在传统的程序设计 ...

  7. 基于SqlSugar的开发框架循序渐进介绍(10)-- 利用axios组件的封装,实现对后端API数据的访问和基类的统一封装处理

    在SqlSugar的开发框架的后端,我们基于Web API的封装了统一的返回结果,使得WebAPI的接口返回值更加简洁,而在前端,我们也需要统一对返回的结果进行解析,并获取和Web API接口对应的数 ...

  8. 开发一个完整的JavaScript组件

    作为一名开发者,大家应该都知道在浏览器中存在一些内置的控件:Alert,Confirm等,但是这些控件通常根据浏览器产商的不同而形态各异,视觉效果往往达不到UI设计师的要求.更重要的是,这类内置控件的 ...

  9. ASP.NET MVC项目实现BasePage基类用作ASPX.CS网页继承

    在ASP.NET MVC项目开发,还是需要创建一些Web Page来实现一些功能,如呈现报表等... 但是一旦项目的.ASPX网页太多了,其中的程序代码也会有代码冗余,出现这些情况,我们得需要对这些代 ...

随机推荐

  1. [ASP.NET]更简单的方法:FormsAuthentication登录ReturnUrl使用绝对路径

    转自:http://www.cnblogs.com/dudu/p/formsauthentication-returnurl-absoluteuri.html [ASP.NET]更简单的方法:Form ...

  2. 函数 MultiByteToWideChar() 详解

    函数原型: int MultiByteToWideChar( UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cchMultiByte ...

  3. (C++)String的用法

    (转自http://www.cnblogs.com/yxnchinahlj/archive/2011/02/12/1952550.html) 之所以抛弃char*的字符串而选用C++标准程序库中的st ...

  4. Remove a Driver Package from the Driver Store

    http://technet.microsoft.com/en-us/library/cc730875.aspx Determine the name of the driver package in ...

  5. Android 深入ViewPager补间动画,实现类京东商城首页广告Banner切换效果

    如有转载,请声明出处: 时之沙: http://blog.csdn.net/t12x3456 某天看到京东商城首页的滑动广告的Banner,在流动切换的时候有立体的动画效果,感觉很有意思,然后研究了下 ...

  6. Android C2DM学习 - 云端推送

    一.基础知识 当我们开发需要和服务器交互的应用程序时,基本上都需要获取服务器端的数据,比如<地震及时通>就需要及时获取服务器上最新的地震信息.要获取服务器上不定时更新的信息一般来说有两种方 ...

  7. 【JavaScript】Registering JavaScript object methods as callbacks

    The registration of callback functions is very common in JavaScript web programming, for example to ...

  8. 提高HTML5 canvas性能的几种方法

    简介 HTML5 canvas 最初起源于苹果(Apple)的一项实验,现在已经成为了web中受到广泛支持的2D快速模式绘图(2Dimmediate mode graphic)的标准.许多开发者现在利 ...

  9. Ogre Addon之Paged Geometry

    还是OGRE好啊,无尽的Addon,无尽的宝藏.既有SkyX,Hydrx这样的天空水体渲染库可供学习,还有Paged Geometry这样的“大规模海量geometry管理系统”.它通过batch,s ...

  10. ext2磁盘布局

    概述           本篇博客主要关注ext2文件系统的磁盘布局,即ext2会在格式化时将磁盘划分成什么样子.   ext2磁盘布局   任何Ext2分区中的第一个块从不受Ext2文件系统的管理, ...