开发语言(程序语言)是一种写作语言,不同的开发语言都有各自的书写方法,如果你想用某种开发语言表达自己的想法,开发应用,就需要了解这种语言规定的书写方法,也就是语法。
一开始,不推荐大家在学习书写方法这种事儿上花太多的时间,只需要了解一些基本的写法就可以了,剩下的可以在实际工作中学习,带着需求去学习,效率会更高。不同的开发语言虽然各有不同,但很多概念都是通用的,所以只要学会其中一门开发语言,再去学习其它的语言就很容易了。
在这一章里,我们先做一些基础的语言训练,了解一下在任何开发语言中都可能会经常出现的一些概念,这样再去做后面的任务就会轻松很多。
语言训练场地
现在我们要学习的是 JavaScript 这种开发语言,找个可以运行这种语言的环境就可以学习它了。浏览器里都带着一个 JavaScript 引擎,所以可以在浏览器上学习这门语言。 之前我们在本地电脑上安装过一个 Node.js,所以也可以在这个环境里学习 JavaScript 这门开发语言。
如果选择在浏览器上学习,推荐使用 Chrome 浏览器,打开浏览器以后,再打开它的开发者工具,然后打开 Console(控制台)这个选项卡,这样就可以直接在控制台上执行 JavaScript 代码,而且可以立即得到执行的结果。
安装了 Node.js 以后,在终端,执行一下 node
命令,可以打开 Node.js 的交互模式,在这种模式下就可以输入并且执行 JavaScript,也可以立即看到执行的结果。我们也可以在项目里创建一些文件,把代码放在文件里,再到终端用 node
命令执行代码文件,观察执行的结果。
控制台:Console
在应用里有些东西你可能不会马上知道它里面到底有什么,这时我们就可以通过 console.log()
这个方法,把它输出到控制台上检查一下,所以控制台应该是最简单的调试应用的地方。
在开发浏览器应用(前端)的时候,控制台指的是浏览器的开发者工具里的 Console 选项卡,在开发 Node.js 应用的时候,控制台指的就是运行应用的终端窗口。
示例:
console.log('你好!');
如果在应用里包含上面这行代码,运行这个应用的时候,在控制台就会输出一个 你好!
数据:Data
在开发应用的时候会用到一些不同类型的数据,比如数字,字符串,数组,对象,布尔值这些都是常见的数据类型。我们可以给数据起个名字,这样以后需要用到这个数据的时候,就可以使用它的名字来表示它了,数据名字的专业称呼是常量(Constant)或者变量(Variable)。
创建一个常量或者变量,这个动作叫声明,声明一个常量或变量,其实就是在内存里申请一小块儿空间来存储数据。给这块空间起个名字(Identifier),以后就可以用这个名字来引用存储在这小块空间里的数据了。
常量:Constant
在 JavaScript 语言里,声明一个常量用的是 const 这个关键词,取的是 Constant(常量) 这个词的一部分。常量的特点是一但声明了一个常量,给它分配了一个值以后,就不能再分配给这个常量新的值了,这就是常量与变量的区别。
示例:
const title = '小白兔的开发之路';
上面创建(声明)了一个叫 title
的常量,给它分配了一个字符串类型的值:小白兔的开发之路。重新给已经声明的常量分配新的值,应用就会报错:TypeError: Assignment to constant variable 。
变量:Variable
在用 JavaScript 语言写的应用里添加一个变量,用的是 let 关键词,也可以用 var 这个关键词。声明了一个变量,给它分配了值以后,可以重新改变它的值。
示例:
let page = 1;
上面用 let 声明了一个叫 page
的变量,给它分配了一个数字类型的值:1。我们可以重新给它一个新的值:
page = 2;
现在 page
的值就会变成数字 2 了。
字符串:String
字符串是在应用里常见的数据类型,字符串就是文字。在 JavaScript 语言里,这种类型的数据一般会用双引号或者单引号包装一下。把一些文字用引号包装一下,把它交给一个常量或者变量,JavaScript 就会认为这个常量或者变量的值的类型是字符串。
示例:
const author = '王皓';
上面声明了一个叫 author
的常量,给它一个字符串类型的值:王皓。
数字:Number
数字也是在应用里经常出现的一种数据类型,如果你发现在应用里出现了一些数字,这些数字的周围并没有使用特别的符号(比如引号)包装,那这个数字就是一个数字类型的值。
示例:
const price = 39.9;
上面声明了一个叫 price
的常量,给它的是个数字类型的值:39.9 。
数组:Array
数组指的就是一组数据,就是在一个数组里面可以包含多个数据项目,这些数据项目的类型可以是不一样的,可以是数字,字符串,也可以是其它的数组。通过一些方法我们可以往现有的数组里面添加新的数据项目,也可以删除掉数组里的数据项目。
数组里包含的数据项目是有顺序的,序号是从 0 开始的,也就是第一个项目的序号是 0,第二个项目的序号是 1,依次往后面排。这个序号也可以称为索引号,英文是 Index。通过数组里的数据项目的序号我们就可以获取到这个数据项目的值。
在 JavaScript 语言里,数组可以使用一组方括号来表示,里面的每个数据项目之间用逗号分隔开。
示例:
const chapters = ['准备开发', '管理代码', '熟悉语言'];
上面声明了一个叫 chapters
的常量,它的值是一个数组,这个数组里有三个字符串类型的数据项目。如果你想得到这个数组里的第一个数据项目的值,可以用 chapters[0]
得到,数组的名字,后面加上一组方括号,方括号里面是数据项目的序号。在这个例子里,访问 chapters[0]
得到的就是一个字符串:'准备开发 ' 。
JavaScript 为处理数组类型的数据提供了很多个方法,所有的数组数据天生就拥有这些方法。比如我要上面这个数组里添加一个新的数据项目,可以用 push()
这个数组方法:
chapters.push('提供服务');
现在 chapters 这个数组里就会有 4 个数据项目了,它们是:
['准备开发', '管理代码', '熟悉语言', '提供服务'];
这种处理数组用的方法有几十个,我们不需要一下子记住所有这些方法,当遇到问题的时候可以去寻找对应的方法。想了解更多,可以搜索关键词:MDN Array 。
对象:Object
一个对象就是一个物件,一个东西,英文用 Object 来表示。在程序里我们可以通过对象来描述一种东西,比如一篇文章,一个评论,一个用户等等。对象里可以包含一些属性还有方法(函数),属性相当于是这个东西的数据,方法相当于是这个东西可以做的事情。
在 JavaScript 语言里,对象可以放在一组大括号里,它里面是一些属性,属性有名字还有对应的值,中间用冒号分隔开,属性与属性之间会用逗号分隔开。创建一个对象,用它来表示一本书,可以像下面这样:
const book = { title: '小白兔的开发之路', price: 39.9, chapters: ['准备开发', '管理代码', '熟悉语言'], };
上面声明了一个常量叫 book
,给它的这个值,类型是对象。这个对象里面有几个属性:title
,price
还有 chapters
。得到对象里的属性的值,使用对象里提供的方法,都可以使用点的形式,比如得到 book
这个对象里的 title
属性的值,可以这样:book.title
,得到的就是:'小白兔的开发之路 ' 。
布尔值:Boolean
布尔值只有两个选择:true 与 false。真的假的,有值没值,有效无效,这些情况用布尔值表示的话就是 true 与 false。true 就是真的,有值,有效 ... false 指的就是假的,没值,无效,0 ...
字符模板:Template strings
使用字符模(mú)板可以更方便的组织文字,字符模板用的是一组反引号( ` ),在字符模板中要输入的值可以放在 ${}
的大括号里面。
示例:
const title = '小白兔的开发之路'; const author = '王皓'; const description = `《 ${title} 》,作者:${author}`;
上面这个例子里,description
的值用了一个字符模板,里面插入了 title
与 author
的值,最终它的值应该是:《 小白兔的开发之路 》,作者:王皓 。
反引号 ` 在键盘上一般跟波浪号 ~ 在同一个按键上。
函数:Function
我们可以把一块儿代码组织在一起,定义成一个函数,可以给它起个名字,这样就可以重复执行它。
创建函数
创建一个函数可以使用 function 这个关键词,后面是要创建的函数的名字,函数也可以没有名字。然后是一组括号,括号里面可以添加函数里面需要的一些参数,接着是一组大括号,把函数要做的事情放在这组大括号里面。
示例:
function log() { console.log('LOG::'); }
上面用 function
定义(声明)了一个叫 log
的函数,这里我们只是简单的用了一个 console.log()
在控制台上输出点内容。
箭头函数
使用 **=> **(胖箭头)的形式也定义函数,箭头的左边儿是函数的参数,右边儿是函数的主体,也就是函数要做的事情。这些事情可以放在一组大括号里面,不过如果函数主体只有一行代码,就不需要用大括号了。
示例:
const log = () => { console.log('LOG::'); };
在上面我们把用箭头形式定义的函数交给了 log
这个常量,也就是 log
的值是一个函数。
执行函数
要使用函数提供的功能,就需要执行一下这个函数。执行函数可以使用函数的名字,后面加上一组括号。
示例:
log();
函数参数
在定义函数的时候可以添加一些参数(Arguments),在函数的主体里面可以使用这些参数,然后在执行函数的时候可以设置这些参数的具体的值。这些参数就相当于是函数的配置信息,函数可以重复使用,每次执行它的时候都可以提供不同的参数的值,这样执行的结果也就会不一样了,也就是参数可以让函数更加灵活。
示例:
function log(message) { console.log('LOG::', message); }
箭头函数:
const log = message => { console.log('LOG::', message); };
在上面定义的函数里面都添加了一个参数,参数的名字是 message,这样在函数的主体里面就可以使用这个参数了。执行函数的时候需要提供函数的参数具体的值。
执行函数的时候,可以设置函数的参数值把,把要提供的参数值按顺序放在括号里,像这样:
log('hello ~');
上面在使用 log()
函数的时候,给它的 message 参数提供的值是 hello ~,执行这个函数输出的结果就会是:LOG:: hello ~
log('你好!');
这次我们在用 log()
函数的时候,给它的 message 参数提供的值是 *你好!*所以,执行这个函数输出的结果会是:LOG:: 你好!
多个参数
函数可以支持多个参数,这些参数之间用逗号分隔开,执行函数提供参数值的时候要按参数的顺序。
示例:
const log = (message, prefix) => { console.log(prefix, message); };
在上面定义的 log()
函数里面,设置了两个参数:message 与 prefix。在执行这个函数的时候,提供给它的第一个参数值是为 message 准备的,提供的第二个参数的值会交给 prefix 。
执行:
log('你好!', '日志::');
在上面执行 log()
函数的时候给它的两个参数都提供了具体的值,你好! 是给 message 参数的值,日志:: 是给 prefix 这个参数的值。执行的结果就是在控制台上输出了:日志:: 你好!
返回结果
如果想利用执行函数以后得到的结果,在定义这个函数的时候就需要让它返回一个结果。在函数的主体里,可以使用 return 这个关键词返回一个结果。
示例:
const greet = name => { return '你好!' + name; };
上面定义的 greet()
函数会返回(Return)它加工处理之后的结果,这里返回就是 你好! 再加上执行 greet() 函数的时候给 name 参数提供的值。
这个函数是用胖箭头的形式定义的,函数很简单,支持一个参数,函数的主体也只有一行,这样情况下,定义这个箭头函数的时候可以简化成一行代码,像这样:
const greet = name => '你好!' + name;
执行:
const greeting = greet('王皓');
因为 greet()
函数可以返回它制造出来的结果,所以可以把这个结果交给别人(常量或变量),这里我们把它交给了一个叫 greeting
的常量,它的值是:你好!王皓,因为这就是执行 greet()
函数得到的结果。
方法:Method
函数如果出现在一个对象里,可以说它是这个对象里的一个方法(Method)。
示例:
const book = { title: '小白兔的开发之路', toString() { return `《${this.title}》`; }, };
book
是个对象,里面有个 title
属性,还有一个 toString()
方法,这个方法其实就是一个函数。在这个 toString()
方法里,用了 this
这个关键词,它指的是这个对象本身。
使用这个对象里的方法,可以这样:
book.toString();
解构:Destructuring
拆开一个数组或者对象,把它们里面的值交给各自的常量或变量,这就是解构这种写法。
示例<解构对象>:
const book = { title: '小白兔的开发之路', author: '王皓', };
上面的 book
是个对象,假设我现在需要声明两个常量:title 与 author,让 title 的值等于 book
对象里的 title
属性的值,让 author 的值等于 book
里的 author
属性的值。
如果不用解构的写法,应该像这样:
const title = book.title; const author = book.author;
使用解构的写法,可以这样:
const { title, author } = book;
意思就是拆开 book
对象的值,把 title 属性的值交给 title
常量,把 author 属性的值交给 author
常量。使用解构的写法定义常量或变量的时候,它们的名字应该跟对象里的属性的名字一致,如果你想使用其它的名字,也可以重新命名一下,用下面这种写法:
const { title: titleAlias } = book;
上面这行代码会声明一个叫 titleAlias
的常量,它的值是 book
对象里的 title
属性的值。
const fruits = ['苹果', '香蕉', '桔子'];
解构上面这个 fruits
数组,可以像这样:
const [c1, c2, c3] = fruits;
c1
,c2
还有 c3
的值分别对应的是 fruits
数组里的第一个,第二个还有第三个项目的值。
展开:Spread
展开,有点像是脱掉数组或者对象的 “壳”,留下它们里面的 “瓤”,我们可以再把得到的这个 “瓤” 放在别人那里。展开操作符是三个点( ... ),把要展开的东西放在这个展开操作符的后面就行了。要展开的东西可以是数组,也可以是个对象。
示例:
const fruits = ['苹果', '香蕉', '桔子']; const vegetables = ['土豆', '茄子', '辣椒'];
上面定义的是两个数组。假设我们要创建一个新的东西,里面包含 fruits
与 vegetables
这两个数组里所有的项目,用展开(Spread)这种写法可以这样做:
const food = [...fruits, ...vegetables];
上面这个 food
是个数组,这个数组里会包含在 fruits
与 vegetables
里面所有的项目。
['苹果', '香蕉', '桔子', '土豆', '茄子', '辣椒'];
const data = { title: '小白兔的开发之路', }; const author = { name: '王皓', };
创建一个新的对象,里面需要上面这个 data
对象里的所有属性,还需要一个 author
属性,对应的值就是上面这个 author
对象。
const book = { ...data, author, };
上面新建的这个 book
对象,应该像下面这样:
{ title: '小白兔的开发之路', author: { name: '王皓', } }
流程控制:Control flow
JavaScript 语言提供了一些方法可以控制代码的执行顺序,下面我们就来学几种常用的方法。
if
检查一个条件,满足条件时执行一块儿代码。
写法 1:
判断一个指定的条件,如果情况符合这个条件,就去执行指定的代码。
if () {}
用 if
这个关键词,然后在它后面的括号里可以设置一个条件,满足这个条件时可以执行大括号里的代码。
示例:
if (speed > 120) { console.log('您已超速!'); }
检查 speed
的值是否大于 120,如果符合这个条件,也就是条件表达式返回的值是 true ,就会执行在 if
后面的大括号里的代码,也就是在控制台上输出:您已超速!
写法 2:
检查一个条件,满足条件时执行一块儿代码,不满足时执行另外一块儿代码。
if () { } else { }
示例:
if (speed > 120) { console.log('您已超速!'); } else { console.log('车速正常~'); }
如果车辆的时速大于 120,就会输出:*您已超速!*不然的话就输出:车速正常~。
switch
检查一个值,设置一些情况,执行不同的代码。
写法:
switch () { case : break; case : break; default: break; }
把要检查的值放在 switch
后面的括号里,再把所有的情况都放在一组大括号里,用 case
可以添加一种情况,case
的下面是符合当前这种情况之后要做的事情,在设置的情况里面用了 break
,这样满足这种情况以后,就会 break
出来,也就不会继续检查其它的情况了,就是从这个 switch
里跳出来。
示例:
let gear = 'P';
上面的 gear
表示车子的档位,我想根据当前档位的值去做不同的事情。如果用 if
这种写法,会像这样:
if (gear === 'P') { console.log('停车'); }
上面判断了 gear
的值是不是等于 P,如果是就在控制台上输出 停车。用这种写法你可能要写很多个 if
,因为有很多种要判断的条件。如果换成 switch
这种写法,会像下面这样:
switch (gear) { case 'P': console.log('停车'); break; case 'R': console.log('倒车'); break; case 'D': console.log('开车'); break; case 'N': console.log('空档'); break; default: console.log('档位异常!'); break; }
在上面这个例子里,用 switch
检查了一下 gear
表示的值,用 case
设置了一些情况,如果 gear
的值是 P ,就执行 case 'P'
这种情况下设置的代码,如果是 R ,就执行 case 'R'
这种情况下设置的代码。如果 gear
的值不满足所有列出的情况,就会执行默认情况(default)下的代码。不需要这种默认的情况,可以不必使用这个 default
。
throw
应用在运行的时候可能会发生一些异常情况,比如你定义了一个函数,它的作用就是往数据仓库里存储数据,非常有可能在执行这个函数的时候会遇到一些异常的情况,比如数据仓库正好不能用了。我们可以在函数里判断一下,如果在存储数据的时候,发生了这些异常情况,就用 throw
来触发一个异常。在使用这个函数的时候,可以设置一下处理执行这个函数的时候可能会发生的异常情况。
在函数里出现的异常情况,可以用 throw 这个关键词来触发异常,这样函数就会停止执行, throw 下面的代码都不会被执行,如果没有处理这个异常,应用也会停止运行。
示例:
const drive = () => { throw new Error('没油了!'); }; drive();
上面定义了一个叫 drive()
的函数,函数里直接用 throw
抛出一个叫 Error 的异常,在 Error()
里添加的字符串是异常信息。执行 drive()
这个函数,应用就会抛出一个异常,应用会停止运行。
try...catch
试着去做一些事情,如果出现异常再处理发生的异常。
写法:
try { } catch() { }
在要做的事情放在 try
区块里面,如果在做的这些事情里发生了异常情况,可以在 catch
区块里处理发生的异常。
示例:
const getGasoline = () => { return false; }; const drive = () => { const gasoline = getGasoline(); if (!gasoline) { throw new Error('没油了!'); } console.log('呜~呜~~~'); }; try { drive(); } catch (error) { console.log(error.message); }
上面定义了两个函数,getGasoline()
与 drive()
,然后执行了 drive()
函数。getGasoline()
函数的功能是获取汽车的油量,为了演示异常,我们让它一直返回 false
。在 drive()
函数里,先用了一下 getGasline()
获取油量,然后判断了一下,如果汽车没油了,就用 throw
抛出一个异常。不然就在控制台上输出 呜~呜~~~
执行 drive()
函数的时候我们把它放在了 try
区块里,这样执行这个函数如果发生了异常,可以在 catch
区块里处理,这里就是在控制台上,输出了 error
对象里的 message
属性的值。
因为 getGasoline()
这个函数会一直返回 false
值,所以执行 drive()
函数就一定会出现异常。也就是如果执行上面这段代码,你会在控制台上看到 没油了! 这几个字儿。如果我们修改 getGasoline()
函数,让它返回 true
,执行上面这块代码会在控制台上输出 呜~呜~~~
类:Class
类(Class)是对象(Object)的模板。定义一个类,在它里面添加属性与方法,然后基于这个类可以去创建一些对象,这些对象里会拥有类里的属性与方法。
创建类
创建一个类,可以用 class
关键词,后面加上类的名字就行了,类里的东西放在一组大括号里。类的名字一般会选择首字母大写,比如我要创建一个 “用户服务 ” 这个类,用户的英文是 User,服务的英文是 Service,所以这个类的名字就可以是 UserService,当然 JavaScript 并不会限制你到底为类取什么名字,你觉得合理就行。
写法:
class ClassName {}
示例:
class Car {}
上面声明(定义、创建)了一个类,名字是 Car
(汽车),这是一个空白的类,里面啥都还没有呢。
实例化
基于类去创建对象,这个动作叫实例化(Instantiate),创建的这个对象可以说它是类的一个实例(Instance)。实例化一个类可以使用 new
这个关键词。
示例:
const c1 = new Car();
上面这行就是实例化了一下 Car
这个类,也就是基于这个类创建了一个对象,我们把这个对象交给了 c1
,可以说 c1
就是 Car
这个类的一个实例。
创建的实例(对象)里面会包含类里的属性与方法,但是 Car
现在是个空白的类,下面我们给它添加点属性还有方法。
属性
属性(Property)就是类里的一些数据,也可以叫做字段(Field)或者成员(Member)。
写法:
class ClassName { propertyName; }
示例:
class Car { engine; }
在上面这个 Car
类里面有个属性叫 engine
,现在基于这个类创建的对象里,天生就会拥有 engine
这个属性了。
方法
方法(Method)是类里面定义的一些行为,也就是可以做的事情,其它就是一些函数。
写法:
class ClassName { methodName() {} }
示例:
class Car { engine; drive() { console.log('呜~呜~~~'); } }
上面这个 Car
里面有个 engine
属性,还有个 drive()
方法,基于这个类创建的对象里面天生就会拥有这个属性与方法。
const c1 = new Car(); c1.drive();
构造方法
类里有个特别的方法叫构造方法(Constructor),在实例化类,也就是基于类去创建对象的时候,会自动执行这个方法。你可以在这个构造方法里安排一些事情。
写法:
class ClassName { constructor() {} }
示例:
class Car { engine; constructor() { console.log('一辆崭新的汽车!'); } drive() { console.log('呜~呜~~~'); } }
在上面这个类里使用了构造方法,现在实例化这个类的时候就会执行在这个构造方法安排的事情,这里就是在控制台上输出一行文字:一辆崭新的汽车!
this
在类里面用 this
可以表示基于这个类创建的对象本身。
示例:
class Car { engine; constructor(engine) { this.engine = engine; console.log('一辆崭新的汽车!'); } drive() { console.log('呜~呜~~~'); } }
在上面这个类的构造方法里,用了一下 this
关键词,this.engine
表示的是对象里的 engine
属性。这里设置了一下这个属性的值,让它等于构造方法里的 engine
参数的值。这样如果基于这个类创建对象的时候,可以设置 engine
参数的值,在构造方法里,会把这个参数的值交给 engine
属性。
const c1 = new Car('V8'); const c2 = new Car('V12');
基于 Car
这个类创建了两个对象:c1
与 c2
。因为现在 Car
这个类的构造方法里支持一个参数,所以在创建对象的时候我们可以设置一下这个参数的值,这个参数的值会交给对象的 engine
属性。所以在访问 c1.engine
的时候,得到的应该就是 V8。访问 c2.engine
的时候,得到的会是 V12。
继承
一个类可以继承(Extends)其它的类,这样这个类里面就会拥有它继承的类里面的东西了。继承可以使用 extends
关键词。
写法:
class Class3 extends Class1 {}
示例:
class Car { drive() { console.log('呜~呜~~~'); } }
上面定义了一个叫 Car
的类,里面有个 drive()
方法。下面可以再去定义一个类,让这个类继承一下 Car
这个类。
class PickupTruck extends Car {}
PickupTruck
(皮卡)这个类继承了 Car
这个类,所以它里面就会拥有 Car
这个类里的东西了,比如 drive()
这个方法。
const p1 = new PickupTruck(); p1.drive();
天生就有的东西
在 JavaScript 语言里,有一些天生就有的东西,比如 console
,JSON
对象,parseInt()
函数等等。之前我们用过 console
对象上的 log()
方法在控制台上输出了一些内容,后面我们还要介绍使用 JSON
这个对象,处理 JSON 格式的数据,用 parseInt()
函数把数据转换成数字类型的值。
字符串,数字,数组,对象等等这些东西,它们天生会拥有一些属性还有方法。比如字符串与数组的上面都会有一个 length
属性,它的值就是字符的个数或者数组项目的个数,数组上面有很多方法可以处理数组,可以添加,删除,查找,排序,截取数据项目。
结语
JavaScript 语言的写法还有很多,没有必要一下子都学完,上面这些就足以让你起步工作了,剩下的可以留到实际的工作中学习,这样效果会更好一些。我们也没有必要把它们都记住,你只需要先知道有这些个东西,知道它们的作用是什么就够了,具体的写法随时都可以查资料复习,以后写的多了也就记住了。MDN(Mozilla Developer Network)这个网站上提供了非常详细的文档,遇到语法问题可以到这个网站上搜索一下。
在后面我们还会介绍一些写法,不过要在真正需要用到的时候才会去学习它们。