本文转自:http://www.xuanfengge.com/jquery-plug-in-written-summary-and-summary-of-writing-object-oriented-manner.html

前言

最近在折腾jQuery插件,写成插件的目的就是为了实现功能与项目相分离,使得这个代码在下一个项目中能直接引用不出错。这使得我们在写插件的时候,就得考虑清楚,怎么写才能使得插件能够通用、灵活度高、可配置、兼容性好、易用性高、耦合度低等。

接下来就对以下几种写法进行分析,前两个是jQuery插件,后面2个是以对象的形式开发,都类似。而且写法也很多,我们要懂得这样写的利弊。另一篇基础文章:jQuery 插件写法

写法一

插件主体

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
{
// 初始态定义
;
 
// 插件定义
{
// 默认参数,可被重写
{
// string
,
// num
,
// bollean
,
// object
,
// function
null
;
 
,
;
 
// 插件配置
;
 
// 初始化函数
{
{
// 对于已初始化的处理
// 如果此时已经存在弹框,则remove掉再添加新的弹框
}
// 初始化弹出框数据
;
// 事件绑定
;
// 加载内容
            
}
// 私有函数
;
;
{
// 内容(分字符和函数两种,字符为静态模板,函数为异步请求后组装的模板,会延迟,所以特殊处理)
{
{
// 便于传带参函数进来并且执行
;
// 有回调函数则执行
;
;
{
;
;
{
;
}
;
 
// 内部使用参数
{
,
'D_dbl'
;
 
// 提供外部函数
{
;
        
 
// 启动插件
;
 
// 链式调用
        
;
// 插件结束
;

 

调用

 
1
2
3
4
5
6
7
8
9
10
11
12
13
{
//覆盖默认值
//回调函数
{
;
}
}
;
// 调用提供的函数
;
{
 
}

 

点评

1. 自调用匿名函数

 
1
2
3
{
// jquery code
;

用处:通过定义一个匿名函数,创建了一个“私有”的命名空间,该命名空间的变量和方法,不会破坏全局的命名空间。这点非常有用也是一个JS框架必须支持的功能,jQuery被应用在成千上万的JavaScript程序中,必须确保jQuery创建的变量不能和导入他的程序所使用的变量发生冲突。

2. 匿名函数为什么要传入window

通过传入window变量,使得window由全局变量变为局部变量,当在jQuery代码块中访问window时,不需要将作用域链回退到顶层作用域,这样可以更快的访问window;这还不是关键所在,更重要的是,将window作为参数传入,可以在压缩代码时进行优化,看看jquery.min.js:

 
1
// jQuery被优化为a, window 被优化为 b

3. 全局变量this定义

 
1
2
,
;

使得在插件的函数内可以使用指向插件的this

4. 插件配置

 
1
;

设置默认参数,同时也可以再插件定义时传入参数覆盖默认值

5. 初始化函数

一般的插件会有init初始化函数并在插件的尾部初始化

6. 私有函数、公有函数

私有函数:插件内使用,函数名使用”_”作为前缀标识

共有函数:可在插件外使用,函数名使用”this.”作为前缀标识,作为插件的一个方法供外部使用

7. return this

最后返回jQuery对象,便于jQuery的链式操作

写法二

主体结构

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
{
;
;
{
9
;
// 插件配置
;
 
// 调用这个对象的方法,传递this
;
;
 
// 提供外部函数
{
;
}
 
//返回jQuery对象,便于Jquery的链式操作    
                   
}
{
,
;
}
}
;

 

点评

1. 美观

插件的方法写在外部,并通过在插件主体传递this的方式调用

2. 定义插件版本号

不过在这里还是没有用到

3. 关于call

这里的第一个参数为传递this,后面的均为参数

语法:

 
1
)

定义:调用一个对象的一个方法,以另一个对象替换当前对象。

说明:call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。

4. 关于”this”

在插件的方法中,可能有用到指向插件的this、和指向事件触发的this,所以事件触发的this用event来获取:event.cuerrntTarget

  • event.currentTarget:指向事件所绑定的元素,按照事件冒泡的方式,向上找到元素
  • event.target:始终指向事件发生时的元素

如:

html代码

 
1
2
3
>
>
>

 js代码

 
1
2
3
4
5
6
7
8
9
10
{
;
;
;
;
{
;
;
;
;

结果输出

 
1
2
3
4
5
6
#inner
>​
>​
#wrapper
>​
>​

 

写法三(原生写法)

主体结构 

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
{
// 配置(默认是从外面传进来)
;
// 初始化函数
;
}
 
{
// 函数执行
;
// 调用对象的私有方法
;
}
 
}
 
// 私有方法
{
;
    
// 使用underscore.js的extend方法来实现属性覆盖
;
{
// 调用对象的共有方法
;
        
}
 
// 公有方法
}

 

使用

 
1
2
;
;

 

点评

1. 关于属性覆盖(对象深拷贝)

原生函数实现方法

 
1
2
3
4
5
6
7
8
9
10
11
12
13
{
;
}
{
{
{
;
;
{
;
}
}
}

demo:

 
1
2
3
4
5
6
;
;
;
//改变test1的b属性对象的第0个数组元素
//不影响test,返回1
//返回change

基于jQuery的实现方法

 
1
;

用一个或多个其他对象来扩展一个对象,返回被扩展的对象。

如果不指定target,则给jQuery命名空间本身进行扩展。这有助于插件作者为jQuery增加新方法。 如果第一个参数设置为true,则jQuery返回一个深层次的副本,递归地复制找到的任何对象。否则的话,副本会与原对象共享结构。 未定义的属性将不会被复制,然而从对象的原型继承的属性将会被复制。

demo:

 
1
2
}

 2. 关于this

这个对象的所有方法的this都指向这个对象,所以就不需要重新指定

写法四

主体结构

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
{
;
;
;
}
{
,
 
{
,
{
{
                switch( match ) {
                    case '<':
                        return '&lt;';
                    case '>':
                        return '&gt;';
                    case '&':
                        return '&amp;';
':
';
                }
            });
        }
';
    },
    init: function() {
{
;
;
;
}
;
 
// 初始化富文本编辑器
;
;

 

点评

写法四和写法三其实都差不多,但是你们有没有看出其中的不一样呢?

1. 两种都是利用原型链给对象添加方法

写法三:

 
1
2
_timedHide
hide

写法四:

 
1
2
3
4
5
{
,
    
}
}

细看写法四利用“对象直接量”的写法给EditorUtil对象添加方法,和写法三的区别在于写法四这样写会造成consturctor属性的改变

constructor属性:始终指向创建当前对象的构造函数

每个函数都有一个默认的属性prototype,而这个prototype的constructor默认指向这个函数。如下例所示:

 

 
1
2
3
4
5
6
7
8
9
10
11
12
  
  
  
  
  
  
  
 
// true  
// true  
// 将上两行代码合并就得到如下结果  
// true

当时当我们重新定义函数的prototype时(注意:和上例的区别,这里不是修改而是覆盖),constructor属性的行为就有点奇怪了,如下示例:

 
1
2
3
4
5
6
7
8
9
10
11
12
  
  
  
  
  
  
  
  
  
// false  
// false  
// false

为什么呢?

原来是因为覆盖Person.prototype时,等价于进行如下代码操作:

 
1
2
3
4
5
  
  
  
  
;

而constructor属性始终指向创建自身的构造函数,所以此时Person.prototype.constructor === Object,即是:

 
1
2
3
4
5
6
7
8
9
10
11
12
  
  
  
  
  
  
  
  
  
// true  
// true  
// true

怎么修正这种问题呢?方法也很简单,重新覆盖Person.prototype.constructor即可:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
  
  
  
  
  
  
  
  
  
  
// true  
// true  
// true

一个最近写的拖拽排序的demo(惯例F12查看代码吧):http://xuanfengge.com/demo/201401/dragSort/

相关文章:

  • 2021-11-13
  • 2021-09-11
  • 2022-12-23
  • 2022-01-06
  • 2022-12-23
  • 2022-01-25
  • 2021-12-28
猜你喜欢
  • 2021-06-23
  • 2022-12-23
  • 2022-12-23
  • 2021-07-18
  • 2022-12-23
  • 2021-09-05
  • 2022-12-23
相关资源
相似解决方案