我在写程序的时候,打算定义数据类型时,经常碰到一个疑惑,到底应该使用基本数据结构封装它,还是使用函数来封装它。
例如封装一个类型Student:
数据版本
function makeStudent(name, age)
{
return {name: name, age: age};
}
function getName(student)
{
return student.name;
}
function getAge(student)
{
return student.age;
}
函数版本
function makeStudent(name, age)
{
return function(method)
{
if (method == "getName") {
return name;
}
else if (method == "getAge") {
return age;
}
}
}
function getName(student) {
return student("getName");
}
function getAge(student) {
return student("getAge");
}
第一个小例子中,makeStudent使用一个Map来封装对象。第二个小例子中,使用函数来封装对象。
这两个小例子都能完成任务,且对于上层使用者来说是完全透明的。然而它们各自的优势,以及使用场景在哪里呢?
首先可以直观看到的是,而且数据版的student的内部数据是全部公开的,任何人都可以看到它的所有字段内容。
而且使用数据版的student有被修改的风险。例如以下程序:
let student = makeStudent("zhangsan", 20);
student.name = "lisi";
上面的例子中,原本student的名字是zhangsan,却可以被改名为lisi。
而使用函数版的student,却没有这些担忧。事实上,函数版的student更接近OOP中的class,因此能做到很多class能做到的事情。
比如我们可以实现多态。
如何理解呢?假设需要再定义一个Teacher类型。
function makeTeacher(name, sex)
{
return function(method)
{
if (method == "getName") {
return "teacher:" + name;
}
else if (method == "getSex") {
return sex;
}
}
}
function getSex(teacher)
{
return teacher("getSex");
}
这个例子中多态的特性主要表现在getName这个函数上:
let teacher = makeTeacher("老师A", 1);
let student = makeStudent("学生A", 20);
let name1 = getName(teacher);
let name2 = getName(student);
调用同样的函数,teacher和student的行为是不一样的。这或许就是函数式的多态吧。
而上面的各种make函数,其实就是函数式版本的class,但更加轻量,更加灵活。