利用不到200行代码构建原生JS类库:从设计到实现
本文详解如何使用原生JavaScript构建一个轻量级类库。通过面向对象设计,涵盖闭包、原型链、事件监听及CSS/HTML操作等核心API的实现。文章提供完整源码解析,帮助开发者深入理解JS类库底层逻辑,无需依赖第三方库即可实现链式调用与跨浏览器兼容。
前言与目标
前言
JavaScript 语言本身具备强大的面向对象特性,并提供了灵活的对象编程能力。本文将采用面向对象的设计思路,指导读者使用原生 JavaScript 编写一个具备类似 jQuery 功能的类库。通过本教程,你将掌握以下关键技术点:
- 闭包的应用:用于减少全局变量污染,并优化变量查找范围。
- 自执行函数在对象构建中的具体用法。
- extend 方法的底层实现原理。
- 跨浏览器事件监听器的统一实现方案。
- 原型链机制与继承模式的具体应用。
下文将详细讲解类库核心 API 的设计与使用示例,并在文末附上完整源码。此前在《3分钟教你用原生js实现具有进度监听的文件上传预览组件》一文中也曾采用类似技术路径,欢迎感兴趣者参考交流。
如需获取更完整的类库代码,请访问 GitHub 仓库《Xuery——仿jquery API风格的轻量级可扩展的原生js框架》(支持本地下载)。
类库设计思路
类库设计思路

API 实现与示例
API 介绍与效果展示
1、事件绑定 Xuery.on(eventName, fn) 示例如下:
Xuery('#demo').on('click', function(e){
alert('hello world!')
})
2、CSS 属性的读取与设置 Xuery.css(string|object, ?[string]) 示例如下:
// 访问css
Xuery('#demo').css('width')
// 设置css
Xuery('#demo').css('width', '1024px')
// 设置css
Xuery('#demo').css({
width: '1024px',
height: '1024px'
})
3、HTML 属性的读取与设置 Xuery.attr(string|object, ?[string]) 示例如下:
// 访问attr
Xuery('#demo').attr('title')
// 设置attr
Xuery('#demo').attr('title', '1024px')
// 设置attrs
Xuery('#demo').attr({
title: '1024px',
name: '1024px'
})
4、HTML 内容的读取与设置 示例如下:
// 访问
Xuery('#demo').html()
// 设置
Xuery('#demo').html('前端学习原生框架')
其他常用 API 未在此一一列举,读者可前往 GitHub 仓库查阅,或基于此基础框架扩展开发属于自己的 JavaScript 框架。
核心源码解析
核心源码
以下源码已添加详细注释,建议仔细阅读。其中涉及原型链与构造函数指向的处理是实现上述调用方式的关键,如有疑惑可在评论区讨论。
/**
* 链模式实现自己的js类库
*/
(function(win, doc){
var Xuery = function(selector, context) {
return new Xuery.fn.init(selector, context)
};
Xuery.fn = Xuery.prototype = {
constructor: Xuery,
init: function(selector, context) {
// 设置元素长度
this.length = 0;
// 默认获取元素的上下文document
context = context || document;
// id选择符,则按位非将-1转化为0
if(~selector.indexOf('#')) {
this[0] = document.getElementById(selector.slice(1));
this.length = 1;
}else{
// 在上下文中选择元素
var doms = context.getElementsByTagName(selector),
i = 0,
len = doms.length;
for(; i<len; i++){
this[i] = doms[i];
}
}
this.context = context;
this.selector = selector;
return this
},
// 增强数组
push: [].push,
sort: [].sort,
splice: [].splice
};
// 方法扩展
Xuery.extend = Xuery.fn.extend = function(){
// 扩展对象从第二个参数算起
var i = 1,
len = arguments.length,
target = arguments[0],
j;
if(i === len){
target = this;
i--;
}
// 将参数对象合并到target
for(; i<len; i++){
for(j in arguments[i]){
target[j] = arguments[i][j];
}
}
return target
}
// 扩展事件方法
Xuery.fn.extend({
on: (function(){
if(document.addEventListener){
return function(type, fn){
var i = this.length -1;
for(; i>=0;i--){
this[i].addEventListener(type, fn, false)
}
return this
}
// ie浏览器dom2级事件
}else if(document.attachEvent){
return function(type, fn){
var i = this.length -1;
for(; i>=0;i--){
this[i].addEvent('on'+type, fn)
}
return this
}
// 不支持dom2的浏览器
}else{
return function(type, fn){
var i = this.length -1;
for(; i>=0;i--){
this[i]['on'+type] = fn;
}
return this
}
}
})()
})
// 将‘-'分割线转换为驼峰式
Xuery.extend({
camelCase: function(str){
return str.replace(/\-(\w)/g, function(all, letter){
return letter.toUpperCase();
})
}
})
// 设置css
Xuery.extend({
css: function(){
var arg = arguments,
len = arg.length;
if(this.length < 1){
return this
}
if(len === 1) {
if(typeof arg[0] === 'string') {
if(this[0].currentStyle){
return this[0].currentStyle[arg[0]];
}else{
return getComputedStyle(this[0], false)[arg[0]]
}
}else if(typeof arg[0] === 'object'){
for(var i in arg[0]){
for(var j=this.length -1; j>=0; j--){
this[j].style[Xuery.camelCase(i)] = arg[0][i];
}
}
}
}else if(len === 2){
for(var j=this.length -1; j>=0; j--){
this[j].style[Xuery.camelCase(arg[0])] = arg[1];
}
}
return this
}
})
// 设置属性
Xuery.extend({
attr: function(){
var arg = arguments,
len = arg.length;
if(len <1){
return this
}
if(len === 1){
if(typeof arg[0] === 'string'){
return this[0].getAttribute(arg[0])
}else if(typeof arg[0] === 'object'){
for(var i in arg[0]){
for(var j=this.length -1; j>= 0; j--){
this[j].setAttribute(i, arg[0][i])
}
}
}
}
else if(len === 2){
for(var j=this.length -1; j>=0; j--){
this[j].setAttribute(arg[0], arg[1]);
}
}
return this
}
})
// 获取或者设置元素内容
Xuery.fn.extend({
html: function(){
var arg = arguments,
len = arg.length;
if(len === 0){
return this[0] && this[0].innerHTML
}else{
for(var i=this.length -1; i>=0; i--){
this[i].innerHTML = arg[0];
}
}
return this
}
})
Xuery.fn.init.prototype = Xuery.fn;
window.Xuery = Xuery;
})(window, document);
总结
总结
以上就是本文的全部内容,希望这些内容能对大家的 JavaScript 学习或工作有所帮助。感谢对本站的支持。
D3.js 比例尺与坐标轴的配置详解
2026-11-05 16:54:46
JavaScript怎么遍历对象的Symbol键和不可枚举属性
2026-11-05 13:13:39
封装一个健壮的JS字符串判空工具函数
2026-11-05 12:16:14
JavaScript合并数组的三种常用方法:concat、扩展运算符与push
2026-11-05 11:22:29
JavaScript立即执行函数(IIFE)是什么及基本语法
2026-11-05 10:51:19
