JavaScript 王国旅行的继续,连对象也没有怎么玩转“封装,继承,多态”?


本文为 码农翻身 微信公众号投稿,未经码农翻身同意禁止转载


又见 JSON 酒馆

Java 小王子在 JavaScript 王国待了也有一段时间,这里虽然不像 Java 帝国那样规范严苛,但也因此千奇百怪,五光十色。

要说小王子最喜欢待的地方,那还是人来人往的 JSON 酒馆,不仅有上好的酒菜,还有机会认识到各式各样的人。这不,一来二去他已经和上回认识的眼镜官员成了朋友,甚至私底下还称兄道弟的。
(参见上回)

今天小王子又和眼镜大哥一起约来吃酒。才寒暄了几句,筷子还没动,酒馆门口就发生了一点骚动。只见来了一位看起来像是教书先生的精瘦男子,旁边还有些随从。
“...... 大哥,这人什么来头,气度不凡啊。”小王子悄声向旁边的眼镜官员问到。
“哈哈,他是函数式大主教,最近他们教派的信众激增啊,真是风水轮流转。”
“函数式?以前随父亲经商途中是听说过他们的事情,好像非常古老而且高深莫测啊,据说只有学者和虔诚的教徒才会加入他们,怎么最近也接收新人了?”
“小弟果然是见多识广,不错,在很久以前的确是这样,不过为何有大批新教徒这种事情也不在我的管辖范围内啊,所以也不太清楚。但是毕竟我是本国语言规范审查官,还是与他打过一些交道。不妨我们邀请他来一起喝酒,你亲自问他。”
小王子本来听到眼镜大哥也不了解情况正有些失望,突然得知可以直接面对面打探对方的底细,顿时兴奋了起来。
“那太好了!”

“这不是眼镜老弟么,别来无恙啊。”
“主教兄,甚好甚好,要不这顿我请?小二!”
饭菜上桌,互相客套了几句之后,话题就开始了。
“主教兄,这是我最近认识的朋友,年纪轻轻就周游四方,他有些事情要问你。”
“哦?”函数式主教把目光放了过来,“你有何打探?”
“久问贵教派向来神秘,为何最近有如此多的新教徒加入呢?”
“哈,这个嘛 ... 现在的年轻人都不喜欢条条框框,本教向来以简洁强大著称,自然就受欢迎了。”

小王子心想,这个主教倒也是有话直说自卖自夸,不过难道没了面向对象这种强大的武器,他还能变出怎么样的花儿来?
“那还敢请教大主教,依你看要怎么实现 Animal, Cat, Dog 这些对象呢?”想了一会儿,小王子认为不如直接发问。
“这个嘛,本教派并无对象这种说法,不过如果你愿意,也可以构造一个对象出来,只需要......”
“我们不用对象~”,还没等着主教说完,旁边的妹子突然发话了。
“哦,小莱,你来说吧。”这位被叫做小莱的少女像是主教的助手,看起来深得主教信任。
“嘻嘻,主教大人说的太复杂了,其实实现你说的那些根本不用什么对象。”
“哦?那该怎么做?”小王子顿时来了兴趣。


函数的翻身

“你想啊,搞出来这些猫啊狗啊的,不就是想让他们都可以吃东西么?干嘛要封装到一个对象中,太压抑了。”

let animal = { name: "animal" };

function eat(animal) {
    console.log(animal.name + " is eating");
}
eat(animal); // animal is eating

哦!Java 小王子恍然大悟,原来 JavaScript 里的方法根本不用强制放在对象中去声明,在这里函数已经翻身做主人,成为了一等公民,再也不用困在对象的牢笼中,声明后就可以直接使用。
“也就是说,猫狗都可以直接拿来调用咯?”

let dog = { name: "dog" };
let cat = { name: "cat" };

eat(dog); // dog is eating
eat(cat); // cat is eating

函数式“封装”

“是的呢~”小莱的脸上还是一样的笑容。
这样的话,继承和多态就变得毫无用武之地。函数直接操作的是数据,数据不同,结果自然就不同,好似“多态”的体现,而且由于函数成了通用的东西,无需继承大家都可以使用,如有需要“重写”,就干脆再写一个函数就行了。而且 JavaSript 是动态类型,根本不需要再抽象出来一个“接口”来统一描述某种抽象。
“嗯... ”小王子总觉得哪里不对,好像少了点什么,但又说不上来。“这是不是太自由了点... eat 本来只能是 animal 才能使用,现在任何东西都能用。这根本没有封装啊!”
“是的呢~ 只要有 name 都可以用哦~这样不是更好么?不过说到封装,我们的封装可更厉害哦~”

function createAnimal(name) {
    let animal = {};
    animal.name = name;
    return animal;
}
// 或者直接
function createAnimal(name) {
    return { name: name };
}
eat(createAnimal("human")); // human is eating

更奇怪的封装

没错,封装说到底是为了屏蔽细节实现,防止外界干扰。这么一来,外界就不需要了解 eat 和 creatAnimal 的实现原理。小王子很是吃惊,但不得不承认这是一种有效的办法。
“嗯~而且不仅可以封装一些数据,还可以对函数进行封装呢~”还没等小王子缓过神儿,轻快的声音又把他拉回现实。“我们函数式教派里面叫它闭包。”

function generateEatFunction(description) {
    function eat(animal) {
        console.log(animal.name + description);
    }
    return eat;
}

// 或者直接
function generateEatFunction(description) {
    return function(animal) {
        console.log(animal.name + description);
    }
}

let eatZH_CN = generateEatFunction(" 在吃东西~");
let xiaolai = createAnimal("小莱");

eatZH_CN(xiaolai); //小莱 在吃东西~

“啊?!”这次小王子彻底呆住了,信息量太大,他没有想到函数还可以这么使用,竟然直接作为返回值返回,还可以“封装”一些值进去。
看到呆住的小王子,小莱解释了起来,“嘿嘿,第一次见到的人都是这样子,没事,习惯了就好。在我们函数式里面呢,函数中可以嵌套函数,内层的函数经常需要引用外层函数中的一些值,而且可以把这个内层函数作为返回值呢~我肯定希望这个函数引用的外层值可以继续使用啊,这样多方便啊。所以 JavaScript 就对其进行了处理,如果返回的函数被引用,JavaScript 就会同时保留这个函数所引用的东西,并不会回收它们(比如这里的 description)。你们面向对象的人其实也经常在方法中声明局部变量是吧,但是你们并不会把一个函数返回出去,自然也就不会考虑方法执行完毕之后要保持哪些局部变量,直接回收掉就好了。”
“哦,这样啊...”小王子还是一脸似懂非懂的样子。

“来,我们做一下分解动作,首先 description 在 generateEatFunction 内部是一个局部量。”

function generateEatFunction(description) {
    // description == " 在吃东西~"
    ...
}

“然后内层的函数使用了这个局部量。此时一切都没有问题。”

function generateEatFunction(description) {
    function eat(animal) {
        // 这里引用了 description
        console.log(animal.name + description);
        // 相当于
        // console.log(animal.name + " 在吃东西~");
    }
    ...
}

“现在精彩的地方来了,我把这个函数返回了出去~当然我不希望这个局部变量被回收,我们依然希望它符合我的预期想法。”

function generateEatFunction(description) {
    function eat(animal) {
        // 这里引用了 description
        console.log(animal.name + description);
        // 相当于
        // console.log(animal.name + " 在吃东西~");
    }
    return eat;
    // 希望 eat 的行为可以像这样:
    // function eat(animal) {
    //     console.log(animal.name + " 在吃东西~");
    // }

}

let eatZH_CN = generateEatFunction(" 在吃东西~");

// 谢天谢地 JavaScrpit 的确没有回收掉那个局部量。
// 效果如同下面这样:
// eatZH_CN = function eat(animal) { console.log(animal.name + " 在吃东西~"); }
// eatZH_CN 成功指向了我们生成的函数。
// 接下来就是正常的调用啦~

eatZH_CN(xiaolai); //小莱 在吃东西~

迈向高阶

“嘿嘿,还不止这样呢~再给你看个东西~”小莱的话匣子一打开就停不下来,由不得小王子发呆,自顾自的继续介绍起来。“函数还可以作为参数传呢~ 这些都叫高阶函数~”

function each(list, fun) {
    for(let i = 0; i < list.length; i++){
        console.log(fun(list[i]));
    }
}

let animals = [dog, cat, xiaolai];

each(animals, eat);
// dog is eating
// cat is eating
// 小莱 is eating
// 

each(animals, eatZH_CN);
// dog 在吃东西~
// cat 在吃东西~
// 小莱 在吃东西~

“哇!传递进来的函数竟然真的就可以直接把它当作函数来使用!这太神奇了!”
“当然这里的 each 函数还不够函数式,其实可以用递归实现啦~毕竟函数式里面不提倡用数组下标来做遍历,但是到了 ES6 才有尾递归优化,所以... ”小莱还在说着......


未来

“咳咳,小莱你够了。客人都被你吓到了。”这时,在旁边默默看着的大主教说话了。
小王子还在震惊之中,不过毕竟是皇族,阅历丰富,还是悟出了一点门道,“我大概明白了一点,由于 JavaScript 是动态类型,其实无论是函数还是对象,在这里都可以做为一个值来传递。函数式里面偏向对值直接进行处理,通过对这些值的传递和组合,就可以组装实现更高级的功能。上面的那个 each 方法也展现出来另一种“多态”的体现。”
“不错不错,我看你这位朋友的来头可不一般啊,新加入的信徒大多都经过漫长的适应期才能理解,他却立即悟出这些道理来。”大主教对小王子赞赏有加,或许以他的智慧已经识破了小王子的身份。
“不不不,还是因为小莱妹妹讲的好啊。”小王子脸红的说道。
“这还不是多亏了我们的原型才能自由灵活的实现各种编程范式么。”眼镜大哥也参合了起来。
“哈哈哈,你又在自夸了。”
“来,说了这么多 eat,不说了,吃菜吃菜~”

看来编程世界上还有这么多种形态,小王子下次要去哪里,又会见识到怎样的东西呢?或许也能给他的 Java 帝国带回一些新鲜的血液?


登场人物名:
小莱 == lambda

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容

  • 'A' 新增 'D' 删除 'M' 修改 'R' 替代 'C' 冲突 'I' 忽略 '?' 未受控 '!' 丢失,...
    丶逝水流年阅读 2,081评论 0 5
  • 枯死的树 拉大锯扯大锯 伐回 劈成小段四半 木头 堆在墙角似山 木头挨着木头 不说话 不会动
    夏霖捷阅读 122评论 0 0
  • 回了趟家,在家带了几天孩子,看了点书,就这样。连向来懒惰的xb都忍不住发来消息催问我写摘要的事,咳,其实懒懒的真好...
    洁瑜GW阅读 151评论 0 0
  • 人总会在失败中不断成长。一次失败算不了什么,因为你肯定不会再在同一个地方失败。而我,却不免嘲笑自己。是的,我...
    梦见我梦见你梦她i阅读 347评论 3 2
  • 869560023876017
    Nightmare梦_2654阅读 169评论 0 0