关于一些前端js框架的源码研究

underscore.js源码

Underscore.js 没有对原生 JavaScript 对象进行扩展,而是经过调用 _() 方法进行封装,一旦封装完成,原生 JavaScript 对象便成为一个 Underscore 对象。node

判断给定变量是不是对象

// Is a given variable an object?
    _.isObject = function(obj) {
        var type = typeof obj;
        return type === 'function' || type === 'object' && !!obj;
    };

这是underscore.js的判断给定变量是不是object的一段源码。 咱们知道typeof会返回以下六个值:数组

1. 'undefined' --- 这个值未定义;
2. 'boolean'    --- 这个值是布尔值;
3. 'string'        --- 这个值是字符串;
4. 'number'     --- 这个值是数值;
5. 'object'       --- 这个值是对象或null;
6. 'function'    --- 这个值是函数。

&&的优先级要高与||!!的做用至关于Boolean(),将其转换为布尔值。浏览器

判断给定值是不是DOM元素

// Is a given value a DOM element?
    _.isElement = function(obj) {
        return !!(obj && obj.nodeType === 1);
    };

一样!!至关于Boolean()的做用,nodeType === 1则说明是元素节点,属性attr是2 ,文本text是3函数

<body>
    <p id="test">测试</p>
<script>
    var t = document.getElementById('test');
    alert(t.nodeType);//1
    alert(t.nodeName);//p
    alert(t.nodeValue);//null
</script>
</body>

firstChild属性测试

var t = document.getElementById('test').firstChild;
alert(t.nodeType);//3
alert(t.nodeName);//#test
alert(t.nodeValue);//测试

文本节点也算是一个节点,因此p的子节点是文本节点,因此返回3this

zepto源码

判断是不是数组

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

Array.isArray() 方法:若是一个对象是数组就返回true,若是不是则返回falsespa

instanceof 用于判断一个变量是否某个对象的实例,如prototype

var a= [];
alert(a instanceof Array);//返回 true

同时 alert(a instanceof Object) 也会返回 true code

isArray 返回布尔值,若是Array.isArraytrue,则返回true,不然返回object instanceof Array的结果。对象

数据类型判断

class2type = {},

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

    function isFunction(value) { return type(value) == "function" }
    function isWindow(obj)     { return obj != null && obj == obj.window }
    function isDocument(obj)   { return obj != null && obj.nodeType == obj.DOCUMENT_NODE }
    function isObject(obj)     { return type(obj) == "object" }

class2type是一个空对象,实际上一个什么都没有的空对象是这样建立的Object.create(null);

咱们能够经过Object.prototype.toString.call()方法来判断数据类型,例如:

console.log(Object.prototype.toString.call(123)) //[object Number]  
console.log(Object.prototype.toString.call('123')) //[object String]    
console.log(Object.prototype.toString.call(undefined)) //[object Undefined]                         
console.log(Object.prototype.toString.call(true)) //[object Boolean]                                      
console.log(Object.prototype.toString.call({})) //[object Object]                                      
console.log(Object.prototype.toString.call([])) //[object Array]             
console.log(Object.prototype.toString.call(function(){})) //[object Function]

首先若是参数objundefinednull,则经过String(obj)转换为对应的原始字符串“undefined”或“null”。

而后class2type[toString.call(obj)]首先借用Object的原型方法toString()来获取obj的字符串表示,返回值的形式是 [object class],其中的class是内部对象类。

而后从对象class2type中取出[object class]对应的小写字符串并返回;若是未取到则一概返回“object

get方法

get: function(idx){
            return idx === undefined ? slice.call(this) : this[idx >= 0 ? idx : idx + this.length]
        },

取集合中对应指定索引的值,若是idx小于0,则idx等于idx+length,length为集合的长度.

可能你刚看到slice.call(this)会以为很纳闷,其实不只是zepto.js的源码,包括jQuerybackbone的源码都是这么写的,只不过它们在最开头作了声明:

var push = array.push;
var slice = array.slice;
var splice = array.splice;

因此slice.call(this)其实仍是Array.slce.call(this)

prototype.js源码

//为对象添加 class 属性值
    
   addClassName: function(element, className) {
        element = $(element);
        Element.removeClassName(element, className);
        element.className += ' ' + className;
    },

    
   //为对象移除 class 属性值
     
    removeClassName: function(element, className) {
        element = $(element);
        if (!element)
            return;
        var newClassName = '';
        var a = element.className.split(' ');
        for (var i = 0; i < a.length; i++) {
            if (a[i] != className) {
                if (i > 0)
                    newClassName += ' ';
                newClassName += a[i];
            }
        }
        element.className = newClassName;
    },

由于addClassName依赖于removeClassName(),因此先分析后者,$()是先将元素封装成prototype对象,

if(!element)  return

这句的意思就是若是元素对象不存在,则忽略再也不继续执行的意思,也就是终止的意思。

split() 方法用于把一个字符串分割成字符串数组。

若是把空字符串 ("") 用做 分隔符,那么 该对象 中的每一个字符之间都会被分割。

判断是否拥有 class 属性值

//是否拥有 class 属性值

hasClassName: function(element, className) {
    element = $(element);
    if (!element)
        return;
    var a = element.className.split(' ');
    for (var i = 0; i < a.length; i++) {
        if (a[i] == className)
            return true;//返回正确的处理结果
    }
    return false;//返回错误的处理结果
},

兼容旧版本浏览器增长Array的push方法

/**
 * 为兼容旧版本的浏览器增长 Array 的 push 方法。
 */
if (!Array.prototype.push) {
    Array.prototype.push = function() {
        var startLength = this.length;//this指代Array
        for (var i = 0; i < arguments.length; i++)
            this[startLength + i] = arguments[i];//this依旧指代Array
        return this.length;
    }
}

!Array.prototype.push若是为true,说明浏览器不支持该方法,则往下执行。this[startLength + i] = arguments[i]将传递进来的每一个参数依次放入数组中,最后返回数组的长度

访问对象可使用(.)表示法,也可使用[]来访问,一样访问数组元素也是

jQuery 源码

jQuery源码太多关联了,因此很差单独拿出来作分析,就举一两个简单的例子吧:

toArray方法

jQuery.prototype = {
    toArray: function() {
            return slice.call( this );
        },
}

Array.prototype.slice.call(arguments)能将具备length属性的对象转成数组,也就是说其目的是将arguments对象的数组提出来转化为数组。例如:

<script>
    var a = {length:4,0:'zero',1:'one',2:'two'};
    console.log(Array.prototype.slice.call(a));// Array [ "zero", "one", "two", <1 个空的存储位置> ]
</script>

slice有两个用法,一个是String.slice,一个是Array.slice,第一个返回的是字符串,第二个返回的是数组。

Array.prototype.slice.call(arguments)可以将arguments转成数组,那么就是arguments.toArray().slice();

由于arguments并非真正的数组对象,只是与数组相似而已,因此它并无slice这个方法,而Array.prototype.slice.call(arguments)能够理解成是将arguments转换成一个数组对象,让arguments具备slice()方法。 好比:

var arr = [1,2,3,4];
 console.log(Array.prototype.slice.call(arr,2));//[3,4]

Array
这是咱们想要的基对象名称

prototype
这能够被认为是一个数组的实例方法的命名空间

slice
这提取数组的一部分并返回新的数组,并无开始和结束索引,它只是返回一个数组的拷贝

call
这是一个很是有用的功能,它容许你从一个对象调用一个函数而且使用它在另外一个上下文环境

下面的写法是等效的:

Array.prototype.slice.call == [].slice.call

看这个例子:

object1 = {
    name:'frank',
    greet:function(){
        alert('hello '+this.name)
    }
};

object2 = {
    name:'trigkit4'
};

// object2没有greet方法
// 但咱们能够从object1中借来

 object1.greet.call(object2);//弹出hello trigkit4

分解一下就是object1.greet运行弹出hello + 'this.name',而后object2对象冒充,this就指代object2

var t = function(){
    console.log(this);// String [ "t", "r", "i", "g", "k", "i", "t", "4" ]
    console.log(typeof this);  //  Object
    console.log(this instanceof String);  // true
};
t.call('trigkit4');

call(this)指向了所传进去的对象。

Object.prototype中已经包含了一些方法:

1.toString ( )

    2.toLocaleString ( )

    3.valueOf ( )

    4.hasOwnProperty (V)

    5.isPrototypeOf (V)

    6.propertyIsEnumerable (V)

on方法

jQuery.fn.extend({
    on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
        var type, origFn;

        // Types can be a map of types/handlers
        if ( typeof types === "object" ) {
            // ( types-Object, selector, data )
            if ( typeof selector !== "string" ) {
                // ( types-Object, data )
                data = data || selector;
                selector = undefined;

            }
        }
})


jQuery.extend(object) :为扩展jQuery类自己.为类添加新的方法。

jQuery.fn.extend(object) :给jQuery对象添加方法。

!= 在表达式两边的数据类型不一致时,会隐式转换为相同数据类型,而后对值进行比较.
!== 不会进行类型转换,在比较时除了对值进行比较之外,还比较两边的数据类型, 它是恒等运算符===的非形式。

on : function(){}js对象字面量的写法

{键:值,键:值}语法中的“健/值”会成为对象的静态成员。若是给某个“健”指定的值是一个匿名函数,那么该函数就会变成对象的静态方法;不然就是对象的一个静态属性。
图片描述

jQuery类型判断

type: function( obj ) {
            if ( obj == null ) {
                return obj + "";
            }
            return typeof obj === "object" || typeof obj === "function" ?
            class2type[ toString.call(obj) ] || "object" :
                typeof obj;
        },

前面已经分析了,class2type = {};因此class2type[ toString.call(obj) ] =
{}.toString.call(obj)。它的做用是改变toStringthis指向为object的实例。

相关文章
相关标签/搜索