资讯专栏INFORMATION COLUMN

读 zepto 源码之工具函数

姘搁『 / 2264人阅读

摘要:在遍历的时候,还对回调函数的返回值进行判断,如果回调函数返回,立即中断遍历。可以遍历数组类数组或对象中的元素,根据回调函数的返回值,将返回值组成一个新的数组,并将该数组扁平化后返回,会将及排除。

Zepto 提供了丰富的工具函数,下面来一一解读。

源码版本

本文阅读的源码为 zepto1.2.0

$.extend

$.extend 方法可以用来扩展目标对象的属性。目标对象的同名属性会被源对象的属性覆盖。

$.extend 其实调用的是内部方法 extend, 所以我们先看看内部方法 extend 的具体实现。

function extend(target, source, deep) {
        for (key in source)  // 遍历源对象的属性值
            if (deep && (isPlainObject(source[key]) || isArray(source[key]))) { // 如果为深度复制,并且源对象的属性值为纯粹对象或者数组
                if (isPlainObject(source[key]) && !isPlainObject(target[key])) // 如果为纯粹对象
                    target[key] = {}  // 如果源对象的属性值为纯粹对象,并且目标对象对应的属性值不为纯粹对象,则将目标对象对应的属性值置为空对象
                if (isArray(source[key]) && !isArray(target[key])) // 如果源对象的属性值为数组,并且目标对象对应的属性值不为数组,则将目标对象对应的属性值置为空数组
                    target[key] = []
                extend(target[key], source[key], deep) // 递归调用extend函数
            } else if (source[key] !== undefined) target[key] = source[key]  // 不对undefined值进行复制
    }

extend 的第一个参数 taget 为目标对象, source 为源对象, deep 表示是否为深度复制。当 deeptrue 时为深度复制, false 时为浅复制。

extend 函数用 for···insource 的属性进行遍历

如果 deepfalse 时,只进行浅复制,将 source 中不为 undefined 的值赋值到 target 对应的属性中(注意,这里用的是 !==,不是 != ,所以只排除严格为 undefined 的值,不包含 null )。如果 source 对应的属性值为对象或者数组,会保持该对象或数组的引用。

如果 deeptrue ,并且 source 的属性值为纯粹对象或者数组时

3.1. 如果 source 的属性为纯粹对象,并且 target 对应的属性不为纯粹对象时,将 target 的对应属性设置为空对象

3.2. 如果 source 的属性为数组,并且 target 对应属性不为数组时,将 target 的对应属性设置为空数组

3.3. 将 sourcetarget 对应的属性及 deep 作为参数,递归调用 extend 函数,以实现深度复制。

现在,再看看 $.extend 的具体实现

$.extend = function(target) {
        var deep, args = slice.call(arguments, 1)
        if (typeof target == "boolean") {
            deep = target
            target = args.shift()
        }
        args.forEach(function(arg) { extend(target, arg, deep) })
        return target
    }

在说原理之前,先来看看 $.extend 的调用方式,调用方式如下:

$.extend(target, [source, [source2, ...]])
                  或
$.extend(true, target, [source, ...])

$.extend 中,如果不需要深度复制,第一个参数可以是目标对象 target, 后面可以有多个 source 源对象。如果需要深度复制,第一个参数为 deep ,第二个参数为 target ,为目标对象,后面可以有多个 source 源对象。

$.extend 函数的参数设计得很优雅,不需要深度复制时,可以不用显式地将 deep 置为 false。这是如何做到的呢?

$.extend 函数中,定义了一个数组 args,用来接受除第一个参数外的所有参数。

然后判断第一个参数 target 是否为布尔值,如果为布尔值,表示第一个参数为 deep ,那么第二个才为目标对象,因此需要重新为 target 赋值为 args.shift()

最后就比较简单了,循环源对象数组 args, 分别调用 extend 方法,实现对目标对象的扩展。

$.each

$.each 用来遍历数组或者对象,源码如下:

$.each = function(elements, callback) {
        var i, key
        if (likeArray(elements)) {  // 类数组
            for (i = 0; i < elements.length; i++)
                if (callback.call(elements[i], i, elements[i]) === false) return elements
        } else { // 对象
            for (key in elements)
                if (callback.call(elements[key], key, elements[key]) === false) return elements
        }

        return elements
    }

先来看看调用方式:$.each(collection, function(index, item){ ... })

$.each 接收两个参数,第一个参数 elements 为需要遍历的数组或者对象,第二个 callback 为回调函数。

如果 elements 为数组,用 for 循环,调用 callback ,并且将数组索引 index 和元素值 item 传给回调函数作为参数;如果为对象,用 for···in 遍历属性值,并且将属性 key 及属性值传给回调函数作为参数。

注意回调函数调用了 call 方法,call 的第一个参数为当前元素值或当前属性值,所以回调函数的上下文变成了当前元素值或属性值,也就是说回调函数中的 this 指向的是 item 。这在dom集合的遍历中相当有用。

在遍历的时候,还对回调函数的返回值进行判断,如果回调函数返回 falseif (callback.call(elements[i], i, elements[i]) === false) ) ,立即中断遍历。

$.each 调用结束后,会将遍历的数组或对象( elements )返回。

$.map

可以遍历数组(类数组)或对象中的元素,根据回调函数的返回值,将返回值组成一个新的数组,并将该数组扁平化后返回,会将 nullundefined 排除。

$.map = function(elements, callback) {
        var value, values = [],
            i, key
        if (likeArray(elements))
            for (i = 0; i < elements.length; i++) {
                value = callback(elements[i], i)
                if (value != null) values.push(value)
            }
        else
            for (key in elements) {
                value = callback(elements[key], key)
                if (value != null) values.push(value)
            }
        return flatten(values)
    }

先来看看调用方式: $.map(collection, function(item, index){ ... })

elements 为类数组或者对象。callback 为回调函数。当为类数组时,用 for 循环,当为对象时,用 for···in 循环。并且将对应的元素(属性值)及索引(属性名)传递给回调函数,如果回调函数的返回值不为 null 或者 undefined ,则将返回值存入新数组中,最后将新数组扁平化后返回。

$.camelCase

该方法是将字符串转换成驼峰式的字符串

$.camelCase = camelize

$.camelCase 调用的是内部方法 camelize ,该方法在前一篇文章《读Zepto源码之内部方法》中已有阐述,本篇文章就不再展开。

$.contains

用来检查给定的父节点中是否包含有给定的子节点,源码如下:

$.contains = document.documentElement.contains ?
        function(parent, node) {
            return parent !== node && parent.contains(node)
        } :
        function(parent, node) {
            while (node && (node = node.parentNode))
                if (node === parent) return true
            return false
        }

先来看看调用:$.contains(parent, node)

参数 parent 为父子点,node 为子节点。

$.contains 的主体是一个三元表达式,返回的是一个匿名函数。三元表达式的条件是 document.documentElement.contains, 用来检测浏览器是否支持 contains 方法,如果支持,则直接调用 contains 方法,并且将 parentnode 为同一个元素的情况排除。

否则,返回另一外匿名函数。该函数会一直向上寻找 node 元素的父元素,如果能找到跟 parent 相等的父元素,则返回 true, 否则返回 false

$.grep

该函数其实就是数组的 filter 函数

  $.grep = function(elements, callback) {
       return filter.call(elements, callback)
   }

从源码中也可以看出,$.grep 调用的就是数组方法 filter

$.inArray

返回指定元素在数组中的索引值

 $.inArray = function(elem, array, i) {
        return emptyArray.indexOf.call(array, elem, i)
    }

先来看看调用 $.inArray(element, array, [fromIndex])

第一个参数 element 为指定的元素,第二个参数为 array 为数组, 第三个参数 fromIndex 为可选参数,表示从哪个索引值开始向后查找。

$.inArray 其实调用的是数组的 indexOf 方法,所以传递的参数跟 indexOf 方法一致。

$.isArray

判断是否为数组

$.isArray = isArray

$.isArray 调用的是内部方法 isArray ,该方法在前一篇文章《读Zepto源码之内部方法》中已有阐述。

$.isFunction

判读是否为函数

$.isFunction = isFunction

$.isFunction 调用的是内部方法 isFunction ,该方法在前一篇文章《读Zepto源码之内部方法》中已有阐述。

$.isNumeric

是否为数值

$.isNumeric = function(val) {
        var num = Number(val), // 将参数转换为Number类型
            type = typeof val
        return val != null && 
          type != "boolean" &&
            (type != "string" || val.length) &&
          !isNaN(num) &&
          isFinite(num) 
          || false
    }

判断是否为数值,需要满足以下条件

不为 null

不为布尔值

不为NaN(当传进来的参数不为数值或如"123"这样形式的字符串时,都会转换成NaN)

为有限数值

当传进来的参数为字符串的形式,如"123" 时,会用到下面这个条件来确保字符串为数字的形式,而不是如 123abc 这样的形式。(type != "string" || val.length) && !isNaN(num) 。这个条件的包含逻辑如下:如果为字符串类型,并且为字符串的长度大于零,并且转换成数组后的结果不为NaN,则断定为数值。(因为 Number("") 的值为 0

$.isPlainObject

是否为纯粹对象,即以 {} 常量或 new Object() 创建的对象

$.isPlainObject = isPlainObject

$.isPlainObject 调用的是内部方法 isPlainObject ,该方法在前一篇文章《读Zepto源码之内部方法》中已有阐述。

$.isWindow

是否为浏览器的 window 对象

$.isWindow = isWindow

$.isWindow 调用的是内部方法 isWindow ,该方法在前一篇文章《读Zepto源码之内部方法》中已有阐述。

$.noop

空函数

$.noop = function() {}

这个在需要传递回调函数作为参数,但是又不想在回调函数中做任何事情的时候会非常有用,这时,只需要传递一个空函数即可。

$.parseJSON

将标准JSON格式的字符串解释成JSON

if (window.JSON) $.parseJSON = JSON.parse

其实就是调用原生的 JSON.parse, 并且在浏览器不支持的情况下,zepto 还不提供这个方法。

$.trim

删除字符串头尾的空格

$.trim = function(str) {
  return str == null ? "" : String.prototype.trim.call(str)
}

如果参数为 null 或者 undefined ,则直接返回空字符串,否则调用字符串原生的 trim 方法去除头尾的空格。

$.type

类型检测

$.type = type

$.type 调用的是内部方法 type ,该方法在前一篇文章《读Zepto源码之内部方法》中已有阐述。

能检测的类型有 "Boolean Number String Function Array Date RegExp Object Error"

系列文章

读Zepto源码之代码结构

读 Zepto 源码之内部方法

参考

Zepto中文文档

Node.contains()

Array.prototype.indexOf()

String.prototype.trim()

最后,所有文章都会同步发送到微信公众号上,欢迎关注,欢迎提意见:

作者:对角另一面

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/82770.html

相关文章

  • Zepto源码Form模块

    摘要:模块处理的是表单提交。表单提交包含两部分,一部分是格式化表单数据,另一部分是触发事件,提交表单。最终返回的结果是一个数组,每个数组项为包含和属性的对象。否则手动绑定事件,如果没有阻止浏览器的默认事件,则在第一个表单上触发,提交表单。 Form 模块处理的是表单提交。表单提交包含两部分,一部分是格式化表单数据,另一部分是触发 submit 事件,提交表单。 读 Zepto 源码系列文章已...

    陈江龙 评论0 收藏0
  • Zepto源码集合操作

    摘要:调用来获取符合条件的集合元素,这在上篇文章读源码之神奇的已经有详细的论述。然后调用方法来合并两个集合,用内部方法来过滤掉重复的项,方法在读源码之内部方法已经有论述。最后也是返回一个集合。 接下来几个篇章,都会解读 zepto 中的跟 dom 相关的方法,也即源码 $.fn 对象中的方法。 读Zepto源码系列文章已经放到了github上,欢迎star: reading-zepto 源码...

    pepperwang 评论0 收藏0
  • Zepto源码IOS3模块

    摘要:用法与参数要理解这段代码,先来看一下的用法和参数用法参数回调函数,有如下参数上一个回调函数返回的值或者是初始值当前值当前值在数组中的索引调用的数组初始值,如果没有提供,则为数组的第一项。接下来,检测回调函数是否为,如果不是,抛出类型错误。 IOS3 模块是针对 IOS 的兼容模块,实现了两个常用方法的兼容,这两个方法分别是 trim 和 reduce 。 读 Zepto 源码系列文章...

    lavnFan 评论0 收藏0
  • Zepto源码Stack模块

    摘要:读源码系列文章已经放到了上,欢迎源码版本本文阅读的源码为改写原有的方法模块改写了以上这些方法,这些方法在调用的时候,会为返回的结果添加的属性,用来保存原来的集合。方法的分析可以看读源码之模块。 Stack 模块为 Zepto 添加了 addSelf 和 end 方法。 读 Zepto 源码系列文章已经放到了github上,欢迎star: reading-zepto 源码版本 本文阅读的...

    crossea 评论0 收藏0
  • Zepto源码Selector模块

    摘要:如果伪类的参数不可以用转换,则参数为字符串,用正则将字符串前后的或去掉,再赋值给最后执行回调,将解释出来的参数传入回调函数中,将执行结果返回。重写的方法,改过的调用的是方法,在回调函数中处理大部分逻辑。 Selector 模块是对 Zepto 选择器的扩展,使得 Zepto 选择器也可以支持部分 CSS3 选择器和 eq 等 Zepto 定义的选择器。 在阅读本篇文章之前,最好先阅读《...

    Jioby 评论0 收藏0

发表评论

0条评论

姘搁『

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<