Internal method of reading Zepto source code

Array method

Definition

var emptyArray = []
    concat = emptyArray.concat
    filter = emptyArray.filter
    slice = emptyArray.slice

zepto started by defining an empty array, emptyArray, to get concat, filter, slice methods of the array

compact

function compact(array) {
  return filter.call(array, function(item) {
    return item != null
  })
}

Delete null and undefined in an array

Here we use the filter method of the array to filter out the elements of item!= null to form a new array. It's easy to understand why undefined can be deleted. This is because when you use!=, not!=, use!=, null undefined will be converted to false first and then compared.

For null and undefined recommendations, check out this article: The difference between undefined and null

flatten

function flatten(array) {
  return array.length > 0 ? $.fn.concat.apply([], array) : array
}

Flattening arrays, such as transforming arrays [1,[2,3], [4,5], 6, [7,[89], into [1,2,3,4,5,6,7,[8,9], can only expand one layer, and multi-layer nesting can only expand one layer.

Here, we first equate $. fn.concat with the original method concat of arrays, and we will analyze $. fn.concat in the following chapters.

The clever thing here is to use apply, which takes the item in array as a parameter, concat.apply([], [1,2,3,[4,5]) is equivalent to []. concat(1,2,3,[4,5]), so that the array is flattened.

uniq

uniq = function(array) {
  return filter.call(array, function(item, idx) {
    return array.indexOf(item) == idx
  })
}

Array de-weighting.

The principle of array de-duplication is to detect whether the first occurrence of an item in an array is equal to that of an item. If it is not equal, it is proved that it is not the first occurrence and filtered out.

String method

camelize

camelize = function(str) {
  return str.replace(/-+(.)?/g, function(match, chr) {
    return chr ? chr.toUpperCase() : ''
  })
}

Converting a string in word-word form to wordWord form can be one or more.

The regular expression matches one or more -, and the capture group is the first letter after the capture number and capitalizes the letter.

dasherize

function dasherize(str) {
    return str.replace(/::/g, '/')
           .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
           .replace(/([a-z\d])([A-Z])/g, '$1_$2')
           .replace(/_/g, '-')
           .toLowerCase()
  }

Convert hump-style writing to hyphen-style writing.

For example, a = A6DExample::Before:

The first regular expression is to replace: / in the string. a becomes A6DExample/Before

The second rule is to add between the occurrence of one or more uppercase letters and the occurrence of one or more uppercase letters and one or more consecutive lowercase letters. a becomes A6D_Example/Before

The third rule is to add between a lowercase letter or number and a capital letter. A becomes A6_D_Example/Before

The fourth regular expression is to replace with -. a becomes A6-D-Example/Before

Finally, all uppercase letters are converted into lowercase letters. A becomes a6-d-example/before

I am not very familiar with regularity. The explanation of regularity refers to: zepto source code -- compact, flatten, camelize, dasherize, uniq -- learning notes

Data Type Detection

Definition

class2type = {},
toString = class2type.toString,

  // Populate the class2type map
$.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
  class2type["[object " + name + "]"] = name.toLowerCase()
})

As you will see later in the article on the $. each function, this code hangs the basic type onto the class2type object. Class2type will be in the following form:

class2type = {
  "[object Boolean]": "boolean",
  "[object Number]": "number"
  ...
} 

type


function type(obj) {
  return obj == null ? String(obj) :
  class2type[toString.call(obj)] || "object"
}

The type function returns the type of data.

If obj = null, that is null and undefined, the string null or undefined is returned

Otherwise, the Object.prototype.toString (toString = class2type.toString) method is called, and the returned result is taken as the key value of the class2type. Object.prototype.toString returns results like [object Boolean] for different data types.

If none of the above is the case, the object type is returned by default.

isFunction & isObject

function isFunction(value) {
  return type(value) === 'function'
}
function isObject(obj) {
  return type(obj) == 'object'
}

Call the type function to determine the type string returned, and you know what data type it is.

isWindow

function isWindow(obj) {
  return obj != null && obj == obj.window
}

Determine whether it is a window object for browsers

The first condition to be satisfied for a window object is that it cannot be null or undefined, and obj.window is its own reference.

isDocument

function isDocument(obj) {
  return obj != null && obj.nodeType == obj.DOCUMENT_NODE
}

Determine whether it is a document object

Node has nodeType attributes, and each attribute value has a corresponding constant. Doument has a nodeType value of 9 and a constant of DOCUMENT_NODE.

See specifically: MDN document: Node.nodeType

isPlainObject

function isPlainObject(obj) {
  return isObject(obj) && !isWindow(obj) && Object.getPrototypeof(obj) == Object.prototype
}

Judging whether it is a pure object

Pure objects must first be isObject(obj)

And it's not a window object! isWindow(obj)

And the prototype should be the same as the prototype of Object.

isArray

isArray = Array.isArray || 
           function(object) { return object instanceof Array}

This method is used to determine whether an array is of type or not.

If the browser supports the isArray native method of the array, it uses the native method, otherwise it detects whether the data is an instance of Array.

As we all know, the principle of instanceof detection is to find whether the prototype of the instance is on the prototype chain of the constructor, and if so, return true. So using instanceof may get inaccurate results. For example:

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script>
        window.onload = function () {
            var fwindow = window.framePage.contentWindow // window object of frame page
            var fArray = fwindow.Array  // Array of frame page
            var fdata = fwindow.data  // frame page data [1,2,3]
            console.log(fdata instanceof fArray) // true
            console.log(fdata instanceof Array) // false
        }
    </script>
    <title>Document</title>
</head>
<body>
    <iframe id="framePage" src="frame.html" frameborder="0"></iframe>
</body>
</html>

frame.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script>
        window.data = [1,2,3]
    </script>
</head>
<body>
    <p>frame page</p>
</body>
</html>

Because iframe runs in a separate environment, fdata instance of Array returns false.

As you can see on MDN, you can use this ployfill to use isArray

if (!Array.isArray) {
  Array.isArray = function(arg) {
    return Object.prototype.toString.call(arg) === '[object Array]'
  }
}

That is to say, isArray can be modified as follows:

isArray = Array.isArray || 
           function(object) { return Object.prototype.toString.call(object) === '[object Array]'}

Why doesn't zepto write that way? If you know, leave a message to inform you.

likeArray

function likeArray(obj) {
  var length = !!obj &&   // obj must exist
                  'length' in obj && // The length attribute must exist in obj
                  obj.length, // Returns the value of length
      type = $.type(obj) // Call the type function to return the data type of obj. I don't quite understand why I should override the type function defined above. Is it not good to call the type function directly by defining one more variable?

  return 'function' != type &&  // Not function al type
        !isWindow(obj) &&  // And it's not a window type
        (
            'array' == type || length === 0 || // If the value for array type or length is 0, return true
    (typeof length == 'number' && length > 0 && (length - 1) in obj)  // Or length is a number, and length is greater than zero, and length - 1 is the key of obj
  )
}

Determine whether the data is an array of classes.

The form of the class array is as follows:

likeArrayData = {
  '0': 0,
  '1': 1,
  "2": 2
  length: 3
}

As you can see, class arrays have length attributes, and key s are numbers in the order of 0, 1, 2, 3.

The code has been commented, so here's a brief summary

First, exclude function type and window object

The array type and length === 0 are considered as class arrays. It's easy to understand that type is array. Length == 0 actually treats it as an empty array.

In the last case, three conditions must be met:

  1. length must be a number

  2. length must be greater than 0, indicating that elements exist in the class array

  3. Key length -1 must exist in obj. As we all know, the last index value of the array is length -1, which is also a check to see if the last key exists.

Series of articles

  1. Code structure for reading Zepto source code

Reference resources

Finally, all articles will be sent to the Wechat Public Number synchronously. Welcome your attention and comments.

Keywords: Javascript Attribute

Added by inferium on Wed, 10 Jul 2019 23:32:43 +0300