angular源碼分析:angular中各種常用函數,比較省代碼的各種小技巧


angular的工具函數

在angular的API文檔中,在最前面就是講的就是angular的工具函數,下面列出來

angular.bind           //用戶將函數和對象綁定在一起,返回一個新的函數
angular.bootstrap      //angular啟動函數,一般不用,除了e2e測試

angular.copy           //對象拷貝
angular.element        //jQlite,作用類似於jQuery
angular.equals         //對象比較。用 == 對兩個對象進行比較,除非他們是同一個對象的引用,否則都不會相等。
angular.extend         //對象擴展,將兩個對象的屬性求並集
angular.forEach        //功能類似於jQuery中each函數,

angular.noop           //額,一個空函數
angular.identity       //額,這個函數可以返回它的第一個函數

angular.injector       //用來創建一個注入器,可以用來給其他函數注入所依賴的對象
angular.module         //兩個參數,注冊模塊;一個參數,返回模塊。太常用了。

angular.fromJson       //將json轉成對象
angular.toJson         //將對象轉成json


angular.uppercase   //小寫轉大寫
angular.lowercase   //大寫轉小寫

//類型檢查
angular.isArray
angular.isDate
angular.isDefined
angular.isElement
angular.isFunction
angular.isNumber
angular.isObject
angular.isString
angular.isUndefined

他們都是怎么實現的,並且怎么用

1.類型檢查函數

function isUndefined(value) {return typeof value === 'undefined';}

function isDefined(value) {return typeof value !== 'undefined';}

function isObject(value) {
  return value !== null && typeof value === 'object';  //需要判斷null的情況,在js中null是一個object,但是又不是我們理解的object
}

function isString(value) {return typeof value === 'string';}

function isNumber(value) {return typeof value === 'number';}

function isDate(value) {
  return toString.call(value) === '[object Date]';  //前面有定義:toString = Object.prototype.toString,  //技巧1
}

var isArray = Array.isArray;

function isFunction(value) {return typeof value === 'function';}


function isElement(node) {
  return !!(node &&      //技巧2
    (node.nodeName  // we are a direct element
    || (node.prop && node.attr && node.find)));  // we have an on and find method part of jQuery API
}

技巧1:通過短引用的定義來減少代碼書寫
技巧2:使用雙嘆號(!!),將表達式的值限制在true/false間。作用類似於C語言中的 bool(expression),將表達式的值強制轉換為布爾值。

2.下面分析大小寫轉換


//方案1:大小寫都是利用字符串對象本身的大小寫轉換函數來完成的

var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};

//方案2:自定義大小寫轉換函數
var manualLowercase = function(s) {
  /* jshint bitwise: false */
  return isString(s)
      ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);}) //技巧3
      : s;
};
var manualUppercase = function(s) {
  /* jshint bitwise: false */
  return isString(s)
      ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
      : s;
};


// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
// with correct but slower alternatives.
//土耳其環境,瀏覽器的默認提供大小寫轉換函數會出問題,所以采用方案2
if ('i' !== 'I'.toLowerCase()) {
  lowercase = manualLowercase;
  uppercase = manualUppercase;
}

技巧3:字符串對象的replace函數妙用
stringObject.replace(regexp/substr,replacement)
三種使用境界:
1.兩個參數都是字符或者字符串。函數會在stringObject中查找第一個字符或字符串,並且替換成第二個字符或字符串
2.第一參數使用正則表達式,第二參數使用字符或字符串。函數會用正則表達式的方式進行查找並替換
3.第一參數是字符串或正則表達式,第二個參數是一個回掉函數。replace函數會按第一個參數查找,然后將值傳到第二參數指定的函數中得到反饋值替換被查找到的字符串。
例子:寫一個函數處理下面字符串,給下面代碼中的a標簽中的內容都添加一個括號

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <a name="dapeng">我是大鵬</a>
    <a href="#dapeng">點擊,鏈接到大鵬</a>
</body>
</html>

js函數:

function add_brackets(inputStr){
    return typeof inputStr == 'string' && inputStr.replace(/(<a.+?>)(.+)(<\/a>)/im,function(){
        return arguments[1] + '(' + arguments[2] + ')' + arguments[3];
    });
}

3.關於angular.noop和angular.identity

下面是angular注釋中給出的angular.noop和angular.identity的使用例子。

     function foo(callback) {
       var result = calculateResult();
       (callback || angular.noop)(result); //技巧4:不用再去判斷callback是否定義
     }

     function transformer(transformationFn, value) {
       return (transformationFn || angular.identity)(value);
     };

技巧4:定義一些默認的對象,在函數中,將參數與默認對象求或,可以省去參數是否定義的判斷。

拷貝,angular.copy

var TYPED_ARRAY_REGEXP = /^\[object (Uint8(Clamped)?)|(Uint16)|(Uint32)|(Int8)|(Int16)|(Int32)|(Float(32)|(64))Array\]$/;
function isTypedArray(value) {
  return TYPED_ARRAY_REGEXP.test(toString.call(value));
}

/**
 * Set or clear the hashkey for an object.
 * @param obj object
 * @param h the hashkey (!truthy to delete the hashkey)
 */
function setHashKey(obj, h) {
  if (h) {
    obj.$$hashKey = h;
  } else {
    delete obj.$$hashKey;
  }
}

function copy(source, destination, stackSource, stackDest) {  //技巧5
  if (isWindow(source) || isScope(source)) {
    throw ngMinErr('cpws',
      "Can't copy! Making copies of Window or Scope instances is not supported.");
  }
  if (isTypedArray(destination)) {
    throw ngMinErr('cpta',
      "Can't copy! TypedArray destination cannot be mutated.");
  }

  if (!destination) {
    destination = source;
    if (isObject(source)) {
      var index;
      if (stackSource && (index = stackSource.indexOf(source)) !== -1) {
        return stackDest[index];
      }

      // TypedArray, Date and RegExp have specific copy functionality and must be
      // pushed onto the stack before returning.
      // Array and other objects create the base object and recurse to copy child
      // objects. The array/object will be pushed onto the stack when recursed.
      if (isArray(source)) {
        return copy(source, [], stackSource, stackDest);
      } else if (isTypedArray(source)) {
        destination = new source.constructor(source);
      } else if (isDate(source)) {
        destination = new Date(source.getTime());
      } else if (isRegExp(source)) {
        destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
        destination.lastIndex = source.lastIndex;
      } else if (isFunction(source.cloneNode)) {
          destination = source.cloneNode(true);
      } else {
        var emptyObject = Object.create(getPrototypeOf(source)); //getPrototypeOf = Object.getPrototypeOf, 這里用原型對象創建了一個空對象
        return copy(source, emptyObject, stackSource, stackDest); //將其轉化為destination有定義的調用
      }

      if (stackDest) {
        stackSource.push(source);
        stackDest.push(destination);
      }
    }
  } else {
    if (source === destination) throw ngMinErr('cpi',
      "Can't copy! Source and destination are identical.");

    stackSource = stackSource || [];
    stackDest = stackDest || [];

    if (isObject(source)) {
      stackSource.push(source);
      stackDest.push(destination);
    }

    var result, key;
    if (isArray(source)) {  //如果源對象是一個數組,循環拷貝數組
      destination.length = 0;
      for (var i = 0; i < source.length; i++) {
        destination.push(copy(source[i], null, stackSource, stackDest));
      }
    } else {
      var h = destination.$$hashKey;
      if (isArray(destination)) {
        destination.length = 0;
      } else {
        forEach(destination, function(value, key) {
          delete destination[key];
        });
      }
      if (isBlankObject(source)) {
        // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
        for (key in source) {
          destination[key] = copy(source[key], null, stackSource, stackDest);
        }
      } else if (source && typeof source.hasOwnProperty === 'function') {  //挨個拷貝鍵值,形成遞歸拷貝
        // Slow path, which must rely on hasOwnProperty
        // 慢路徑,必須依賴於對象擁有hasOwnProperty方法
        for (key in source) {
          if (source.hasOwnProperty(key)) {
            destination[key] = copy(source[key], null, stackSource, stackDest);
          }
        }
      } else {
        // Slowest path --- hasOwnProperty can't be called as a method 
        // 更慢的路徑,hasOwnProperty不能被當成對象的方法調用的時候
        for (key in source) {
          if (hasOwnProperty.call(source, key)) { //var hasOwnProperty = Object.prototype.hasOwnProperty;
            destination[key] = copy(source[key], null, stackSource, stackDest);
          }
        }
      }
      setHashKey(destination,h);  
    }
  }
  return destination;
}

技巧5:構造遞歸函數。用遞歸的方式處理問題的條件在於,能把問題轉化為規模縮小了的同類問題的子問題。缺點,效率較低。理論上任何遞歸問題,都可以用循環來完成。

對象是否相等,angular.equals

function equals(o1, o2) {
  if (o1 === o2) return true;
  if (o1 === null || o2 === null) return false;
  if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
  var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
  if (t1 == t2) {
    if (t1 == 'object') {
      if (isArray(o1)) {  //數組,等每個鍵值遞歸調用
        if (!isArray(o2)) return false;
        if ((length = o1.length) == o2.length) {
          for (key = 0; key < length; key++) {
            if (!equals(o1[key], o2[key])) return false;
          }
          return true;
        }
      } else if (isDate(o1)) { //Date,轉成時間戳比較
        if (!isDate(o2)) return false;
        return equals(o1.getTime(), o2.getTime());
      } else if (isRegExp(o1)) {  //正則式,轉成字符串比較
        return isRegExp(o2) ? o1.toString() == o2.toString() : false;
      } else {
        if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
          isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
        keySet = createMap();
        for (key in o1) {  //對象屬性的比較,也是遞歸調用,遍歷第一個對象所有的屬性,發現有不等的地方,立即返回false
          if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
          if (!equals(o1[key], o2[key])) return false;
          keySet[key] = true;
        }
        for (key in o2) { //遍歷第二對象,繼續檢查,發現兩個對象有不等的地方,立即返回false
          if (!(key in keySet) &&
              key.charAt(0) !== '$' &&
              isDefined(o2[key]) &&
              !isFunction(o2[key])) return false;
        }
        return true;
      }
    }
  }
  return false;
}

上一期:angular源碼分析:angular中的依賴注入式如何實現的
下一期:angular源碼分析:angular的源代碼目錄結構
ps:也許這個該先講,可能有同學看到一堆文件,不知從何看起。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM