JavaScript:函数链接或属性访问?

时间:2014-10-01 20:24:46

标签: javascript function object

如果有人可以选择以某种方式设计东西,那么通常更好(通过性能,可维护性,可读性,功能等......)进行功能链接或使用对象属性访问器进行分层访问为什么?

它完全取决于用例,还是一般优越的?

示例:

var foo = bar.get('parent').get('child').get('grandChild')...

// vs

var foo = bar['parent']['child']['grandChild']...
// or
var foo = bar.parent.child.grandChild...

还是有更好的方法来完成它吗?

请注意,由于源缩小,使用不是有效JavaScript标识符的属性值等,点符号.可能并不总是可行。

编辑:

由于这太过于通用,那么更具体的例子呢......

考虑以下内容......

var animals = {
    dogs: {
        'Chloe': {
            name: 'Chloe',
            fetch: function() {},
            rollover: function() {},
            pups: {
                'Charlie': {
                    name: 'Charlie',
                    fetch: function() {},
                    rollover: function() {}
                },
                'Lily': {
                    //...
                }
            }
        },
        'Toby': {
            //...
        }
    },
    cats: {
        'Fido': {
            name: 'Fido',
            meow: function() {},
            kittens: {
                'Mia': {
                    name: 'Mia',
                    meow: function() {}
                },
                'Shadow': {
                    //...
                }
            }
        }
    }
}

请注意,这是尝试显示层次结构并且不显示任何原型继承,尽管它可能存在(并且可能在示例中)。

在如图所示的层次结构中,可以使用属性访问来获取某个小狗,如下所示

var myPup = animals.dogs['Chloe'].pups['Charlie'];
// or
var myPup = animals.dogs.Chloe.pups.Charlie;
// or
var myPup = animals['dogs']['Chloe']['pups']['Charlie'];

这是理想的吗?或者使用函数访问小狗有显着的好处

var myPup = animals.dogs('Chloe').pups('Charlie');

1 个答案:

答案 0 :(得分:2)

我认为您使用错误的工具来处理错误的工作。像你这样对象的深度嵌套是数据建模的一个非常糟糕的选择。这是您的数据模型的样子:

+ animals
|
+--+ dogs
|  |
|  +--+ Chloe
|  |  |
|  |  +--+ Charlie
|  |  |
|  |  +--+ Lily
|  |
|  +--+ Toby
|
+--+ cats
   |
   +--+ Fido
      |
      +--+ Mia
      |
      +--+ Shadow

这种数据模型称为hierarchical model,因为数据以层次结构的形式排列。

分层数据模型不是一个好的数据模型,因为它没有提供access path independence。这仅仅意味着(由于数据的建模方式),在不知道其访问路径的情况下找不到给定的对象是不可能的。

例如,Lily的访问路径为animals.dogs.Chloe.LilyToby的访问路径为animals.dogs.Toby。访问路径独立性很重要,因为这意味着您不必担心对象在数据模型中的位置。这使编程更简单,并确保在相同的时间内访问每个对象。

在上面的示例中,访问Lily所需的时间比访问Toby所需的时间长,因为它更深一层。为了说明访问路径依赖如何使编程更加困难,请考虑以下代码:

function findParentOf(animal) {
    var dogs = animals.dogs;

    for (var dog in dogs)
         if (dogs[dog].pups.hasOwnProperty(animal))
             return dog;

    var cats = animals.cats;

    for (var cat in cats)
         if (cats[cat].kittens.hasOwnProperty(animal))
             return cat;

    return null;
}

现在想象你有许多不同类型的动物,如狗,猫,兔子,仓鼠等。你能看到你会有多少代码吗?

事实上,您的数据模型也有很多冗余。例如,每个对象都有一个name,即使它的键具有相同的值。此外,每只狗和小狗都有fetchrollover功能,每只猫和小猫都有meow功能。

使用正确的抽象可以很容易地概括所有这些。例如,这就是我要做的事情:

function animal(kind, parent, name) {
    return {
        kind: kind,
        parent: parent,
        name: name
    };
}

var dog = animal.bind(null, "dog", null);
var pup = animal.bind(null, "dog");

var cat = animal.bind(null, "cat", null);
var kitten = animal.bind(null, "cat");

var animals = [
    dog("Chloe"),
    pup("Chloe", "Charlie"),
    pup("Chloe", "Lily"),
    dog("Toby"),
    cat("Fido"),
    kitten("Fido", "Mia"),
    kitten("Fido", "Shadow")
];

这种数据建模方式称为relational model。关系模型最好的一点是它具有访问路径独立性。因此,您可以轻松找到对象并执行其他任务。例如:

function findAnimal(name) {
    return animals.find(function (animal) {
        return animal.name === name;
    });
}

function findParentOf(name) {
    var animal = findAnimal(name);

    if (animal) {
        var parent = animal.parent;
        return parent && findAnimal(parent);
    }
}

function findChildrenOf(name) {
    return animals.filter(function (animal) {
        return animal.parent === name;
    });
}

现在我们有了访问路径独立性,我们可以轻松找到对象。例如:

var lily = findAnimal("Lily");
var toby = findAnimal("Toby");
var fido = findParentOf("Shadow");
var miaAndShadow = findChildrenOf("Fido");

最后,当谈到fetchrollovermeow函数时,您可以按如下方式定义它们:

function fetch(animal) {
    if (animal.kind === "dog") {
        // do something
    }
}

function rollover(animal) {
    if (animal.kind === "dog") {
        // do something
    }
}

function meow(animal) {
    if (animal.kind === "cat") {
        // do something
    }
}

甚至更好,将它们放入命名空间。感谢Richard Simpson给了我这个想法:

dog.fetch = function (animal) {
    if (animal.kind === "dog") {
        // do something
    }
};

dog.rollover = function (animal) {
    if (animal.kind === "dog") {
        // do something
    }
};

cat.meow = function (animal) {
    if (animal.kind === "cat") {
        // do something
    }
};

然后你可以这样做:

dog.rollover(findAnimal("Charlie"));

function composition

var rolloverDog = compose(dog.rollover, findAnimal);

function compose(f, g) {
    return function (x) {
        return f(g(x));
    };
}

rolloverDog("Charlie");

只是我的两分钱。希望这会有所帮助。