如何开发原生的 JavaScript 插件(知识点+写法)

一、前言

通过 "WWW" 原则我们来了解 JavaScript 插件这个东西

第一个 W "What" -- 是什么?什么是插件,我就不照搬书本上的抽象概念了,我个人简单理解就是,能方便实现某个功能的扩展工具.(下面我会通过简单的例子来帮助读者理解)

第二个 W "Why" -- 为什么? 为什么要有插件这种东西,首先结合第一个 W 来理解就是,使用插件的目的是方便我们实现某一个功能. 也就是说在编程过程中我们只需要找轮子,或者改轮子而不需要重新造轮子.节省开发时间,并且各司其职会更加专业(做得更好)。其次就是方便维护,因为每个功能模块可以分得更清楚,所谓的松耦合。

第三个 W "How" -- 如何做?我们如何开发 JavaScript 插件?这是我们这片文章要谈论的重点.

二、准备知识

在讨论如何做之前我们不妨先通过反向思维来看看插件的特点。我们从如何使用 Javascript 插件开始。

假设我们现在要使用插件 js-plugin.js

第一步:引入插件,注意依赖项,例如有些插件是基于 jquery 编写的,先引入 jquery

第二步:通过插件提供的 API 实现我们所要的业务

以经典的 jquery 使用方法为例

<script src="//cdn.bootcss.com/jquery/3.1.0/jquery.min.js"></script>
<script>
    $(function(){
        $("#Id").html('hello world!');
    })
</script>

顺便说一句,能使用CDN的尽量使用CDN,这将使你的资源加载得更快.并节省你主机的带宽开销
传送门: BootCDN

上述两点其实也就是说我们的插件要做到,引入相关文件就可以方便地进行使用。换句话说插件必须满足下面的特点:

首先,我觉得插件最重要的一点 -- 复用性。就是说你这个插件在这个项目中是能用的,搬到另一个项目中它也是能用的(废话),并且原则上依赖项越少越好

其次,我觉得这是插件的宗旨 -- 易用性。开发一个插件,如果使用繁琐,倒不如重新造轮子,那就失去了插件的意义。

除此之外,当然还有高效性,考虑执行的效率还有内存的优化。

三、Module 模式

插件开发不得不提的是 Modlule 模式,Module -- 模块,模块化开发,是在编程中十分通用的模式。说白了就是把业务需求分模块。每一个模块负责一个功能的实现。有点像其他面向对象编程语言中的类。例如 JsonHelper 专门负责 json 解析,FilesUpload,专门用来做文件上传的,等等这些。

插件就是用这样一种模块化思想来进行开发的,下面我们通过代码来简单解释下 Module 模式。

var Helloword = function(objId){
    var _get_dom = function(Id){
        return document.getElementById(Id);
    }
    var _aim_obj = _get_dom(objId);
    var _say_hello = function(str){
        _aim_obj.innerHTML = str;    HelloWorld.sayHello('hello','hello text');
    }
    return{
        sayHello:_say_hello
    }
}

由上述代码可见,我们将某些功能,如 “sayHello” 给归到 Helloword (模块)中了。当然我们可以继续在下面添加其他功能,但都归于模块 Helloword 来管理。这就是 Module 的体现。

使用方法(注意这里使用了 new )

var Hei = new Helloword('hello');
Hei.sayHello('Hello Word');

var Hei2 = new Helloword('hi');
Hei2.sayHello('hi');

更直观点,我们来看下完整的代码

<!DOCTYPE html>
<html>
<head>
    <title>Module</title>
</head>
<body>
    <div Id="hello"></div>
    <div Id="hi"></div>
    <script type="text/javascript">
        var Helloword = function(objId){
            var _get_dom = function(Id){
                return document.getElementById(Id);
            }
            var _aim_obj = _get_dom(objId);
            var _say_hello = function(str){
                _aim_obj.innerHTML = str;
            }
            return{
                sayHello:_say_hello
            }
        }
        var Hei = new Helloword('hello');
        Hei.sayHello('Hello Word');
        
        var Hei = new Helloword('hi');
        Hei.sayHello('hi');        
    </script>
</body>
</html>

运行结果如下

运行结果

我们这里需要注意的是,每使用 new 创建出来的新对象都将开辟新的内存空间(新的一份copy),只要引用没有释放,那么该对象的占用的内存空间将不会被回收。那么如何避免过多浪费内存呢?一句话“释放引用”,只需要释放对该对象的所有引用,垃圾回收机制就会将该对象占用的内存空间回收。

var Hei = new Helloword('hello');
Hei.sayHello('Hello Word');

Hei = null;//解除引用。

这样还要“手动”内存管理,麻烦。如何让该模块在内存中只保留一份(copy)呢?请看下面一段代码

var HelloWorld = (function(){
    var _getDom = function(Id){
        return document.getElementById(Id)                    
    }
    var _sayHello = function(Id,str){
        _getDom(Id).innerHTML = str;
    }
    return {
        getDom:_getDom,
        sayHello:_sayHello
    }
}())

使用方法

HelloWorld.sayHello('hello','hello text');

是的,正如你所见到的,不需要 new 了。使用时不再需要创建新对象,也就是说我们只保持了该对象在内存中的一份引用,也就是HelloWorld 对它的引用。当 HelloWorld 对其引用解除时其所占用的内存将得到释放。上述代码实质上是一个匿名闭包。如果对闭包不是很理解的朋友可以看看我写的上一篇文章《浅析 JavaScript 中的闭包(Closures)》

四、插件基础代码

了解了上面的种种之后我们要开始直切主题了。

首先我们创建一个 js 文件 取名为 first-js-plugin.js(啥名字都行),键入以下代码

;
var plugin =(function(){
    function _firstFunc(str){
        console.log(str);
    };
    return{
        firstFunc: _firstFunc,
    };
})();

再创建一个 HTML页面 取名为 pluginTest.html (啥名字都行)

完整代码如下

<!DOCTYPE html>
<html>    
<head>
    <title></title>
    <script type="text/javascript" src="./first-js-plugin.js"></script>
    <script type="text/javascript">
        plugin.firstFunc("Hello ! I am firstFunc");
    </script>
</head>
<body>

</body>
</html>

运行结果:

运行结果

通过这个简单的插件,我们来分析一下里面的代码.

在分析代码之前我们先来了解另一个东西,自调用匿名函数(防止插件用户定义函数与插件冲突)

(function(){
    //code
})();

可能有些童鞋会觉得有点陌生,那看下下面的代码

var func = function(){
    //code
}
func();

其实这两段代码是等价的,当然有点差别,第一个是匿名函数.作用都是定义一个函数并立即执行.

(function(){
    //code
})();

代码分析:

  1. 最后面的小括号 () 表示执行该函数

  2. (匿名函数) 小括号(分组表达式)包起来匿名函数的声明,作用相当是将函数声明转为表达式,这样才能执行,仅此而已

    如果采取以下写法

     function(){
         //code
     }();
    

    编译器报错,问题是函数声明无法执行,表达式才能执行

搞清楚这些之后我们回头给下面的代码加上分析,如下

;//JavaScript 弱语法的特点,如果前面刚好有个函数没有以";"结尾,那么可能会有语法错误

/*
 plugin.api_funcs 给对象设置属性,属性值为 自调用匿名函数
 这里涉及到js作用域链以及闭包的知识点
*/    
var plugin =(function(){
    function _firstFunc(str){
        alert(str);
    };
    
    //返回API
    return{
        firstFunc: _firstFunc
    };
})();

我们将代码抽取一下(只为帮助理解,已经理解的朋友请忽略)

//01.定义变量
var plugin = 某对象;

//02.创建对象并返回
(function(){//code;return ...})();//匿名执行函数 return 某对象

//然后看核心的返回
return{firstFunc: _firstFunc};

//说白了就是,通过某个key将一个函数存储起来.使用时通过key访问到这个函数
var plugin = {key:function(){//code}}

//所以最终的体现如下
var plugin = {firstFunc: “具体的函数引用”}

所以我们最后才能通过,插件名.属性 来使用插件,正如:

plugin.firstFunc("Hello ! I am firstFunc");

四、插件的几种写法

这里我就不墨迹了,直接上代码,关键处会给注释

  1. 面向对象思想 类方式

     //自定义类    
     function plugin(){}
    
     //提供默认参数
     plugin.prototype.str = "default param";
    
     //提供方法(如果不传参,则使用默认参数)
     plugin.prototype.firstFunc = function(str = this.str){
         alert(str);
     }
    
     //创建"对象"
     var p = new plugin();
     //调用方法
     p.firstFunc("Hello ! I am firstFunc");//Hello ! I am firstFunc
     p.firstFunc();//default param
    
  2. 闭包方式

    闭包方式就是我们刚刚一直在介绍

     var plugin =(function(){
         function _firstFunc(str){
             alert(str);
         };
         return{
             firstFunc: _firstFunc,
         };
     })();
    
  3. 第二种方式上的一些变化

     (function(){
         //定义一些默认参数
         var _options={
             default_word:"default hello"                
         }
    
         //定义一些api
         var _plugin_api = {
             firstFunc:function(str = _options.default_word){
                 alert(str);
                 return this;//返回当前方法
             },
             secondFunc:function(){
                 alert("secondFunc");
                 return this;//返回当前方法
             }
         }
         //这里确定了插件的名称
         this.CJPlugin = _plugin_api;
     })();
    
     CJPlugin.firstFunc("hello");//hello
     CJPlugin.firstFunc();//default hello
     CJPlugin.secondFunc();//secondFunc
    
结束语

JavaScript 插件的相关知识今天暂时聊到这了.下篇文章笔者将通过实例来介绍如何开发一款属于自己的省市区三级联动插件.如果有朋友正在学习插件开发.那么下篇文章可能我们有更多可以探讨的话题。

限于笔者技术,文章观点难免有不当之处,希望发现问题的朋友帮忙指正,笔者将会及时更新。也请转载的朋友注明文章出处并附上原文链接,以便读者能及时获取到文章更新后的内容,以免误导读者。笔者力求避免写些晦涩难懂的文章(虽然也有人说这样显得高逼格,专业),尽量使用简单的用词和例子来帮助理解。如果表达上有好的建议的话也希望朋友们在评论处指出。
本文为作者原创,转载请注明出处! 《如何开发原生的 JavaScript 插件(知识点+写法)》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,383评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,522评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,852评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,621评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,741评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,929评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,076评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,803评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,265评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,582评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,716评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,395评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,039评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,027评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,488评论 2 361
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,612评论 2 350

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,637评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,839评论 25 707
  • 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。简单...
    舟渔行舟阅读 7,729评论 2 17
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,599评论 18 399
  • 经朋友不断推荐,读了《杀死一只知更鸟》,尽管我没有预设任何的期待,还是觉得阅读体验很棒。沿着作者给予的思路,我找到...
    孤单相许阅读 194评论 0 0