【JS迷你书】我的 JavaScript 世界观

5,007 阅读7分钟

我们都生活在主观的世界里,但真实世界却是个复杂系统。

对于一个非线性系统来说,用任何线性思维去理解都会所偏颇。

用《失控》的观点来说,对于非线性系统,你只有运行起来才知道它具体会是什么。

话虽如此!

《好好学习》一书中,一个重要假设就是:复杂现象背后都是由几个简单的规律所主导的

个人认为 JS 也是属于这种情形的。

JavaScript(ES3)为啥是这样?

也许只有 JavaScript 的作者才清楚吧,毕竟他只花了 10 天就发明了这个强大的存在。

本文不想去回顾历史,也不是去还原作者的创作思路。而是以尝试以自己的思路去理解 JS

希望本篇文章,能给一些初学者点启发,尤其对于以类为主导的语言出身者(比如 Java 使用者)。

除此之外,本文再无所求。

JS 的发明者 Brendan Eich 说这门语言主要有两大特性:一等函数对象原型

一等函数,我认为这个才是 JS 最为核心的东西。

以函数是一等公民为出发点,就能解释 JS 为啥有那些难点。

什么是一等函数呢(First-class function)?

函数是一等公民是说,函数被设计成了“值”。

值能干什么它就能干什么。

值可以存进某个变量,或者某个结构,那么函数表达式声明就是理所当然的。

值除了可以被赋予某个变量,也可以单独存在。比如 "x",所以函数就可以是匿名的。

值可以作为参数传入函数,函数也可以,因此在 JS 中回调的实现显得那么自然。

值可以作为返回值,函数也可以。这一点就非常重要了,因为这直接导致闭包这一机制必须存在。

值可以随时用,也就是动态的,函数也可随时用,因此匿名函数自执行,我觉得非常合理。

除了函数之外还有其他一等公民吗?

函数是,数组是,对象也是。他们都体现了“值”的特性。

因此在 JS 中,有两句话,特别有名:

JavaScript 的一切都是对象,

JavaScript 的一切也都是值。

二者在 JS 中实现了对立统一。

知道这些后,也许你对很多东西就不再迷糊了,反而会越合计越合理。

下面将举例来说明,为啥 JS 中有那么多“奇怪”的东东。

1. 函数做为参数

比如:

setTimeout(function({}, 1000);

还记得当初学习JS时,感觉很迷糊。函数竟然能当参数。

setTimeout(later, 1000);

你还记得,很多书都告诉你不要写成 later() 吗?

后来我认识到函数是一等公民后,学习 es5 时,也就不再迷糊:

[123].map(function(valuereturn value * value; });

函数能当参数传递,JQuery 对它的使用可以说达到了极致。具体应该不用举例了吧。

2. 函数作为返回值

函数作为返回值这块儿,直接导致了闭包的存在。

当然这不是充要条件,但可以作为一个理解闭包出发点。

我们知道,函数基本作用就是作为一个可复用代码的封装,有输入,有输出。

比如:

function sum(a, b{
  return a + b;
};

但是当函数竟然还能返回函数时,有趣的事情就发生了:

function sumCreater(x{
  return function(y{
    return x + y;
  };
}
var sum10 = sumCreater(10);
sum10(12)// 22

函数能返回函数是好事,但是被返回的函数最起码应该能运行才行。

你要运行的话,内部引用的外部变量自然而然就该能被找到。

不然怎么办?难道你想让浏览器直接报错?这个机制就是闭包。

这里具体不展开,因为我只是在搭建世界观。

关于函数能当返回值,能当参数。这就涉及了高级函数概念。

因此也有了很多函数式编程的基础知识,比如柯里化,偏函数,函数组合等等。

3. 匿名函数调用表达式

(function({})();

函数是可以调用的,这没啥可说的。那么声明个函数,直接运行一下,这应该一点也不奇怪。

4. 对象

一等函数是核心,之所以这么说,也因为它对 JS 支持面向对象编程机制起到了关键作用。

一个对象,应该有属性,有方法。因为函数是值,所以下面的代码,应该很亲切:

var object = {
  sayfunction({
    console.log('hello world!');
  };
};

5. this 问题

因为对象的方法,要经常用到自己的属性,this

是应该必须有的,不然用什么指代当前这个对象本身呢?

var object = {
  name'laoyao',
  sayfunction({
    console.log(this.name);
  };
};

那么这里就涉及到一个问题,this 是在函数语义下的存在。

假如直接调用函数,this 指向什么呢?

我又可以不可以动态修改一个函数 this 指向呢?

以上就是 JSthis 指向问题。

6. 对象复用机制

你有个东西,我觉得好,想为我所用。

办法1,我去偷或者借,JS 中可以 callapply

办法2,我 copy 一份,这就是 JS 中的混入(mixin)。

办法3,我认你为爹。你都是我爹了,我用你的东西,还不行吗?这是 Java 的世界观。

办法4,我当你是我的智囊。我解决不了问题,找你来想办法。

办法3和办法4,就是两种复用思想,一个是继承,一个是委托。

JSprototype,就是委托的机制。请参考《你不知道的JavaScript》上卷。

7. new 操作符

我总不能每使用一个相似的对象时,就声明一个吧。

因此必须有种方式,通过类似“模板”的形式来做。这就是 new 的作用。

JS 中的 newJava 中的 new,表层含义是一样的,都是用于生成对象实例。

然而,JS 中的 new 在我看来只是封装了对象间的委托关系罢了。

后记

上面总总就是我对 JS 的世界观。

为啥要用“世界观”这个词语呢?因为我最近在看一本书,《世界观》。

书中说,对世界的看法和信念,虽是一片一片的,却是相互联系的,是能整合到一起去的,从而构成完整的拼图。

比如你相信世界是以地球为中心的,那么就可以解释物体为啥会向下落。

同样,假如你相信万有引力,也可以解释物体为何向下落。

如果采用某种方式,能让自己觉得一些知识是自然而然,就本该是那样的,也许会更好地对其吸收和利用。

当然,采用不同语言世界观,就会形成不同的编程范式。

就像有人喜欢使用面向对象编程来使用 JS,而有人喜欢函数式编程那样。

说到编程范式,提下设计模式。

面向对象有面向对象的设计模式,函数式编程也有自己的设计模式。

解决问题的动机是一样的,但在 Java 中的复杂形式,而在 JS 中,却简单了很多。

这跟 JS 函数是一等对象是密不可分的。

提到了设计模式,最后说句 JS 的原型继承方式,就是合成复用模式。

往往“有一个”比“是一个”的方式更好。

所以,你想有钱,不一定需要是富二代,你可以傍个大款。

所以,你想认识世界,不一定需要自己变成旅行者,你可以上知乎问“周游世界是一种怎样的体验?”。

所以,你想干成大事,不一定需要自己能力多强,你只要有几个聪明人为你所用就行了。

所以,借刀杀人、狐假虎威以及借鸡下蛋都是聪明人最爱使用的方法。

。。。

希望你也能找到一种方式,让你产生 JS 就该是这样的一种错觉。

本文完。

《JavaScript 迷你书》传送门,全面夯实基础

掘金收藏