關於一些前端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的實例。

相關文章
相關標籤/搜索