美文网首页敏捷软件开发
疾风式全栈教程(6)-语法中班

疾风式全栈教程(6)-语法中班

作者: 码农田伟 | 来源:发表于2018-05-02 18:11 被阅读0次

    现在我们要开始接触一些较为深入的知识了. 可能不再多作解释, 直接用通用的技术名词, 大家慢慢习惯就好了. 还是那句话, 有不懂的就搜一下.

    简单说一下为什么用中班呢, 因为我们想分三次讲语法, 原来是准备叫初级, 中级, 高级的, 后来想想可能我们讲到高级, 也达不到一般程序员初级的水平, 所以就按照幼儿园的叫法, 叫小班, 中班, 大班吧. 也希望大家能一直保持一颗童心, 永远活泼可爱 ( ^_^ )

    观察我们以前写的内容, 我们可以发现, 有一些是我们自己想和写出来的, 我们可以控制和理解. 有一些直接照着写就能用, 不知道从哪来的. 这部分我们就说是从环境中来的.

    什么是环境呢, 就是我们之前安装的那一大堆软件. 那些都是其他程序员写好的, 所以我们安装后就可以用了. 我们要学会利用别人已经写好的软件, 不用全都自己写. 这样可以节省一些时间.

    但是要用别人写的东西, 我们就要了解别人提供了一些什么功能, 以及使用的方法. 一般软件都会提供使用文档, 常用的软件我们在网上也能搜到很多教程. 

    比如我们之前用的 alert 和 console.log 在chrome里可以用, 因为chrome提供了这些功能. 一般来说我们称这些现成的函数为API. 或者更简单的称为接口.

    另外还有一些不能称为函数的东东,  比如 if else for while 之类,  也是需要有相应的语言环境才能被有效的运行. 并且我们写的代码需要符合环境要求的语法格式. 这些约定就是常说的语法. 环境的设计者按照语法约定来开发运行环境, 我们也按照同样的语法原则来写代码, 双方遵守同样的约定, 互相能认识, 才能正常交流. 就像我们与人沟通, 要用双方都懂的语言一样.

    js作为一门语言, 提供相应的语法约定, 这个约定现在由ECMA委员会负责维护. 称为ECMA Script 标准(简写为es).  chrome按照这个标准开发js运行环境, 我们也按照这个标准写代码, 所以, 虽然不是来自同一个国家, 但是我们写的js代码可以在chrome里正常运行.

    es的标准现在更新快了, 每年一个新版本, 新功能层出不穷. 对于我们来说, 至少要掌握到2015年的版本, 也就是俗称的es6. 可能很多资料还是介绍的更早的es5甚至es3, 也都是可以用的, 但是我们的原则还是哪个简单就用哪个.

    这里有一个问题就是兼容性. 如果双方用的不是同一个版本的标准, 可能就会有一些区别, 有些代码没法正确运行. 好在现在大家都在努力统一浏览器的运行环境, chrome firefox safari opera 微软 这几家浏览器大厂的新产品一般都是符合es6标准的. 如果有什么不确定的地方, 可以在网上查询, 有个网站叫can I use, 提供了很多兼容性数据. 不过在中国, 多数情况下我们考虑chrome就可以了. 所谓国产浏览器也多数用的是chrome的内核, 包括手机上的浏览器. chrome的内核称为webkit, 当然不同版本之间可能还是会有些差别. 没什么特殊情况的话, 尽量用最新版就可以了.

    关于环境问题又讲了好多, 没办法, 作为今天的开发者, 一方面我们可以利用很多过去开发好的产品, 另一方面我们处理环境问题的时间可能比处理自己代码的时间还要多. 希望将来一些混乱的环境配置可以逐渐统一和简化.

    接下来我们开始一些基本的知识, 这部分内容还是非常统一的.

    关键字和标识符

    首先是我们直接就用的这些词 if else for while 都是环境中已经存在的名字, 叫关键字或保留字. 也就是我们不能随便用这些名字, 因为人家已经用了, 我们就不能再叫一样的名字了, 否则就会重名, 分不清谁是谁了. 关于哪些词是已经有的关键字, 网上一搜就有. js的比java的还有多一些, 不过把java的基本都包括了. 另外这些词通常也是计算机领域的高频词, 可以多看几遍, 即使记不下来也要混个脸熟.

    而我们自己起的名字叫标识符. 命名要有一定原则, 用英文字母肯定没有问题, 数字可以加在字母后面. $符号如果愿意折腾的也可以用上, 不过用之前最好查一下文档或者试一下, 看看当前语法是否允许用. 建议用最简单的就好, 又不用记太多规则, 又不容易出问题. 多个单词组成的词, 每个单词首字母大写, 利于阅读. 至于词首是否大写, 看使用情况. 在一些特定的场合还会出现用_或者-连接多个单词的命名风格. 我们只能说, 首先, 越简单越好, 当然要在看了能明白的前提下. 其次, 看一下别人是怎么写的, 照着写一般就可以, 最后, 就是最终都以实际测试结果为准. 始终记住, 实践是检验真理的唯一标准.

    运算符和表达式

    语言环境里还提供了一些常用的操作符.  比如算术操作符, 跟我们在数学课上用的基本一样.  + - * /

    乘法换成了星号, 除法用了斜杠. 总体还是差不多的.

    而像 < 小于号 > 大于号 这些称为逻辑运算符, 意思也是一样的, 比较大小.

    需要注意的两点是:

    判断相等用==  因为赋值用了=符号, 判断相等就只能用==了. 等于的判断看起来简单, 有些时候不注意却很容易出问题. 比较的都是数字的话是没太大问题的, 如果是字符串比较, js里也可以用==来判断两个字符串内容是否一样. 而java里, 字符串用==比较只是比较内存地址, 内容是否一样要用equals函数判断. 如果遇到字符串按内容比较一定要注意.

    字典序 也是和字符串有关, 数字比较大小没什么好说的, 和数学里一样, 但是涉及到字符串时, 就是比较字典序, 也就是挨个比较字母顺序谁先谁后. 排在前面的就算小的.

    除此之外, 一些诸如 != 不等于 <= 小于等于 >= 大于等于 之类都比较简单, 不难理解, 无非是熟悉写法而已.

    用运算符把名称或值连起来, 就成了表达式. 算术运算符当然连接的都是数字, 运算的结果也是数字. 但是+还可以用来拼接字符串, 需要注意(这里也是有一套规则的, 但是现在我们可以不记这些麻烦的规则, 以后我们会讲到使用模板字符串).

    而逻辑运算符, 运算的结果是boolean值, 他们为if后面的的那个判断条件提供了基础素材.

    之所以是基础, 因为他们还可以用关系运算符来组合. 关系运算符就是Boolean值的运算符. 其实就是我们常说的 与 或 非,  分别用符号 &&  ||  ! 表示. 如果对这部分数学知识有印象, 就不难理解, 即使没接触过, 也可以用中文的 并且 或者 不是 来对应替换.

    实际工作中常用的也就这些了. 其他像位运算涉及底层, 较少用到, 开始阶段可以先不关注. ++ -- 和组合赋值 我们也不需要, 一方面没有提供新的功能, 只是一些简写, 另一方面, 不符合值不可变的函数式风格.  见了能认识就好, 实际上你会发现, 没有他们, 我们的生活会更美好.

    另外两个需要提一下的, 一个是求余数的运算符 %, 这个还是比较常用的, 有时候也叫取模运算. 就理解成算除法中的余数就可以了, 真正深入理解涉及到数论这样高深的数学知识, 对于我们实际工作来讲没必要深究.

    另一个常用的就是三元预算符, 实际上是if的一种简写,  格式 

    boolean表达式 ? 真时的值 : 假时的值

    这个在js和java代码中还是经常能看到的. 在kotlin里直接和if合并了.  可以写 

    if(boolean表达式) 真时的值 else 假时的值.

    这些运算符大部分是连接前后两个值的, 称为二元运算符, 和数学中的写法一样. 例外是 ! 是一元运算符, 只对他后面那个值起作用, 而 三元运算符 涉及到三个值. 如果按顺序写一个长表达式, 运算符之间的优先级也和数学上差不多, 先乘除后加减, 先算术运算, 后关系运算, 然后是逻辑运算. 先 非, 后 与 然后 或. 当然其实没必要记这些, 也不要写这样的表达式. 就像数学上一样, 想要哪里优先, 就加上小括号就可以了. 小括号的优先级比他们都高.

    另外我们之前用的赋值符号 = 也算是一种运算符, 不过优先级很低, 总是把其他运算处理完, 最后才赋值的.

    函数

    有了表达式, 我们就可以有函数. 函数可以看成有一定格式的表达式.

    比如 a + b 是一个表达式,

    但是写成 

    function(a, b){ return a + b } 

    就成了一个函数

    当然这是在js里函数的一种写法, 现在在es6里, 我们可以用更简短的写法

    (a, b) => a + b

    俗称箭头函数, 或箭头表达式. 我感觉这样写更好理解, 只是在表达式前面加了个箭头, 之前的括号里把表达式中用到的名称罗列了一下而已. 箭头前面的内容称为参数, 后面的表达式的运算结果是函数的返回值.

    当然, 函数是一个很深刻的概念, 否则也不会有函数使编程这种说法. 计算中的任何概念都可以用函数表示, 也就是一切皆可为函数. 比如,  一个简单的 a,  也可以写成 a => a,  一个以a为参数, 返回a的函数.(参数只有一个时可以不写小括号)

    而 function 和 return 这种写法, 就是把 函数 和 返回 这两个词的英文 明确的写出来了而已.

    这种写法必须要加大括号, 而箭头函数如果只有一个表达式, 可以不写大括号.

    也就是说, 函数可以包含多个表达式, 用大括号围起来, 规定最后一个表达式的结果是函数的返回值.

    由表达式组成的函数是很美好的函数, 因为跟数学上的函数几乎一致. 所以被尊称为纯函数.

    既然有纯函数, 那就说明会有不纯的函数. 比如我们前面用的alert 和 console.log, 很难说他们是由什么表达式组成的, 也不返回什么有意义的值. 所以console里显示的是undefined, 如果一个函数返回undefined, 几乎就可以认定他不是一个纯函数, 数学上的函数都是为了返回有意义的值的, 而undefined代表未定义, 无意义.

    当然, 不纯的函数也是有用的, 我们之前就一直用他们来举例子, 并且不得不说, 有时候用起来还挺方便. 但是为了更美好的软件世界, 我们还是尽可能使用纯函数, 他们的输入和输出(也就是参数和返回值)脉络清晰, 有迹可循, 仿佛水晶般晶莹剔透, 显得那么美好. 对于不纯的函数, 如果确定要用的话就用吧, 毕竟生活不是完美的. 我们不是所有都能控制, 我们只能尽可能把自己能控制的控制好, 让他尽量朝着更美好的方向去发展, 就行了. 古语云, 水至清则无鱼, 是很有道理的.

    语句

    另外, 还有一个我觉得其实不应该出现, 实际上却经常出现的概念就是语句. 简单的说, 就是 if else for while 这些东东 和 他们后面的结构, 就叫语句. 为什么说他们不应该出现呢, 因为这些概念最早是从古老而底层的汇编语言中遗留下来的, 他们既不是函数, 也不是表达式, 显得有些别扭. 其实没有他们我们也能编程, 甚至可能做的更好. 所以在纯粹的函数式语言里是没有语句的. kotlin里也取消了大部分语句, 不过还是保留了循环相关的语句. 而我们用的js和java也还遵循传统, 使用条件和循环语句. 大家知道就好了, 实际工作的话, 影响不是太大.

    命名空间和对象

    接下来我们说一下关于命名空间的问题. 其实还有一些相关的概念, 作用域以及可见性, 这些概念总感觉多少都有点相似, 但要严格区分好像又不一样, 我们先不考虑学术问题. 从实用的角度讲, 我们只关心, 在什么地方能找到自己要找的东西. 至于作用域和可见性的问题我们在以后还要讲.

    为什么alert可以用一个词, 这么短的名字, 而console.log却要用两个词, 中间还要加个点呢. 当然可以把他看成名字就是叫成这样的, 没有什么为什么. 就跟父母给孩子起名一样, 爱叫啥叫啥, 别人管不着. 如果你达到这样的理解程度, 那么恭喜你已经领悟了实用主义的高级境界. 对于像什么System.out.println之类的, 乃至更长的名字都可以来者不惧了. 其实从根本上说, 也确实是这样, 只不过是名字而已, 再长再短都是名字, 都是为了区分, 只要能区分清楚就行.

    嗯, 不过人们总是想要有一些听起来高大上的理论, 才能显得自己很有学问, 最好还能有一些一般人听不懂的专业名词. 于是, 我们有了命名空间, 和对象这些概念. console就是一个对象, 他下面可以挂函数, 或者其他的对象, log就是挂在他下面的一个函数, 所以中间用点来表示他们的关系. 而他上面, 或者说前面, 也是一个对象, 就是window对象, 不用写出来因为console可以帮我们省略掉window对象的名字, 因为他是我们在这个环境里能访问到的最上面一级的对象, 也可以理解成最高, 最大的对象(怎么突然感觉高大上就是说的他啊)通过他我们理论上能找到所有的对象和函数, 只要按正确的路径一路点下去就可以了. 所以alert可以直接写, 因为他直接挂在window上了, 而window因为是默认对象被省略了. 如果我们把alert挂在console上, 也就同样要写console.alert了.

    有没有明白一点呢, 什么是对象呢, 简单的说, 点前面那个就是对象, 更简单一点就是一个名字, 通过他可以找到他上面和下面的对象. 哈哈, 有点像那句经典台词, 上级的姓名和地址我知道, 下级的姓名和地址我也知道. 就简单把对象当成一个联络员或路标吧, 可以点出东西来的东西. 如果我们一定要解释函数和对象这两个概念, 我们就会发现, 函数也是一种对象, 那可能就真的晕了. 我们可以简单先了解一下, 先用起来, 用的越多就会理解的越深越清楚了.

    所以呢, 有些语言里直接使用命名空间这个概念, 比如C++和C#(他们的名字是不是看起来很有意思). 不过在我们接触的js, java 和 kotlin里, 都没有直接使用这个概念, 大家可以大概理解一下就行了. 

    休息一会, 换换脑子, 来点轻松的. 在js里的, 有方便的写法新建一个对象, 直接写一个{}就创建了一个空的对象, 当然也是需要有个名字的. 为了简单我们就用单个字母的名字吧, 大家在实际项目的时候, 还是养成规范的命名习惯, 好的名字能清楚的表达代表的意思.

    const a = {}

    现在a就代表一个空对象了, 我们就可以往下面挂东西了. 额, 好像说是往里面放东西更好吧. 不管怎么说吧, 就是可以用他点出一些新东西

    比如

    a. b

    得到undefined, 因为我们还没有往里面放东西呢. 再试

    a. b = 1

    通过赋值放一个值进去, 再看a. b, 有值了吧, 就是我们刚刚放进去那个值.

    起其他名字, 放其他值行不行, 没有问题, 可以大胆尝试. 但是我们还想试点更有意思的, 放一个函数行不行, 会怎么样呢

    a. c = x => x

    用最简单的箭头函数作实验, 看起来是放进去了. 意味着我们可以用这个函数了.

    a. c(2)

    返回了2, 确实是我们放进去的那个函数. 所以我们可以想到, log也是这样放到console里去的, 有兴趣翻翻源码的话, 应该会找到类似的表达的吧.

    再试一个好玩的, 放一个对象又会怎么样呢. 大胆尝试吧

    a. d = {}

    哈哈 返回了什么 Object 看来在计算机世界里还是英语比较通行啊, 这个词就是对象的意思.

    这个对象也是对象啊, 那意味着什么呢, 他里面也可以继续放东西, 一层一层的放下去, 写出来就成了一长串的点连起来的名字了.

    希望到目前为止大家能够对对象有一些认识了. 还有两个'破坏性'的实验, 有兴趣(爱折腾)的可以试一下, 不过如果怕自己被弄的更晕的话, 也可以跳过.

    一个是 如果在函数后加点, 会怎么样呢. 试过之后, 实际情况是, 不会怎么样, 跟对象一样, 也能正常用. 所以说明什么呢, 在js里, 函数也是对象啊.

    还没晕的话, 继续实验, 如果在基本类型的值后面继续加点, 又会怎么样呢. 试过之后, 结果是, 虽然看起来没有报错, 但是实际上是访问不到的, 返回undefined. 说明什么呢, 基本类型的值和对象是有一些区别的. 实际上, 上面说的函数和对象也是有一些区别的. 尽管我们在作一般的工作时可能很少需要关心这些细节, 但是如果要做一些深入的开发和研究, 这些区别就会变得比较重要了.

    好了, 暂时收起我们探索世界的好奇心. 下面要学一些很实用很重要的知识了

    数组

    首先我们介绍一个新词, 数组, 中文还是很好理解的, 见名知意, 就是一组数吗, 但是换成英文array, 也就只能知道是5个字母了.

    不过五个字母也有好处, 数组里放的也不一定都是数哦, 也可能是一组字母, 甚至是对象, 函数, 理论上说, 放什么都行, 甚至可以放数组本身. 也就是说, 数组就是一组容器, 里面可以放多个我们想放的东西, 放进去的这些东西就称为数组的元素.

    js里的数组更任性, 可以随便放东西, 既不用关心类型, 也不用关心容量. java里是没有这么自由的, 数组要明确的指出元素的个数, 并且同一数组里放的所有元素必须是同一类型的. 这个类型也是在创建数组时就要指定的.

    好吧, 还是从简单的js开始. js里新建一个数组也容易, 用中括号 [ ] 就可以了. 像对象一样, 起个名字

    const shuzu = []

    得到一个空的数组.

    放东西的话, 调用push

    shuzu. push(3)

    返回1, 表示数组当前的元素个数, 也叫长度, 英文是length

    继续放, 就继续push

    也可以在创建的同时放元素进去

    const shuzu2 = [9, 8, 7]

    元素放进去了, 怎么用呢, 数组里的元素是有编号的, 从0开始, 用的时候指定编号, 就可以访问对应这个编号的元素了.

    shuzu2[0]

    输出9

    修改的话直接赋值就行

    shuzu2[0] = 7

    再看, 第一个元素变成7了

    如果访问不存在的编号会怎么样呢

    shuzu2[3]

    输出undefined.

    获取数组的长度, 也就是那个length, 直接点就行

    shuzu2.length

    查询, 添加, 修改, 都有了, 还差什么呢, 删除. 删除比较麻烦, 建议也不要进行删除操作. 如果一定要操作, 分几种情况, pop删除最后一个元素, shift删除第一个元素, splice在可以在数组的某个位置删除并新加一些元素. 具体用法可以自行搜索.

    哈哈, 数组的基本操作就是这样了, 增删改查, 不是很难, 要注意的就是编号从0开始, 因此最后一个元素的编号是length-1.

    高阶函数

    哇哦, 然后真正重要和有意思的部分来了. 我们要介绍神奇的高阶函数了. 一定要认真看这部分哦, 因为实在太有用了.

    其实高阶函数用起来也很简单. 我们就先学三个最常用的就可以了, 理解了这种思想, 其他也就一样会用了.

    map filter reduce

    号称函数式编程三板斧. 很多地方都会见到和用到的.

    这三个函数在js里都是数组类型自带的方法, 有数组就可以调用了.

    先说map, map是对数组中的每个元素依次进行变换, 产生一个由变换之后的元素组成的新数组.

    变换的规则可以由箭头函数指定.

    比如

    const yuan = [3, 2, 1]

    const hou = yuan. map(it => it + 1)

    得到的新数组, 元素个数和原数组一样, 排列顺序也没变, 但是每个元素都按照箭头函数的规则进行了变换, 这里是加1. 想进行什么操作, 都可以在这里指定. 但是注意如果你想得到有值的新数组, 这里就需要是有返回值的纯函数.

    如果确实只是想对数组中的元素依次进行一些操作, 不需要得到新数组里的值的话, 可以用forEach代替map, 用法是一样的.

    map不难理解, 就是一一映射而已. 而filter也不难. filter是过滤的意思, 可能说成是筛选更好理解. 从数组里筛选出需要的元素, 组成新的数组.

    const yuan = [3, 2, 1, 6, 4, 5]

    const hou = yuan. filter(it => it < 3)

    得到的新数组里只包含小于3的两个元素. 可以看出filter的特点是, 要求的函数是要返回boolean值的, 根据返回值的真假筛选元素.

    至于reduce, 就要难理解一些了. 大家试着理解吧, 我也理解不深.

    比如说, 我们要对数组中的元素进行汇总, 求出所有元素的和.

    const yuan = [3, 2, 1]

    const hou = yuan. reduce((a, b) => a + b,  0)

    算出结果是对的, 但是思路是什么样呢. 我们看到, reduce的参数是两个, 除了一个箭头函数, 还有一个0. 而且这个箭头函数, 还是一个两个参数的函数.

    这就是reduce的特点. 用一个有两个参数返回一个值的函数, 依次处理数组中的每两个元素, 不断重复. 最终得到一个结果, 就是对整个数组操作的结果. 而0提供了操作的初始值, 也就是说, 开始时, 先用0和第一个元素运算. 这个例子中, 去掉0 也不影响结果. 因为这里指定的操作是加法, 加0不会改变运算结果.

    ruduce就介绍到这里吧, 还是需要多练习才能理解. 好在平时还是用map和filter的机会更多. 特别是这些函数可以连用, 因为返回的都还是数组, 可以继续调用数组的方法. 于是我们可以自由的先map再filter, 然后再map, 再filter, 感觉很爽有木有. 不用写循环, 只要声明几个简单的箭头函数, 用高阶函数连起来, 就可以自由操作数组元素了.

    好了, 三个高阶函数就介绍这么多吧. 总体还是挺简单的. 从这里我们可以明白, 为什么我们说可以不用写循环的代码了, 因为高阶函数在内部帮我们循环了. 而且上面为什么说不需要处理数组的删除, 因为我们可以用filter把需要的元素筛选出来作为一个新数组.

    哦, 忘了说的是, 为什么叫高阶函数呢, 因为他们的参数中有函数.

    嗯, 如果是js的话, 基本上学习这些就可以开始干简单的活了. 其他就是处理一些实际工作中遇到的环境, 细节问题, 以及更多熟练基本技能, 和深入理解基本概念了.

    但是java里还有好多理论, 特别是听起来高大上的所谓面向对象, 也是比较烧脑的. 我们在以后介绍吧. 现在es6里也有class和继承了. 但是, 实际工作中还是感觉这些是大规模项目或者技术框架类的项目才会用到的. 对于我们一般接触的小的业务项目, 基本增删改查也就足够了. 所以, 对于我们来说, 还是循序渐进, 先学好基础, 再了解高级理论吧.

    相关文章

      网友评论

        本文标题:疾风式全栈教程(6)-语法中班

        本文链接:https://www.haomeiwen.com/subject/pqccrftx.html