ちょっと話題の記事

積極的に利用したい AngularJS グローバル API

2014.05.16

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

angularjs125title

本記事で使用している AngularJS のバージョン

1.2.16

フレームワークが提供している機能は積極的に使いたい

AngularJS の標準モジュール (ng module) には、サービスやディレクティブといったメイン機能以外に、便利な関数群のコンポーネントが用意されています。

ng(core module) Global APIs
https://docs.angularjs.org/api/ng#function

AngularJS を利用している以上は、他所のライブラリやフレームワークを極力使わずに、依存性を減らしてアプリケーションの純度を高めたいと考えています。たとえば、真偽判定 ( オブジェクトの評価 ) や配列走査する場合においても、提供されている API を積極的に利用するよう心掛けたいものです。

というわけで、実際に利用している API をいくつか紹介したいと思います。

真偽判定

評価対象のオブジェクトが null や undefined でないかを確認したいとき、以下のメソッドを使うことによって対応することができます。

  • angular.isDefined
    参照が定義されていることを評価
  • angular.isUndefined
    参照が定義されていないことを評価
  • angular.equals
    2 つの値 ( オブジェクト ) が等価であることを評価

凡例 1

var noUndefinedAndNull = function(value) {
    return angular.isDefined(value) && !(angular.equals(value, null));
};

angular.isDefined, angular.isUndefined の中身は以下のように実装されています。

angular.js

…

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

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

…

未定義か否かをチェックするためのメソッドなので、間違って null や '' ( 空文字 ) をチェックしないように注意しましょう。

凡例 2

angular.isUndefined(undefined); //true
angular.isUndefined(null);      //false
angular.isUndefined(NaN);       //false
angular.isUndefined('');        //false
angular.isUndefined(0);         //false
angular.isUndefined(1);         //false


angular.isDefined(undefined);   //false
angular.isDefined(null);        //true
angular.isDefined(NaN);         //true
angular.isDefined('');          //true
angular.isUndefined(0);         //true
angular.isUndefined(1);         //true

参考

JavaScript:undefined値の判定 - 泥のように
http://blog.tojiru.net/article/205007468.html

angular.equals の中身は以下のように実装されています。

angular.js

…

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)) {
                return isDate(o2) && o1.getTime() == o2.getTime();
            } else if (isRegExp(o1) && isRegExp(o2)) {
                return o1.toString() == o2.toString();
            } else {
                if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) || isArray(o2)) return false;
                keySet = {};
                for(key in o1) {
                    if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
                    if (!equals(o1[key], o2[key])) return false;
                    keySet[key] = true;
                }
                for(key in o2) {
                    if (!keySet.hasOwnProperty(key) &&
                        key.charAt(0) !== '$' &&
                        o2[key] !== undefined &&
                        !isFunction(o2[key])) return false;
                }
                return true;
            }
        }
    }
    return false;
}

…
[/javascript]

<p>厳密な比較 ( ===, !== ) をしたり、対象のオブジェクトの型をチェックしたりと、細かく比較していることが分ります。このメソッドを使用することによって比較演算子の選択を間違える可能性が低くなり、多人数開発のときにルール化することができて、品質が保たれることが期待できます。</p>

<p>ためしに色々比較してみました。</p>

<p><strong>凡例 3</strong></p>

angular.equals(undefined, undefined)       //true
angular.equals(undefined, null)            //false
angular.equals(undefined, NaN)             //false
angular.equals(undefined, '')              //false

angular.equals(null, null)                 //true
angular.equals(null, NaN)                  //false
angular.equals(null, '')                   //false

angular.equals(NaN, NaN)                   //true
angular.equals(NaN, '')                    //false
angular.equals('', '')                     //true

angular.equals(/[A-Z]/g, /[A-Z]/g)         //true
angular.equals(/[A-Z]/g, /[a-z]/g)         //false

angular.equals($scope.$parent, $rootScope) //コントローラの階層次第で結果が異なる

配列走査

ライブラリやフレームワークを使用せずに配列走査するときには、対象オブジェクトが配列か連想配列化かによって、走査方法を意識する必要があります。

凡例 4

Array.prototype.hoge = function() {};

var item;
var testArray = ['a', 'b', 'c'];
var testObject = {
    a: 'AAA',
    b: 'BBB',
    c: 'CCC'
};

for (item in testArray) {
    $log.debug('test 1 : ', item);
    //0
    //1
    //2
    //hoge
}

for (item in testObject) {
    $log.debug('test 2 : ', item);
    //a
    //b
    //c
}

参考

JavaScript の配列と連想配列の違い - IT戦記
http://d.hatena.ne.jp/amachang/20070202/1170386546

また、多人数開発のとき実装方法に一貫性がないと品質は保たれませんので、できれば配列も連想配列も同様かつ安心な方法で走査したい…といったときに助けてくれるのが angular.forEach です。

凡例 5

Array.prototype.hoge = function() {};

var item;
var testArray = ['a', 'b', 'c'];
var testObject = {
    a: 'AAA',
    b: 'BBB',
    c: 'CCC'
};

angular.forEach(testArray, function(item, key) {
    $log.debug('test 3 : testArray[' + key + '] = ' + item);
    //testArray[0] = a
    //testArray[1] = b
    //testArray[2] = c
});

angular.forEach(testObject, function(item, key) {
    $log.debug('test 4 : testObject.' + key + ' = ' + item);
    //testObject.a = AAA
    //testObject.b = BBB
    //testObject.c = CCC
});

安全に走査されていることが確認できます。

angular.forEach の中身は以下のように実装されています。

angular.js

function forEach(obj, iterator, context) { var key; if (obj) { if (isFunction(obj)){ for (key in obj) { // Need to check if hasOwnProperty exists, // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) { iterator.call(context, obj[key], key); } } } else if (obj.forEach && obj.forEach !== forEach) { obj.forEach(iterator, context); } else if (isArrayLike(obj)) { for (key = 0; key < obj.length; key++) iterator.call(context, obj[key], key); } else { for (key in obj) { if (obj.hasOwnProperty(key)) { iterator.call(context, obj[key], key); } } } } return obj; } … [/javascript]

細かくチェックしていますね。

絞め

以上が私が積極的に使っている API でした。意外としっかりした機能が提供されていると思われた方もいらっしゃるのではないでしょうか。大人数で寄ってたかって AngularJS アプリを開発する必要があるときには、ぜひ使ってみてください。