javascript组件开发之基类继承实现
上一篇文章大概的介绍了一下关于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组件开发之基类继承实现的更多相关文章
- javascript组件开发
最近忙于重构项目,今天周末把在重构中的一些思想记记: 一.javascript的组件开发:基类的封装 由于这次重构项目需要对各种组件进行封装,并且这些组件的实现方式都差不多,所以想到对组件封装一个ba ...
- PythonI/O进阶学习笔记_4.自定义序列类(序列基类继承关系/可切片对象/推导式)
前言: 本文代码基于python3 Content: 1.python中的序列类分类 2. python序列中abc基类继承关系 3. 由list的extend等方法来看序列类的一些特定方法 4. l ...
- C#虚基类继承与接口的区别
类:定义新的数据类型以及这些新的数据类型进行相互操作的方法 定义方式: class Cat { } class Cat:object { } C#中所有的类都是默认由object类派生来的,显示指定或 ...
- c#基类继承
[ 塔 · 第 三 条 约 定 ] 编写一个多边形作为基类(成员:定点数)抽象方法(子类实现):体积.边长 正三角形类:成员 边长 长方形类:成员 长宽 using System; using Sys ...
- odoo开发笔记 -- 模型(类)继承的几种机制
1. 类继承 2. 原型继承 3. 委托继承 待完善 https://www.cnblogs.com/chenshuquan/p/10523626.html
- C++学习之路—继承与派生(一):基本概念与基类成员的访问属性
(本文根据<c++程序设计>(谭浩强)总结而成,整理者:华科小涛@http://www.cnblogs.com/hust-ghtao,转载请注明) 1 基本思想与概念 在传统的程序设计 ...
- 基于SqlSugar的开发框架循序渐进介绍(10)-- 利用axios组件的封装,实现对后端API数据的访问和基类的统一封装处理
在SqlSugar的开发框架的后端,我们基于Web API的封装了统一的返回结果,使得WebAPI的接口返回值更加简洁,而在前端,我们也需要统一对返回的结果进行解析,并获取和Web API接口对应的数 ...
- 开发一个完整的JavaScript组件
作为一名开发者,大家应该都知道在浏览器中存在一些内置的控件:Alert,Confirm等,但是这些控件通常根据浏览器产商的不同而形态各异,视觉效果往往达不到UI设计师的要求.更重要的是,这类内置控件的 ...
- ASP.NET MVC项目实现BasePage基类用作ASPX.CS网页继承
在ASP.NET MVC项目开发,还是需要创建一些Web Page来实现一些功能,如呈现报表等... 但是一旦项目的.ASPX网页太多了,其中的程序代码也会有代码冗余,出现这些情况,我们得需要对这些代 ...
随机推荐
- 观察者模式:关于通知的使用(NSNotificationCenter)
一.通知的使用方法 1.发出通知 例如:[[NSNotificationCenter defaultCenter]postNotificationName:@"backToFirstPage ...
- jqeuery $.ajax 与后台jsone交互
============================action后台,我这里是SpringMVC================================================@C ...
- [Microsoft][ODBC SQL Server Driver][DBNETLIB]SQL Server 不存在或访问被拒绝
一般连接sql数据库,IP_connstr="driver={SQL Server}; server=127.0.0.1;database=数据库名字;uid=sa;pwd=密码" ...
- iPhone中国移动收不到彩信,联通不用设置都可以,具体设置方法:
打开“设置”. 打开“通用”. 打开“蜂窝移动网络”. 打开“蜂窝数据移动网络”. 在“蜂窝移动数据”一栏中的“APN”处填入“cmnet”. 在“彩信”一栏中的“APN”处填入“cmnet”,“MM ...
- Java常见排序算法之堆排序
在学习算法的过程中,我们难免会接触很多和排序相关的算法.总而言之,对于任何编程人员来说,基本的排序算法是必须要掌握的. 从今天开始,我们将要进行基本的排序算法的讲解.Are you ready?Let ...
- 【转】深入浅出REST
转自:http://www.infoq.com/cn/articles/rest-introduction 不知你是否意识到,围绕着什么才是实现异构的应用到应用通信的“正确”方式,一场争论正进行的如火 ...
- 将 Android 应用移植到 BlackBerry PlayBook 上
美国西部时间18号早上,也就是我们的19号凌晨,BlackBerry DevCon活动隆重举行,PlayBook 2.0开发测试版随之发布.PlayBook 2.0的一个重要功能就是支持Android ...
- android标题栏(titlebar)显示进度条
在后台线程中执行各种操作(网络连接.大数据存储)的时候,我们希望让客户能看到后台有操作在进行,那么既能有效的提示用户,又不占用当前操作空间,最好的方法就是在标题栏有个进度条. [代码] [Java]代 ...
- C# Redis分布式缓存
C# Redis实战(七) 七.修改数据 在上一篇 C# Redis实战(六)中介绍了如何查询Redis中数据,本篇将介绍如何修改Redis中相关数据.大家都知道Redis是key-value型存储系 ...
- stm32 IAP + APP ==>双剑合一
(扩展-IAP主要用于产品出厂后应用程序的更新作用,上一篇博文详细的对IAP 升级程序做了详细的分析http://blog.csdn.net/yx_l128125/article/details/12 ...