一、JavaScript介绍
JavaScript是目前所有主流浏览器上唯一支持的脚本语言,这也是早期JavaScript
的唯一用途。其主要作用是在不与服务器交互的情况下修改HTML
页面内容,因此其最关键的部分是DOM
(文档对象模型),也就是HTML
元素的结构。通过Ajax
可以使HTML
页面通过JavaScript
,在不重新加载页面的情况下从服务器上获取数据并显示,大幅提高用户体验。通过JavaScript
,使Web
页面发展成胖客户端成为可能。
语言的性质
本节对JavaScript
的性质做简要介绍,以帮你理解一些疑问。
JavaScript
和ECMAScript
(JavaScript versus ECMAScript)
编程语言称为JavaScript
,语言标准被称为ECMAScript
。他们有不同名字的原因是因为“Java”已经被注册为商标(属于Oracle)。目前,只有Mozilla
被正式允许使用“JavaScript”名称,因为很久以前他们得到一份许可。因此,开放的语言标准拥有不同的名字。当前的JavaScript
版本是ECMAScript 6
,ECMAScript 7
当前是开发版。
JavaScript
之父,Brendan Eich
迅速了创建一门编程语言。(否则,Netscape将使用其他技术)。他借鉴了几门其他语言的一些特性:
- JavaScript借鉴了Java的语法和如何区分原始值和对象。
- JavaScript的函数设计受Scheme和AWK的启发——他们(的函数)都是第一类(first-class)对象,并且在语言中广泛使用。闭包使他们(函数)变成强大的工具。
- Self影响了JavaScript独一无二的面向对象编程(OOP)风格。它的核心思想(在这里我们没有提到)非常优雅,基于此创建的语言非常少。但后面会提到一个简单的模式照顾大部分用例。JavaScript面向对象编程的杀手级特性是你可以直接创建对象。不需要先创建类或其他类似的东西。
- Perl和Python影响了JavaScript字符串,数组和正则表达式的操作。
JavaScript
在最初的时候并不是一个完善的语言,因此也导致JavaScript
遗留了很多令人诟病的问题。在开发稍大规模的应用时会显得力不从心,但是由于JavaScript
本身是一种非常灵活的语言,因此在它的基础上开发程序库比较容易,因此出现了一大批非常优秀的第三方库,如jQuery,ExtJS,underscorejs,backbone等等,由于这些第三方库,JavaScript
变得非常简单。其中jQuery
的使用非常广泛,它大幅简化了DOM
和Ajax
,已经成为了很多网站的标配。jQuery
虽然基于JavaScript
,但它提供了另外一种编程范式,也就是逻辑式编程,与SQL
和正则表达式类似。
JavaScript能做什么
如上图,JavaScript
作为Github上最流行、最火的编程语言,几乎无所不能。这里是PuYart的关于JavaScript
就要统治世界了的文章,可以让我们了解JavaScript
到底能做什么的一些介绍。
- Web前端(各种前端工具类库、前端框架、动画效果、数据可视化等)
- 服务端开发(Node.js)
- 移动应用或者
Hybrid App
(Cordova) - 桌面应用(NW.js、Electron)
- 游戏(Unity3D、Cocos2d-js、Pomelo)
- VR(JavaScript在VR世界的应用)
- 硬件、嵌入式物联网等(Tessel:用JavaScript做嵌入式开发)
- 操作系统(NodeOS)
Atwood’s Law: any application that can be written in JavaScript, will eventually be written in JavaScript.(Atwood定律:凡是能用JavaScript写出来的,最终都会用JavaScript写出来。)
二、 JavaScript语法
语句和表达式
了解JavaScript
的语法,先来了解两个主要的语法类型:语句和表达式。
- 语句通常是“做某些事情”。程序是一组语句的序列。举个例子,下面声明(创建)一个变量
foo
:
1 | var foo; |
- 表达式是产生“值”。他们通常位于赋值操作的右边、函数参数等。举个例子:
1 | 3 * 7 |
语句和表达式之间的区别最好通过实例说明,JavaScript
(像Java)有两种不同的方式实现if-then-else
。一种是用语句:
1 | var x; |
另一种是表达式:
1 | var x = y >= 0 ? y : -y; |
你可以将后者作为函数参数(但前者不行):
1 | myFunction(y >= 0 ? y : -y) |
最后,每当JavaScript
期待一个语句,你也可以用一个表达式代替。例如:
1 | foo(bar(7, 1)); |
foo(...);
是一个语句(也叫做表达式语句),bar(7, 1)
则是一个表达式。他们都实现函数调用。
流程控制语句和语句块
流程控制语句,其语句体可以是单条语句。举两个例子:
1 | if (obj !== null) obj.foo(); |
然而,任何语句总能被语句块代替,花括号包含零或多条语句。因此,你也可以这样写:
1 | if (obj !== null) { |
为便于程序的阅读和维护,推荐使用后一种方式,即语句块方式。
分号
JavaScript
中的分号是可选的。但省略(分号)可能会带来意想不到的结果,所以我建议还是写上分号。
正如上面所看到的,分号作为语句的结尾,但语句块不需要。仅有一种情况下你能看到语句块后面有分号——函数表达式后面的函数体块。表达式作为语句的结尾,后面是分号:
1 | var x = 3 * 7; |
注释
JavaScript
的注释有两种形式:单行注释和多行注释。单行注释以//
开头,以换行符结尾:
1 | x++; // 单行(single-line)注释 |
多行注释用/**/
包裹
1 | /* |
三、变量和赋值
JavaScript
中的变量在使用前必须先声明,否则会报错引用错误(Reference Error):
1 | var foo; // 声明变量“foo” |
赋值
你可以在声明变量的同时为其赋值:
1 | var foo = 6; |
你也可以给已经存在的变量重新赋值:
1 | foo = 4; // 更改变量的值 |
复合赋值操作符
有很多复合赋值操作符,例如+=。下面的两个赋值操作等价:
1 | x += 1; |
标识符和变量名
标识符就是事物的名字,在JavaScript
中他们扮演不同的语法角色。例如,变量的名称是一个标识符。
大体上,标识符的第一个字符可以是任何Unicode
字符、美元标志符($)或下划线(_)。后面可以是任意字符和数字。因此,下面全是合法的标识符:
1 | arg0 |
注意:首字符不能是数字,如果是数字的话,该如何区分是数字还是变量呢?
一些标识符是“保留关键字”——他们是语法的一部分,不能用作变量名。从技术上讲,下面三个标识符不是保留字,但也不应该作为变量名:
1 | Infinity NaN undefined |
四、值
JavaScript
有所有我们期待的编程语言值类型:布尔,数字,字符串,数组等。JavaScript
中的所有值都有属性。每个属性有一个键(或名字)和一个值。你可以使用点(.)操作符读取属性:
1 | value.propKey |
举个例子:字符串abc
有属性lenght
(长度)
1 | var str = 'abc'; |
上面的代码也可以写成下面这样:
1 | 'abc'.length // 得到3 |
点操作符也可以用来给属性赋值:
1 | var obj = {}; // 空对象 |
你也可以通过它(.)调用方法:
1 | 'hello'.toUpperCase(); // 得到HELLO |
上面,我们在值hello
上面调用方法toUpperCase()
。
原始类型值和对象
JavaScript定义了不同值之间的区别:
- 原始值包括:
boolean
,number
,string
,null
和undefined
。 - 所有其他的值都是对象。实际上对象被定义为——所有不为原始值的值。
两者之间的主要区别在于他们是如何被比较的:每一个对象有一个独一无二的标志,并且仅和自己相等:
1 | var obj1 = {}; // 一个空对象 |
相反,所有原始值只要编码值相同就被认为是相同的:
1 | var prim1 = 123; |
原始类型值
下面全是原始类型值(简称:原始值):
布尔类型:true,false
数字类型:1736,1.351
字符串类型: ‘abc’,”abc”
两个“无值(non-values)”:undefined,null
原始值的特征:值做比较时,“内容”做比较。
1 | 3 === 3 // true |
- 无法更改:值的属性无法更改,无法添加和移除属性,获取未知属性总返回undefined。
1 | var str = 'abc'; |
对象
对象的类型
所有非原始值的值都是对象。最常见的几种对象类型是:
- 简单对象(类型是
Object
)能通过对象字面量创建:
1 | { |
上面的对象有两个属性:firstName
属性的值是“Jane”,lastName
属性的值是“Doe”。
- 数组(类型是
Array
)能通过数组字面量创建:
1 | [ ‘apple’, ‘banana’, ‘cherry’ ] |
上面的数组有三个元素,可以通过数字索引访问。例如“apple”的索引是0。
- 正则表达式对象(类型是
RegExp
)能通过正则表达式字面量创建。
1 | /^a+b+$/ |
对象的特征
- 比较的是引用:比较的是标识符,每个值有自己的标识符。
1 | {} === {} // 两个不同的空对象, false |
- 默认可以更改。
1 | var obj = {}; |
所有的数据结构(如数组)都是对象,但并不是所有的对象都是数据结构。例如:正则表达式是对象,但不是数据结构。
undefined 和 null
JavaScript
有两个“无值)”:undefined
和null
。
undefined
的意思是“没有值”。未初始化的变量是undefined
:
1 | var foo; |
读取不存在的属性时,将返回undefined
:
1 | > var obj = {}; // 空对象 |
缺省的参数也是undefined
:
1 | function f(x) { |
null
的意思是“没有对象”。它被用来表示对象的无值(参数,链上的对象等)。
通常情况下你应该把undefined
和null
看成是等价的,如果他们代表相同意义的无值的话。检查他们的一种方式是通过严格比较:
1 | if (x === undefined || x === null) { |
另一种在实际中使用的方法是认为undefined 和 null 都是false:
1 | if (!x) { |
警告:false,0,NaN 和 “” 都被当作false。
包装类型
对象类型的实例Foo
(包括内建类型,例如Array和其他自定义类型)从对象Foo.prototype
上获取方法。你可以通过读取这个方法的方式(不是调用)验证这点:
1 | [].push === Array.prototype.push // true |
相反,原始类型是没有类型的,所以每个原始类型有一个关联类型,称之为包装类型:
- 布尔值的包装类型是 Boolean。布尔值从Boolean.prototype上获取方法:
1 | > true.toString === Boolean.prototype.toString //true |
注意:包装类型名字的首字母是大写的B。如果在JavaScript中布尔值的类型可以访问,那么它可能会被转换为布尔对象。
- 数字值的包装类型是
Number
。 - 字符串值的包装类型是
String
。
包装类型也有实例(他们的实例是对象),但不常用。相反,包装类型有其他用处:如果你将他们作为函数调用,他们可以将值转换为原始类型。
1 | Number('123') //123 |
通过typeof和instanceof将值分类
有两个操作符可以用来将值分类:typeof
主要用于原始值,instanceof
主要用于对象。
typeof 使用方法如下:
typeof «value»
typeof
返回描述value
“类型”的一个字符串。例如:
1 | typeof true //'boolean' |
下面列出了typeof
操作的所有结果:
1 | 操作数 结果 |
有两个结果和我们上面说的的原始值与对象是矛盾的:
- 函数的类型是
function
而不是object
。因为函数(类型为“function”)是对象(类型是对象)的子类型,这不是一个错误。 null
的类型是object
。这是一个bug,但从没被修复,因为修复后会破坏现有的代码。
instanceof使用方法如下:
«value» instanceof «Constr»
如果value
是一个对象,并且value
是由构造函数Constr
创建的(参考:类)。例如:
1 | var b = new Bar(); // 通过构造函数Bar创建对象 |
深入阅读
五、布尔
布尔类型原始值包括true
和false
。下面的操作符会得到布尔值:
- 二元逻辑运算符:&&(与),||(或)
- 前缀逻辑运算符:!(非)
- 等值运算符:=== !== == !=
- 比较运算符(字符串或数字):> >= < <=
真值和假值
每当JavaScript
希望一个布尔值时(例如:if语句的条件),可以使用任何值。它将被理解(转换)为true
或false
。下面的值被理解为false
:
- undefined, null
- 布尔: false
- 数字: 0, NaN
- 字符串: ‘’
所有其他值被认为true
。被理解为false
的值称为假值,被理解为true
的值称为真值。可以使用Boolean
作为函数,测试值被理解为什么。
1 | Boolean(undefined) //false |
二元逻辑运算符
JavaScript
中的二元逻辑运算符是短路运算——如果第一个操作数可以确定结果,第二个操作数将不被验证(运算)。例如,在下面的代码中,函数foo()
永远不会被调用。
1 | false && foo() |
此外,二元逻辑运算符会返回操作数中的一个,可能是一个布尔值,也可能不是。
- 与:如果第一个操作数是假值,返回第一个。否则返回第二个操作数。
1 | NaN && 'abc' //NaN |
- 或:如果第一个操作数是真值,返回第一个。否则,返回第二个操作数。
1 | 'abc' || 123 //'abc' |
等值运算符
在JavaScript
中检测相等,你可以使用严格相等(===
)和严格不等(!==
)。或者你也可以使用非严格相等(==
)和非严格不等(!=
)。
经验规则:总是用严格运算符,假装非严格运算符不存在。严格相等更安全。
深入阅读
六、数字
JavaScript
中的所有数字都是浮点型(虽然大部分的JavaScript引擎内部也使用整数)。至于为什么这样设计,查看这里(每一个JavaScript开发者应该了解的浮点知识)。
1 | 1 === 1.0 //true |
特殊数字:
NaN
(“不是一个数字 not a number”): 错误值。
1 | Number('xyz') // 'xyz' 不能被转换为数字得到:NaN |
Infinity
:也是最大错误值(无穷大)
1 | 3 / 0 //Infinity |
Infinity
有时很有用,因为它比任何其他数字都大。同样,-Infinity
比其他任何数字都小。
JavaScript
有两个零,+0
和-0
。它(js引擎)通常不让你看到,并简单将两个零都显示为0:
1 | +0 //0 |
因此最好假装只有一个零(正如我们看到假值时所做的那样:**-0 和 +0 都是假值**)。
运算符
JavaScript
中有下列算数运算符:
1 | 加: number1 + number2 |
全局对象Math
通过函数提供更多算数运算操作。
JavaScript
中也有位运算符(例如:&)。
七、字符串
字符串可以直接通过字符串字面量创建。这些字面量被单引号或双引号包裹。反斜线(\)转义字符并且产生一些控制字符。例如:
1 | 'abc' |
可以通过方括号访问单个字符:
1 | var str = 'abc'; |
length
属性是字符串的字符数量。
1 | 'abc'.length //3 |
提醒:字符串是不可变的,如果你想改变现有字符串,你需要创建一个新的字符串。
字符串运算符
字符串可以通过加号操作符(+)拼接,如果其中一个操作数为字符串,会将另一个操作数也转换为字符串。
1 | var msgCount = 3; |
连续执行拼接操作可以使用+=
操作符:
1 | var str = ''; |
字符串方法
字符串有许多有用的方法。例如:
1 | 'abc'.slice(1) // 复制子字符串,得到索引1及其之后的字符串,即:'bc' |
八、语句
条件(Conditionals)
if
语句通过布尔条件决定执行那个分支:
1 | if (myvar === 0) { |
下面的switch
语句,furit的值决定那个分支被执行。
1 | switch (fruit) { |
循环(Loops)
for 循环的格式如下:
1 | for(初始化; 当条件成立时循环; 下一步操作) |
例子:
1 | for (var i=0; i < arr.length; i++) { |
当条件成立时while
循环继续循环它的循环体。
1 | // 和上面的for循环相等 |
当条件成立时,do-while
循环继续循环。由于条件位于循环体之后,所以循环体总是被至少至少执行一次。
1 | do { |
在所有的循环中:
- break中断循环
- continue开始一个新的循环迭代
九、函数
定义函数的一种方法是通过函数声明:
1 | function add(param1, param2) { |
上面的代码定义一个名称叫做add
的函数,有两个参数param1
和param2
,并且返回参数的和。下面是如何调用这个函数:
1 | add(6, 1) //7 |
另一种定义add()
函数的方法是通过函数表达式:
1 | var add = function (param1, param2) { |
函数表达式产生一个值,因此可以直接将函数作为参数传递给其他函数:
1 | someOtherFunction(function (p1, p2) { ... }); |
函数声明提升
函数声明会被提升,他们全被移动到当前作用域开始之处。这允许你在函数声明之前调用它们:
1 | function foo() { |
注意:虽然变量声明也会被提升,但赋值的过程不会被提升:
1 | function foo() { |
特殊变量参数
在JavaScript
中你可以调用任意函数并传递任意数量的参数——语言绝不会“抱怨”(参数检测)。都可以正常工作,然而,使所有参数可访问需要通过特殊变量arguments
。arguments
看起来像数组,但它没有数组的方法(称为类数组 array-like)。
1 | function f() { return arguments } |
太多或太少参数
让我们通过下面的函数探索JavaScript
中传递太多或太少参数时如何处理
1 | function f(x, y) { |
多出的参数将被忽略(可以通过arguments
访问):
1 | f('a', 'b', 'c') //a b |
缺少的参数将会是undefined
:
1 | f('a') //a undefined |
可选参数
下面是一个常见模式,给参数设置默认值:
1 | function pair(x, y) { |
在(*)
这行,如果x是真值(除了:null
,undefined
等), 操作符返回x。否则,它返回第二个操作数。
1 | pair() //[ 0, 0 ] |
强制数量
如果你想强制参数的数量,你可以检测arguments.length
:
1 | function pair(x, y) { |
将arguments 转换为数组
arguments
不是一个数组,它仅仅是类数组(array-like):它有一个length
属性,并且你可以通过方括号索引方式访问它的元素。然而,你不能移除元素,或在它上面调用任何数组方法。因此,有时你需要将其转换为数组。这就是下面函数的作用。
1 | function toArray(arrayLikeObject) { |
十、异常处理
异常处理最常见的方式像下面这样:
1 | function throwException() { |
try分支包裹易出错的代码,如果try分支内部抛出异常,catch分支将会执行。
十一、严格模式
严格模式开启检测和一些其他措施,使JavaScript
变成更整洁的语言。推荐使用严格模式。为了开启严格模式,只需在JavaScript
文件或script
标签第一行添加如下语句:
1 | ; |
你也可以在每个函数上选择性开启严格模式,只需将上面的代码放在函数的开头:
1 | function functionInStrictMode() { |
下面的两小节看下严格模式的三大好处。
明确错误
让我们看一个例子,严格模式给我们明确的错误,否则JavaScript
总是静默失败:下面的函数f()
执行一些非法操作,它试图更改所有字符串都有的只读属性——length
:
1 | function f() { |
当你调用上面的函数,它静默失败,赋值操作被简单忽略。让我们将f()
在严格模式下运行:
1 | function f_strict() { |
现在浏览器报给我们一些错误:
1 | f_strict() // TypeError: Cannot assign to read only property 'length' of abc |
不是方法的函数中的this
在严格模式下,不作为方法的函数中的this
值是undefined
:
1 | function f_strict() { |
在非严格模式下,this
的值是被称作全局对象(global object
)(在浏览器里是window
):
1 | function f() { |
不再自动创建全局变量
在非严格模式下,如果你给不存在的变量赋值,JavaScript
会自动创建一个全局变量:
1 | function f() { foo = 5 } |
在严格模式下,这会产生一个错误:
1 | function f_strict() { 'use strict'; foo2 = 4; } |
深入阅读
十二、变量作用域和闭包
在JavaScript
中,你必须使用变量之前,通过var
声明变量:
1 | var x; |
你可以用一条var
语句声明和初始化多个变量:
1 | var x = 1, y = 2, z = 3; |
但我建议每个变量使用一条语句。因此,我将上面的语句重写为:
1 | var x = 1; |
由于提升(见下文),最好在函数顶部声明变量。
变量和函数作用域
变量的作用域总是整个函数(没有块级作用域)。例如:
1 | function foo() { |
我们可以看到tmp变量不仅在(*)所在行的语句块存在,它在整个函数内都存在。
变量提升
变量声明会被提升:声明会被移到函数的顶部,但赋值过程不会。举个例子,在下面的函数中(*)
行位置声明了一个变量。
1 | function foo() { |
在内部,上面的函数被执行像下面这样:
1 | function foo() { |
闭包
每个函数保持和函数体内部变量的连接,甚至离开创建它的作用域之后。例如:
1 | function createIncrementor(start) { |
在(*)
行开始的函数在它创建时保留上下文,并在内部保存一个start
活动值:
1 | var inc = createIncrementor(5); |
闭包是一个函数加上和其作用域链的链接。因此,createIncrementor()
返回的是一个闭包。
IIFE:模拟块级作用域
有时你想模拟一个块,例如你想将变量从全局作用域隔离。完成这个工作的模式叫做 IIFE
(立即执行函数表达式(Immediately Invoked Function Expression
)):
1 | (function () { // 块开始 |
上面你会看到函数表达式被立即执行。外面的括号用来阻止它被解析成函数声明;只有函数表达式能被立即调用。函数体产生一个新的作用域并使tmp
变为局部变量。
闭包实现变量共享
下面是个经典问题,如果你不知道,会让你费尽思量。因此,先浏览下,对问题有个大概的了解。
闭包保持和外部变量的连接,有时可能和你想像的行为不一致:
1 | var result = []; |
(*)
行的返回值总是当前的i值,而不是当函数被创建时的i值。当循环结束后,i的值是5,这是为什么数组中的所有函数的返回值总是一样的。如果你想捕获当前变量的快照,你可以使用IIFE
:
1 | for (var i=0; i < 5; i++) { |
深入阅读
十三、对象和继承
和所有的值类型一样,对象有属性。事实上,你可以将对象当作一组属性的集合,每个属性都是一对(键和值)。键是字符串,值可以是任意JavaScript
值。到目前为止,我们仅仅见过键是标识符的属性,因为点操作符处理的键必须为标识符。在这节,你讲见到另一种访问属性的方法,能将任意字符串作为键。
单个对象
在JavaScript
中,你可以直接创建对象,通过对象字面量:
1 | var jane = { |
上面的对象有两个属性:name
和describe
。你能读(“get”)和 写(“set”)属性:
1 | jane.name // get,'Jane' |
属性是函数如describe
可以被当作方法调用。当调用他们时可以在它们内部通过this引用对象。
1 | jane.describe() // 调用方法,'Person named John' |
in
操作符用来检测一个属性是否存在:
1 | 'newProperty' in jane // true |
若读取一个不存在的属性,将会得到undefined
值。因此上面的两个检查也可以像下面这样:
1 | jane.newProperty !== undefined // true |
delete
操作符用来删除一个属性:
1 | delete jane.newProperty //true |
任意键属性
属性的键可以是任意字符串。到目前为止,我们看到的对象字面量中的和点操作符后的属性关键字。按这种方法你只能使用标识符。如果你想用其他任意字符串作为键名,你必须在对象字面量里加上引号,并使用方括号获取和设置属性。
1 | var obj = { 'not an identifier': 123 }; |
方括号允许你动态计算属性关键字:
1 | var x = 'name'; |
引用方法
如果你引用一个方法,它将失去和对象的连接。就其本身而言,函数不是方法,其中的this
值为undefined
(严格模式下)。
1 | var func = jane.describe; |
解决办法是使用函数内置的bind()
方法。它创建一个新函数,其this
值固定为给定的值。
1 | var func2 = jane.describe.bind(jane); |
方法内部的函数
每个函数都有一个特殊变量this
。如果你在方法内部嵌入函数是很不方便的,因为你不能从函数中访问方法的this
。下面是一个例子,我们调用forEach
循环一个数组:
1 | var jane = { |
调用logHiToFriends
会产生错误:
1 | jane.logHiToFriends() // TypeError: Cannot read property 'name' of undefined |
有两种方法修复这问题。
- 将
this
存储在不同的变量。
1 | logHiToFriends: function () { |
- forEach的第二个参数允许提供
this
值。
1 | logHiToFriends: function () { |
在JavaScript
中函数表达式经常被用作函数参数。时刻小心函数表达式中的this
。
构造函数:对象工厂
除了作为“真正”的函数和方法,函数还在JavaScript中扮演第三种角色:如果通过new操作符调用,他们会变为构造函数,对象的工厂。构造函数是对其他语言中的类的粗略模拟。约定俗成,构造函数的第一个字母大写。例如:
1 | // 设置实例数据 |
我们看到构造函数分为两部分:首先,Point
函数设置实例数据。其次,Point.prototype
属性包含对象的方法。前者的数据是每个实例私有的,后面的数据是所有实例共享的。
我们通过new操作符调用Point
:
1 | var p = new Point(3, 5); |
p是Point
的一个实例:
1 | p instanceof Point //true |
深入阅读
十四、数组
数组是数组元素的序列,能通过整数索引方法数组元素,数组索引从0开始。
数组字面量
数组字面量创建数组很方便:
1 | > var arr = [ 'a', 'b', 'c' ]; |
上面的数组有三个元素:分别是字符串“a”,“b”, “c”。你可以通过整数索引访问它们:
1 | arr[0] //'a' |
length
属性总表示一个数组有多少项元素。
1 | arr.length //3 |
除此之外它也可以用来从数组上移除尾部元素:
1 | arr.length = 2; |
in
操作符也可以在数组上工作。
1 | 1 in arr // arr在索引为1处是否有元素?,true |
值得注意的是数组是对象,因此可以有对象属性:
1 | arr.foo = 123; |
数组方法
数组有许多方法。举些例子:
1 | var arr = [ 'a', 'b', 'c' ]; |
遍历数组
有几种方法可以遍历数组元素。其中两个最重要的是forEach
和map
。
forEach
遍历整个数组,并将当前元素和它的索引传递给一个函数:
1 | [ 'a', 'b', 'c' ].forEach(function (elem, index) { // (*) |
上面代码的输出
1 | 0. a |
注意(*)
行的函数参数是可省略的。例如:它可以只有一个参数elem
。
map
创建一个新数组,通过给每个存在数组元素应用一个函数:
1 | [1,2,3].map(function (x) { |
深入阅读
十五、正则表达式
JavaScript
内建支持正则表达式。他们被双斜线分隔:
1 | /^abc$/ |
方法 test():测试是否匹配
1 | /^a+b+$/.test('aaab') // true |
方法 exec():匹配和捕获组
1 | /a(b+)a/.exec('_abbba_aba_') // [ 'abbba', 'bbb' ] |
返回的数组第一项(索引为0)是完整匹配,捕获的第一个分组在第二项(索引为1),等。有一种方法可以反复调用获取所有匹配。
方法 replace():搜索并替换
1 | '<a> <bbb>'.replace(/<(.*?)>/g, '[$1]') // '[a] [bbb]' |
replace
的第一个参数必须是正则表达式,并且开启全局搜索(/g
标记),否则仅第一个匹配项会被替换。有一种方法使用一个函数来计算替换项。
十六、数学
Math是一个有算数功能的对象。例如:
1 | Math.abs(-2) // 2 |
十七、标准库的其他功能
JavaScript
标准库相对简单,但有很多其他东西你可以使用:
Date:日期构造函数,主要功能有转换和创建日期字符串,访问日期组成部分(年,小时等)。
JSON:一个对象,功能是转换和生成JSON
数据。
console.*方法:浏览器的具体方法,不是语言成分的部分,但他们也可以在Node.js中工作。
十八、下一步学什么?
在你学会了这篇文章的基础教程后,你可以转到大部分章节末尾提到的高级教程。此外,我建议你看下面的资源:
- Style guides: I have written a guide to style guides
- Underscore.js: 一个弥补JavaScript标准库缺少的功能的库
- JSbooks – free JavaScript books
- Frontend rescue: how to keep up to date on frontend technologies
- http://yanhaijing.com 当然还有我的博客也非常不错哦
- http://yanhaijing.com/es5 如果你想成为高手,我建议阅读
ecmascript
规范 - 给javascript初学者的24条最佳实践
- 我希望我知道的七个JavaScript技巧
参考自原文:http://www.2ality.com/2013/06/basic-javascript.html
参考自译文:http://yanhaijing.com/basejs/
本文章由blinkfox原创,本站仅仅为学习转发