JavaScript 学习笔记(1):关于函数、对象以及面向对象

来源:互联网 发布:淘宝手机版怎么贷款 编辑:程序博客网 时间:2024/05/20 22:03

JavaScript 学习笔记(1):关于函数、对象以及面向对象

最一开始以C++、Java、C#之类的语言的思维的方式去理解JavaScript,结果各种被瞎眼。最终接触得稍微多了点之后,才终于回过神来,原来JavaScript根本不是我想象中的那种东西。

在这里记录一下自己学习中的感想。如果能帮上其他和我一样刚刚入门的新手或者得到老手的指正和建议,这都是再好不过的事情了。

因为是学习笔记而不是教程,所以面向的读者是“至少稍微用过JavaScript一点点”的人。

以下正文。

1. 函数是一种数据

不要把函数和方法与数据分开来看。在JavaScript里面,函数本身也是一种数据。或许这样的写法更让人感到熟悉:

function() sayhello{    alert("hello world")}

但是这个是骗人的。真相是这样:

var sayhello = function(){    alert("hello world");}

函数是一种数据,它和字符串、Object一样,是可以存储在变量里面的。

2. 对象其实就是字典

暂时忘记“类”那个东西吧。

JavaScript里面的对象,其实就是某种以字符串为键,任意数据为值的字典。

因此,用索引器去访问obj['sayhello']()和直接用点符访问obj.sayhello()的效果是相同的。(当然,因为底层实现原理,点符访问通常是要比索引器访问快一点点点的,不过真的只有一点点点点。)

因为对象就是字典,所以我们也可以动态地给对象增加字段、改变对象的结构。

另外一个有趣的小技巧是,因为解释器(说不定也有一半是编译器?现在已经傻傻搞不清楚了)的优化,动态地改变对象的结构运行效率是会降低的。尽可能在声明时一步到位才是明智之选。

3. 其实还是有面向对象的

JavaScript自身有一种奇怪的、并不是基于类的继承方式。但是它并不难理解、只要学它的人能暂时忘了C++、Java、C#之类的基于类的面向对象语言的思路。

先忘了那些东西吧……我们先假设一下,如果我们真的没有面向对象,要怎么基于“已有的JavaScript”模拟出来?

3.1 让我们先模拟一个“抽象化-实例化”的“类机制”来试试?

首先,我们应该有个“模板”,它记录了某一类对象的共同特性,比如这样:

Person类的对象,都会具有name、age和sex字段。

考虑一下这么做怎么样?

var Person = function(name,age,sex) {    this.name = name;    this.age = age;    this.sex = sex;}var zhangsan = {};Person.call(zhangsan,"zhangsan",16,"M");//这句话的意思是,让Person这个函数,把zhangsan当做this指针指向的目标来运行。

你会发现,zhangsan这个对象成功地获得了name、age和sex三个字段。这和我们预期的效果“声明某一类对象、并可以通过某种方法实例化它们”是相符的。

说白了就是,我们定义了某个函数。这个函数用函数的形态存储了某一类对象的基本结构信息。如果我们以某种特殊的方式(call)运行这个函数,这个函数就能把某个空对象初始化成某种结构。

于是这样,我们就能理解为什么JavaScript里面新建对象的方法看上去这么奇葩了:

var classA = function(){...}var objA = new classA();

并不是“函数变成类”了,而是“这是一个用于把对象初始化的构造函数”。

3.2 如何方便地进行代码重用?

“继承要怎么模拟呢?”

不不不不,这不是问题的关键,问题的关键是我们不应该模拟“继承”。JavaScript里面只有对象和用于初始化对象的构造函数,没有类。

我们想做的事情是代码重用,而不是非要去“继承”个什么。

换个思路,如果我们有objAobjBobjC,三个对象,其中objBobjC分别有一个字段指向了objA,那么我们通过objBobjC就能访问同一个objA的代码。这样是不是就有的像是继承的意思了呢?

但是这个“超简易继承法”仍然有很多漏洞。接下来我们一个一个指出这些漏洞、并假设有一个完美的字段“__proto__”可以解决这个问题,那么它应该具备什么特性:

  1. 这样书写起来很麻烦
    存在的问题:
    objB.objA.xxx或者objC.objA.xxx这样写很不舒服。既然是“继承来的”,那么我想objB.xxxobjC.xxx这样使用它。
    如果有个完美的字段的话……:
    如果有一个完美的__proto__字段的话,那么__proto__所指向的对象,它的字段一定要能够被原对象直接调用才行。
  2. “objA”的字段最好不要被随意修改
    存在的问题:
    既然一大堆对象指向了objA,那么它如果可以被随意修改的话,世界就乱套了。
    如果有个完美的字段的话……:
    如果有一个完美的__proto__字段的话,那么__proto__的字段对于“继承”它的人来说,肯定是只能读不能写的。如果想写,就自己新建一个同名字段,写到自己身上去。然后读的时候进行检查,如果自己身上有这个字段了,就不要去读__proto__上面的同名字段了。
  3. 不要再“如果”了,__proto__字段就是真的
    打开控制台自己试试吧。

OK,读到这里,“继承”的问题就已经被我们轻而易举的解决了不是吗?

__proto__在JavaScript里面又称作是原型,然后原型也可以拥有原型,这样就会产生一条“继承链”,虽然和Java之类基于类的面向对象思路不同。但是简单的继承也是可以做得到的。

至于想要接口之类的东西……啊,这就爱莫能助了。考虑一下TypeScript吧,能够编译生成JavaScript的有类型检查的脚本语言。在VisualStudio和Webstrom下面用起来的感觉都是超级棒的。

3.3 其他杂七杂八的事情

  1. JavaScript里面每个函数都是有一个prototype字段的,用构造函数初始化一个对象时,它就会把自己的prototype字段赋值给被初始化的对象的__proto__。就像这样,就相当于“继承”了,不需要我们手动操作:

    var Animal = function(){ ... }var Cat = function() { ... }Cat.prototype = new Animal()
  2. 虽然原型这种方式是JavaScript官方给的标准继承方式,但是实际上想要达到继承的效果,还有很多很多“邪门歪道”。比如说:

    1. 简单的JavaScript继承,通过为根类定义create、extends等函数,来达到继承的目的。
      这种语法看上去很漂亮,也很常见,比如说游戏引擎cocos2d-js就是用的这类语法。
    2. Object.create()
      ES5里面新加的继承方式……好像现在并不怎么常见的样子。
0 0
原创粉丝点击