2、构造jQuery对象5——静态属性和方法

在构造jQuery对象模块中还定义了一些重要的静态属性和方法,它们时其余模块实现的基础。html

// 涉及外部代码行

// 代码行:48——124
var arr = [];

var document = window.document;

// 该方法与 setPrototypeOf 方法配套,用于读取一个对象的 prototype 对象。
var getProto = Object.getPrototypeOf;

var slice = arr.slice;

var concat = arr.concat;

var push = arr.push;

var indexOf = arr.indexOf;

var class2type = {};

var toString = class2type.toString;

var hasOwn = class2type.hasOwnProperty;

var fnToString = hasOwn.toString;

var ObjectFunctionString = fnToString.call( Object );

var support = {};

// isFunction(obj)用于判断传入的参数是不是函数
var isFunction = function isFunction(obj) {

    // Support: Chrome <=57, Firefox <=52
    // In some browsers, typeof returns "function" for HTML <object> elements
    // (i.e., `typeof document.createElement( "object" ) === "function"`).
    // We don't want to classify *any* DOM node as a function.
    // nodeType 属性可返回节点的类型。 
    /* 
    nodeType值-元素类型 
1-ELEMENT 
2-ATTRIBUTE 
3-TEXT 
4-CDATA 
5-ENTITY REFERENCE 
6-ENTITY 
7-PI (processing instruction) 
8-COMMENT 
9-DOCUMENT 
10-DOCUMENT TYPE 
11-DOCUMENT FRAGMENT 
12-NOTATION 
 */
    return typeof obj === "function" && typeof obj.nodeType !== "number";
};

// isWindow(obj)用于判断传入的参数是不是window对象,经过检测是否存在特征属性window来实现。
var isWindow = function isWindow(obj) {
    return obj != null && obj === obj.window;
};



// 变量用于保存脚本属性
var preservedScriptAttributes = {
    type: true,
    src: true,
    noModule: true
};

// 使用 jQuery html() 方法时插入的脚本老是执行的,jQuery 会检查传入的内容,并执行其中的每个脚本。
function DOMEval(code, doc, node) {
    doc = doc || document;
    // 设置当前文档,默认为document

    var i,
        script = doc.createElement("script");
    // 在文档中添加script节点

    script.text = code;
    // 给script添加内容

    // 若是存在节点
    if (node) {
        for (i in preservedScriptAttributes) {
            if (node[i]) {
                script[i] = node[i];
            }
        }
    }
    doc.head.appendChild(script).parentNode.removeChild(script);
    // 将代码放入文档中,当即执行,而后当即删除;
}


// 方法toType(obj)用于判断参数的内建JavaScript类型。
function toType(obj) {
    // 若是参数是undefined或null,返回"undefined"或"null ";
    if (obj == null) {
        return obj + "";
    }

    // Support: Android <=2.3 only (functionish RegExp)
    // 若是参数是JavaScript内部对象,则返回对应的字符串名称;其余状况一概返回"object"或"function"或class2type的类型。
    return typeof obj === "object" || typeof obj === "function" ?
        class2type[toString.call(obj)] || "object" :
        typeof obj;
}
// 代码行:144
rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;

// 主代码行:296——498
jQuery.extend({

    // Unique for each copy of jQuery on the page
    // 经过随机数(Math.random())给每一个副本都设定一个惟一值,而且经过正则replace( /\D/g, "" )把全部非数字替换成空。\D相似于[^0-9] 非数字。
    expando: "jQuery" + (version + Math.random()).replace(/\D/g, ""),

    // Assume jQuery is ready without the ready module
    // 设定jQuery在模块未加载的状况依然加载完毕。
    isReady: true,

    // error(msg),接受一个字符串,抛出一个包含了该字符串的异常。开发插件时能够覆盖这个方法,用来显示更有用或更多的错误提示消息。
    error: function (msg) {
        throw new Error(msg);
    },

    // noop()表示一个空函数。当但愿传递一个什么也不作的函数时,能够使用空函数。开发插件时,这个方法能够作为可选回调函数的默认值,若是没有提供回调函数,则执行jQuery.noop()。
    noop: function () {},

    // isPlainObject(object)用于判断传入的参数是不是“纯粹的对象”,便是否是用对象直接量{}或new Object()建立的对象。
    isPlainObject: function (obj) {
        var proto, Ctor;

        // Detect obvious negatives
        // Use toString instead of jQuery.type to catch host objects
        // 若是参数obj知足如下条件之一,则返回false:
        // 参数obj能够转换为false
        // Object.prototype.toString.call(obj)返回的不是[object Object]
        // 若是不知足以上全部条件,则至少能够肯定参数obj是对象。
        if (!obj || toString.call(obj) !== "[object Object]") {
            return false;
        }

        // 读取obj对象的 prototype 对象并赋值给proto
        proto = getProto(obj);

        // Objects with no prototype (e.g., `Object.create( null )`) are plain
        // 若是对象是个普通的对象没有原型(例如,Object.create(null))则返回true。
        if (!proto) {
            return true;
        }

        // Objects with prototype are plain iff they were constructed by a global Object function
        // 函数hasOwn.call()指向Object.prototype.hasOwnProperty(property),用于检测对象是否含有执行名称的非继承性。
        // fnToString:将函数转换成字符串 ObjectFunctionString:function Object() { [native code] }
        Ctor = hasOwn.call(proto, "constructor") && proto.constructor;
        // 返回判断对象是否是经过newObject()方式建立的结果
        return typeof Ctor === "function" && fnToString.call(Ctor) === ObjectFunctionString;
    },

    // isEmptyObject(object)用于检测对象是否为空(即不包含属性)
    isEmptyObject: function (obj) {

        /* eslint-disable no-unused-vars */
        // See https://github.com/eslint/eslint/issues/6125
        var name;
        // for-in循环会同时枚举非继承属性和从原型对象继承的属性,若是有,则当即返回false,不然默认返回true。
        for (name in obj) {
            return false;
        }
        return true;
    },

    // Evaluates a script in a global context
    // jQuery.globalEval(code):用于在全局做用域中执行JavaScript代码。
    globalEval: function (code) {
        // code:待执行的JavaScript语句或表达式
        DOMEval(code);
    },

    // 静态方法jQuery.each()是一个通用的遍历迭代方法,用于无缝地遍历对象和数组。对于数组和含有length属性的类数组对象。改方法经过下标遍历,从0到length-1;对于其余对象则经过属性名遍历(for-in),在遍历过程当中,若是回调函数返回false,则结束遍历。
    each: function (obj, callback) {
        // obj:待遍历的数组或对象。
      // callback:回调函数,会在数组的每一个元素或对象的每一个属性上执行。
        var length, i = 0;

        // 对于数组或类数组对象,经过for循环遍历下标。
        if (isArrayLike(obj)) {
            length = obj.length;
            for (; i < length; i++) {
                // 执行回调函数时传入3个参数:元素、下标、元素
                if (callback.call(obj[i], i, obj[i]) === false) {
                    break;
                }
            }
            // 对于对象用for-in循环遍历属性名。
        } else {
            for (i in obj) {
                // 执行回调函数时传入连个参数:对应的属性值、下标或属性名、对应的属性值。
                if (callback.call(obj[i], i, obj[i]) === false) {
                    break;
                }
            }
        }
        // 返回obj
        return obj;
    },

    // Support: Android <=4.0 only
    // jQuery.trim(text):用于移除字符串开头和结尾的空白符。若是是null返回空字符串,不然将text转成字符串,经过replace()中正则表达式rtrim匹配的子串并返回。
    trim: function (text) {
        return text == null ?
            "" :
            (text + "").replace(rtrim, "");
    },

    // results is for internal usage only
    // makeArray能够将一个类数组转换成真正的数组。若是传入第二个参数resullts(仅在jQuery内部使用),第一个参数arr中的元素将被合并入第二个参数,最后返回第二个参数,此时返回的不必定是真正的数组。
    makeArray: function (arr, results) {
        // arr:待转换对象,能够是任何类型
        // results:仅在jQuery内部使用。若是传入参数resultes,则在该参数上添加元素。

        // 定义返回值ret。若是传入了参数result则把该参数做为返回值,不然新建一个空数组做为返回值。
        var ret = results || [];

        // 若是传入的参数arr不是null、undefined的状况
        if (arr != null) {
            // 使用isArrayLike判断arr是不是数组或类数组对象。若是是数组或类数组对象,调用jQuery.merge()将arr合并到返回值ret中
            if (isArrayLike(Object(arr))) {
                jQuery.merge(ret,
                    typeof arr === "string" ? [arr] : arr
                );
                // 不然,由于不肯定ret的格式。因此选择push.call()的方式合并而不是ret.push()。若是只传入参数array,则返回值ret是真正的数组;若是还传入了第二个参数result,则返回值ret的类型取决于该参数的类型。
            } else {
                push.call(ret, arr);
            }
        }

        // 返回ret
        return ret;
    },

    // 在数组中查找指定的下标并返回其下标。elem要查找的值,arr将遍历的数组,i指定查找的位置。
    inArray: function (elem, arr, i) {
        // elem:要查找的值
        // arr:数组,将遍历这个数组来查找参数value在其中的下标
        // i:指定开始查找的位置
        // 若是数组为空,则默认返回-1。不然调用indexOf.call()方法返回其下标
        return arr == null ? -1 : indexOf.call(arr, elem, i);
    },

    // Support: Android <=4.0 only, PhantomJS 1 only
    // push.apply(_, arraylike) throws on ancient WebKit
    // 用于合并两个数组的元素到第一个数组中,第一个参数能够是数组或类数组对象,即必须含有整数(或能够转换成整些)属性length;第二个参数能够数组,类数组对象或任何含有连续属性的对象。注意:方法jQuery.merge()的合并具备破坏性,将第二个参数合并到第一个参数时,会改变第一个参数。若是不但愿改变第一个参数,能够在调用jQuery.merge()方法前对第一个参数进行备份。
    merge: function (first, second) {
        // 初始化变量,将second.length转换成数字,并将first.length赋值给i,循环添加second的属性到first中,由于不肯定first是不是数组,因此用i来修正first.length。最后返回first。
        var len = +second.length,
            j = 0,
            i = first.length;

        for (; j < len; j++) {
            first[i++] = second[j];
        }

        first.length = i;

        return first;
    },

    // 用于查找数组中知足过滤函数的元素,原数组不受影响。
    grep: function (elems, callback, invert) {
        // elems待遍历查找的数组
        // callback过滤每一个元素的函数,执行时被传入两个参数;当前元素和它的下标。该函数应该返回一个布尔值。
        // invert若是参数是false或未传入,grep()会返回一个知足回调函数的元素数组;若是invert是true,则返回一个不知足回调函数的元素数组。
        var callbackInverse,
            matches = [],
            i = 0,
            length = elems.length,
            callbackExpect = !invert;

        // Go through the array, only saving the items
        // that pass the validator function
        // 遍历数组elems,为每一个元素执行过滤函数。若是参数invert为true,把执行结果为false的元素放入结果数组matches;若是参数invert为false,把执行结果为true的元素放入结果数组matches;
        for (; i < length; i++) {
            callbackInverse = !callback(elems[i], i);
            if (callbackInverse !== callbackExpect) {
                matches.push(elems[i]);
            }
        }

        // 返回结果数组matches
        return matches;
    },

    // arg is for internal usage only
    // 静态方法jQuery.map()对数组中的每一个元素或对象的每一个属性调用一个回调函数,并将回调函数的返回值放入一个新数组中。执行回调函数时传入两个参数:数组元素或属性值,元素下标或属性名。关键字this指向全局对象window。回调函数的返回值会被放入新的数组中;若是返回一个数组,数组中将被扁平化后插入结果集;若是返回null或undefined,则不会放入任何元素。
    map: function (elems, callback, arg) {
        // elems:待遍历的数组或对象
        // callback:回调函数,会在数组的每一个元素或对象的每一个属性上执行。执行时传入两个参数:数组元素或属性值,元素下标或属性名。
        // arg:仅限于jQuery内部使用。若是调用jQuery.map()时传入了参数arg,则该参数会被传给回调函数callback。
        var length, value,
            i = 0,
            ret = [];

        // Go through the array, translating each of the items to their new values
        // 判断elems是数组,若是为true,将经过下标遍历
        if (isArrayLike(elems)) {
            length = elems.length;
            for (; i < length; i++) {
                // 为每一个元素执行回调函数callback,执行时依次传入三个参数:元素、下标、arg
                value = callback(elems[i], i, arg);

                // 若是回调函数的返回值不是null和undefined,则把返回值放入结果集ret
                if (value != null) {
                    ret.push(value);
                }
            }

            // Go through every key on the object,
            // 不然将经过属性名遍历
        } else {
            // 对于对象经过for..in循环遍历属性名
            for (i in elems) {
                // 为每一个属性值执行回调函数callback,执行时依次传入三个参数:元素、下标、arg
                value = callback(elems[i], i, arg);

                // 若是回调函数的返回值不是null和undefined,则把返回值放入结果集ret
                if (value != null) {
                    ret.push(value);
                }
            }
        }

        // Flatten any nested arrays
        // 最后在空数组[]上调用方法concat()扁平化结果集ret中的元素,并返回。
        return concat.apply([], ret);
    },

    // A global GUID counter for objects
    // guid是一个全局计数器,用于jQuery事件模块和缓存模块。在jQuery事件模块中,每一个事件监听函数会被设置一个guid属性,用来惟一标识这个函数。在缓存模块中,经过在DOM元素上附加一个惟一标识,来关联元素和该元素对应的缓存。属性jQuery.guid初始值为1,使用时自增1。
    guid: 1,

    // jQuery.support is not used in Core but other projects attach their
    // properties to it so it needs to exist.
    // support用于其余项目附加它们的属性
    support: support
});

// 添加Symbol属性
if (typeof Symbol === "function") {
    jQuery.fn[Symbol.iterator] = arr[Symbol.iterator];
    // 为 arr与jQuery.fn添加Symbol.iterator属性
    // Symbol.iterator 为每个对象定义了默认的迭代器。该迭代器能够被 for...of 循环使用。
}

// Populate the class2type map
// 初始化class2type结果为:
/*
{
    "[object Boolean]":"boolean",
    "[object Number]":"number",
    "[object String]":"string",
    "[object Function]":"function",
    "[object Array]":"array",
    "[object Date]":"date",
    "[object RegExp]":"regexp",
    "[object Object]":"object",
    "[object Error]":"error",
    "[object Symbol]":"symbol",
} 
*/
jQuery.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),
    function (i, name) {
        class2type["[object " + name + "]"] = name.toLowerCase();
    });

    // 用于判断obj是数组仍是对象
function isArrayLike(obj) {

    // Support: real iOS 8.2 only (not reproducible in simulator)
    // `in` check used to prevent JIT error (gh-2145)
    // hasOwn isn't used here due to false negatives
    // regarding Nodelist length in IE
    //!!obj 用于判断obj是非空的,即obj不是undefined、null或""(空)。
    var length = !!obj && "length" in obj && obj.length,
        type = toType(obj);

        // 是函数或窗口返回false
    if (isFunction(obj) || isWindow(obj)) {
        return false;
    }

    // elem.length是数值型,知足下列条件则为true;obj是真正的数组、length等于0、length大于0,且length - 1存在,便是一个类数组对象。
    return type === "array" || length === 0 ||
        typeof length === "number" && length > 0 && (length - 1) in obj;
}
相关文章
相关标签/搜索