体验一下链式写法

2017-10-17

开篇闲扯

恩,脑袋一热,突然想要用一下链式写法,但是期间又在看一些资料,于是就拖了又拖,链式写法很早就写好了,但是因为还对其他代码进行了改动,或者说是重写,所以就没有发布了,也没继续写这篇博客,现在终于都调整好了。

还是向实际情况妥协

根据实际的使用情况,之前的用法是

1
2
3
4
5
6
7
import {DOM} from 'pocket-tool';

// 先获取dom元素
const element = DOM.getElement('#id');

// 再对进行添加class
DOM.addClass(element, 'new-class');

额。。。我自己用起来也是相当不爽的,于是乎,改!换链式写法。
最终效果如下

1
2
3
4
5
6
7
import {DOM} from 'pocket-tool';

// 先获取dom元素
const element = DOM.getElement('#id');

// 再对进行添加class
element.addClass('new-class');

恩,这样就清爽多了,接下来开始分析具体的思路。

修改两次写法

其实链式写法的关键在于最后的return this;

proto

第一次是使用了__proto__的写法,其实思路也蛮简单的,将每个DOM的原型链该写到我定义的对象中。

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
// 获取dom元素
let element = document.getElmentByClassName('class');

// 拓展方法
const extendObj = {
addClass(className) {
...
return this; // 需要支持链式写法的方法,最后返回this
},
removeClass(className) {
...
return this;
},
on(event, handler, propagation) {
return ... // 不需要支持链式写法的方法,则不需要返回
},
off(event, handler, propagation) {
return ...
},
getDataSet(key) {
return ...
}
};

// 将类数组对象转换为数组对象
element = Array.from(element);

// 构建Element的原型链
element.forEach(e => {
if (!Utils.isNull(e)) {
e.__proto__ = Object.assign(e.__proto__, __proto__); // 重新创建__proto__
}
});

这样虽然可以,但是会有很多不可控因素,因为都坚决抵制修改原生方法的原型链,而且在IE中也不支持__proto__,所以当我提交代码后,没过多久就进行修改了。

借鉴jQuery

尝试了各种方法无果后,最后还是去借鉴了一下jQuery,为啥一开始没看咧,理由很简单,因为一些事情总要自己摸索的嘛。虽然说最后还是回到了jQuery但是过程还是很愉快的,哈哈哈。
jQuery是将一个个DOM元素加到数组中,然后进行访问,但是数据的原型链又是Object。所以就借鉴它们的写法,只不过我没有转化为类数组对象,而是将DOM按顺序,通过index作为key,最后再加上length

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

let elementArray = document.getElementbyClassName('class');
// 拓展方法
const extendObj = {
addClass(className) {
...
return this;
},
removeClass(className) {
...
return this;
},
on(event, handler, propagation) {
return ...
},
off(event, handler, propagation) {
return ...;
},
getDataSet(key) {
return ...;
}
};

// 构建Element的原型链
let element = Object.create(extendObj);

// 创建类数组对象
elementArray.forEach((e, index) => {
element[index] = e;
});

// 赋上length
element.length = elementArray.length;

其实思路蛮简单的,只要将element的原型链指向extendObj就好了,这样在访问方法时会自动在原型链上进行查找。
return this的话,其实就是返回当前的对象,也就是element因此还能继续访问对应的方法。具体的代码就不展开了。

原型链很重要

原型链蛮重要的,额,说实话我也掌握的并没有很好,其中有错误的地方还需要大家指正,当然这里也不会写很多关于原型链的知识。
这里想说的是我如何使用的。原来DOM元素上原型链不能去修改,因为会导致其他不可预知的错误。所以通过上面的写法,当我们调用方法的时候,访问的原型链其实是extendObj。当然也增加了一个用来访问原生DOM元素的方法eq

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 拓展方法
const extendObj = {
eq(index) {
if (this[index]) { // this 是element这个对象,index刚好能够访问对应key的元素
let e = Object.create(extendObj);// 这时还要创建一个新的对象,保证还能继续使用我们定义的方法
e[0] = this[index];
e.length = 1;
return e; // 返回取得的元素
}
return this[index];
}
};

element.eq(0) // HTMLElement

这样访问的就是原生的DOM元素啦,这样就做了相对的隔离,又保证了功能的完整性。

结语

诶,好像这是我写的最简短的博客,不过也没关系,意在理一理思路。这次的改写对我巩固原型链的帮助也是蛮大的,回去不断的查找资料,找到合适的解决方案。

下面是我的微信

欢迎骚扰

ww1o01