bee.js一个类zepto的适合嵌入的小型库

18 December 2015

前端开发 bee.js 嵌入 dom

前言

已经有才不多一个月没有更新博客了,自从上个月分析完了zepto.js就没有再提笔写博客。一方面是自己确实有太多的东西不知道, 另一方面,我在想该不该把这个库写出来–bee.js。我对bee.js的定位是:嵌入,小,模块化,所以我取了bee(蜜蜂),本来是打算用蜂鸟的但是感觉太长了。 可能因为我工作的原因,有时候需要在别人的页面里面嵌入一段js的时候,会发现别人的页面里面没有jquery | zepto 之类的操作dom的类库,但又不想引入第3方库(因为感觉太大了),瞬间感觉整个世界都不好了。

bee.js github地址: https://github.com/Yi-love/bee/blob/master/bee-master.js

模块划分

大体上我打算将bee.js分为3个模块:

    1. 核心模块
    1. event事件模块
    1. ajax模块

为了适应嵌入环境,所以压缩后的代码打算控制在10kb上下。以免对真正需要执行的代码增加负担。

核心模块

我对bee.js的第一个定位嵌入,就是为了可以嵌入到页面当中。所以它必须小,所以核心模块的功能只有基本的:

    1. dom获取
    1. className操作
    1. html操作
    1. text操作
    1. attr操作
    1. val操作

我想说,你会用jquery|zepto那么你就会bee.js,可以说 bee.js就是mini版的zepto,zepto已经超过了20kb, 作为嵌入执行代码已经感觉它太过于庞大了。

正题

zepto源码分析的第一篇我就已经分析过了dom的操作,这篇主要是来聊聊第二次解读zepto源码已经编写bee.js时所遇到的问题。 有时候你觉得很简单的事情可能并不简单。就好比我遇到的第一个问题,如何使 new Bee(dom , selector) 返回的是一个数组, 而不是一个对象。

你可能会觉得好笑,不加思索或者没有真正了解js的话你可能觉得这真的是一件不可思议的事。这第一个坑我踩的满满的。我努力的 比对了zepto和我自己的代码,没什么出入啊。

最后通过代码删减法,比对出了差别,那就是$.fn中多了以下几个函数: $.fn

这个是为什么呢,唯一有可能就是 new Bee(dom , selector)有了数组的某些功能,使它返回变为数组。 我们都知道:数组有两种基本的创建方式:

  //使用Array构造函数
  var arr = new Array()
  //赋值法
  var arr2 = [];

我想可能因为$.fn添加了数组的几个基本函数而造成了 new Bee(dom , selector)返回的变为数组。

说到这我不免有一点感伤,只能怪我没有很好的理解zepto使用的创建对象的模式。所以在理思路的时候总是一团糟。 这几天我也没有闲着,在恶补js的对象创建以及继承。

我们先来看看一种原型模式下的实例的创建:

 function Person(){
 }
 var friend = new Person();
 
 Person.prototype = {
   constructor : Person,
   name : 'Jin',
   age : 24,
   job: 'ue',
   getName : function(){
     return this.name;
   }
 }
 friend.getName()//error

这里说明了一点:实例的指针仅指向原型,而不指向构造函数。所以上面的friend会没有getName方法而造成报错。 那该如何让friend可以调用getName()函数呢。

方法一:让friend.prototype = Person.prototype ,于是在调用getName前添加此代码。

 function Person(){
 }
 var friend = new Person();
 
 Person.prototype = {
   constructor : Person,
   name : 'Jin',
   age : 24,
   job: 'ue',
   getName : function(){
     return this.name;
   }
 }
 friend.prototype = Person.prototype;
 friend.getName()//error

运行代码还是出错,但我们通过:可以成功的获取到数据。

 friend.prototype.getName()//Jin

出现上面的问题其实就是我们没有修改friend指向构造函数原型的指针([[prototype]])。 如何才能让friend.getName()生效。 下面是这个过程的内幕: person

理解了上面的内幕,那我们来看看bee.js(zepto.js)是怎样实现对象的创建的。

  var bee = {} , $;
  function Bee(dom , selector){
    this.selector = selector||'';
  }
  bee.B = function(dom , selector){
    return new Bee(dom,selector);
  }
  $ = function(selector){
    var dom //=doSomeThing();
    return bee.B(dom,selector);
  }
  $.fn = {
    constructor:bee.B,
    length : 0,
    //...
    slice : function(){
      console.log(this.selector);
    }
  }
  bee.B.prototype = Bee.prototype = $.fn;

我稍微的简化了代码,这样可以更好的理解内幕原理是怎么实现的。 bee

最后使用 $ 来获取的selector最后得到了一个新的Bee()实例对象,它不仅拥有Bee的属性还拥有Bee的prototype函数。 照这样的话可以直接返回Bee()实例对象即可。那为什么还要用bee.B包装一下呢。

下面是我的疑问,不知道你又没有:

    1. 就是上面说的为什么要用bee.B包装?
    1. 还是老问题如何返回数组,我们看到上面在chrome中的temp还是一个对象?

1.bee.B的包装

我们加入去掉bee.B这一层的话,那么这就是一个活脱脱的组合使用构造函数模式和原型。 但当我们加上bee.B这一层函数的话,又好比使用寄生模式,返回的Bee实例对象和Bee.B是没有一点关系的。 如果我们要使用instanceof对Bee实例对象进行判断,那么把constructor:bee.B设置成这样就可以了。 这样我们就可以在外层进行另外的操作。原始返回的依然是Bee实例对象。

返回数组

由于原型模式的重要性不仅体现在创建自定义类型方面,就连所有的原生的引用类型,都采用这种模式创建。

所有的原生引用类型(Array , String, Object ,…)都在其构造函数上定义方法。

通过对上面的理解,可以推测只要我们在任意一个对象上面添加任意的引用类型就可以模拟该类型。换句话说就是:我们在一个对象上添加Array的prototype函数, 是不是就可以像使用数组方法一样使用该对象实例。