本章介绍“基础 JavaScript”,这是我对 JavaScript 子集的命名,它尽可能简洁,同时又能让你高效工作。当你开始学习 JavaScript 时,我建议你先用它编程一段时间,然后再学习该语言的其余部分。这样,你就不必一次性学习所有内容,避免混淆。
本节介绍 JavaScript 的一些背景知识,帮助你理解其原因。
ECMAScript 是 JavaScript 的官方名称。之所以需要一个新名称,是因为 JavaScript 是一个商标(最初由 Sun 公司持有,现在由 Oracle 公司持有)。 目前,Mozilla 是少数几家被允许正式使用 JavaScript 名称的公司之一,因为它很久以前就获得了许可。对于一般用途,适用以下规则:
JavaScript 的创建者 Brendan Eich 别无选择,只能非常快地创建这门语言(否则 Netscape 就会采用其他更糟糕的技术)。他借鉴了几种编程语言:Java(语法、原始值与对象)、Scheme 和 AWK(一等函数)、Self(原型继承)以及 Perl 和 Python(字符串、数组和正则表达式)。
JavaScript 直到 ECMAScript 3 才支持异常 处理,这解释了为什么该语言经常自动转换值并且经常静默失败:它最初无法抛出异常。
一方面,JavaScript 有一些怪癖,并且缺少很多功能(块级作用域变量、模块、对子类化的支持等)。另一方面,它有几个强大的功能,可以让你解决这些问题。在其他语言中,你需要学习语言特性。而在 JavaScript 中,你通常学习的是模式。
考虑到它的影响,JavaScript 能够实现函数式编程(高阶函数;内置 map、reduce 等)和面向对象编程(对象、继承)相结合的编程风格也就不足为奇了。
一些语法 示例:
// Two slashes start single-line commentsvarx;// declaring a variablex=3+y;// assigning a value to the variable `x`foo(x,y);// calling function `foo` with parameters `x` and `y`obj.bar(3);// calling method `bar` of object `obj`// A conditional statementif(x===0){// Is `x` equal to zero?x=123;}// Defining function `baz` with parameters `a` and `b`functionbaz(a,b){returna+b;}
请注意等号的两种不同用法:
=) 用于为变量赋值。===) 用于比较两个值(请参阅 相等运算符)。要理解 JavaScript 的语法, 你需要知道它有两个主要的语法类别:语句和表达式:
语句“做事情”。程序是由一系列语句组成的。下面是一个语句的例子,它声明(创建)了一个变量 foo
varfoo;
表达式产生值。它们是函数参数、赋值的右侧等等。下面是一个表达式的例子
3*7
语句和表达式之间的区别可以用 JavaScript 有两种不同的方式来实现 if-then-else 来最好地说明——要么作为语句:
varx;if(y>=0){x=y;}else{x=-y;}
要么作为表达式
varx=y>=0?y:-y;
你可以将后者用作函数参数(但不能将前者用作函数参数)
myFunction(y>=0?y:-y)
最后,在 JavaScript 期望使用语句的地方,你也可以使用表达式;例如
foo(7,1);
整行是一个语句(所谓的 表达式语句),但 函数调用 foo(7, 1) 是一个表达式。
在 JavaScript 中,分号是可选的。但是,我建议始终包含它们,因为否则 JavaScript 可能会错误地猜测语句的结尾。 详细信息请参阅 自动分号插入。
分号用于终止语句,但不用于终止代码块。有一种情况下,你会在代码块后面看到分号:函数表达式是一个以代码块结尾的表达式。 如果这样的表达式出现在语句的最后,则后面跟着一个分号:
// Pattern: var _ = ___;varx=3*7;varf=function(){};// function expr. inside var decl.
JavaScript 有两种 注释:单行注释和多行注释。单行注释以 // 开头,并以行尾终止:
x++;// single-line comment
多行注释 由 /* 和 */ 分隔:
/* This isa multilinecomment.*/
JavaScript 中的变量在 使用之前声明:
varfoo;// declare variable `foo`
标识符 是在 JavaScript 中扮演各种语法角色的名称。 例如,变量的名称就是一个标识符。标识符区分大小写。
粗略地说,标识符的第一个字符可以是任何 Unicode 字母、美元符号 ($) 或下划线 (_)。后续字符还可以是任何 Unicode 数字。因此,以下都是合法的标识符
arg0_tmp$elemπ
以下标识符是 保留字——它们是语法的一部分,不能用作 变量名(包括函数名和参数名):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
以下三个标识符不是保留字,但你应该像对待保留字一样对待它们
|
|
|
最后,你还应该避免使用标准全局变量的名称(请参阅 第 23 章)。 你可以将它们用于局部变量而不会破坏任何内容,但你的代码仍然会变得混乱。
JavaScript 有许多我们期望编程语言拥有的值 :布尔值、数字、字符串、数组等等。 JavaScript 中的所有值都有 属性。[1] 每个属性都有一个 键(或 名称)和一个 值。你可以将属性视为记录的字段。你使用点 (.) 运算符来读取属性
value.propKey
例如,字符串 'abc' 具有属性 length
> var str = 'abc'; > str.length 3
以上代码也可以写成
> 'abc'.length 3
点运算符还用于为 属性赋值:
> var obj = {}; // empty object
> obj.foo = 123; // create property `foo`, set it to 123
123
> obj.foo
123你还可以使用它来 调用方法:
> 'hello'.toUpperCase() 'HELLO'
在上面的例子中,我们调用了值 'hello' 上的方法 toUpperCase()。
JavaScript 对值进行了一些武断的区分:
null 和 undefined。两者之间的一个主要区别在于它们的比较方式; 每个对象都有一个唯一的标识,并且 只(严格)等于它自己:
> var obj1 = {}; // an empty object
> var obj2 = {}; // another empty object
> obj1 === obj2
false
> obj1 === obj1
true相反,所有编码相同值的原始值都被认为是相同的
> var prim1 = 123; > var prim2 = 123; > prim1 === prim2 true
接下来的两节将更详细地解释原始值和对象。
以下是所有原始值(或简称 基元):
true、false(请参阅 布尔值)1736、1.351(请参阅 数字)'abc'、"abc"(请参阅 字符串)undefined、null(请参阅 undefined 和 null)原始值具有以下 特点:
比较的是“内容”
> 3 === 3 true > 'abc' === 'abc' true
属性不能 更改、添加或删除:
> var str = 'abc'; > str.length = 1; // try to change property `length` > str.length // ⇒ no effect 3 > str.foo = 3; // try to create property `foo` > str.foo // ⇒ no effect, unknown property undefined
(读取未知属性始终返回 undefined。)
所有非原始 值都是 对象。最常见的对象类型是:
普通对象,可以通过 对象字面量 创建(请参阅 单个对象)
{firstName:'Jane',lastName:'Doe'}
数组,可以通过 数组字面量 创建(请参阅 数组)
['apple','banana','cherry']
上面的数组有三个元素,可以通过数字索引访问。例如,'apple' 的索引为 0。
正则表达式,可以通过 正则表达式字面量 创建(请参阅 正则表达式)
/^a+b+$/对象具有以下 特点:
大多数编程语言都有表示缺失信息的值。JavaScript 有两个这样的“非值”,undefined 和 null:
undefined 表示“无值”。未初始化的变量为 undefined
> var foo; > foo undefined
缺失的参数为 undefined
> function f(x) { return x }
> f()
undefined如果读取不存在的属性,则会得到 undefined
> var obj = {}; // empty object
> obj.foo
undefinednull 表示“无对象”。每当预期出现对象时(参数、对象链中的最后一个等),它都被用作非值。undefined 和 null 没有属性,甚至没有标准方法,例如 toString()。
函数通常允许您通过 undefined 或 null 来指示缺失值。您可以通过显式检查来执行相同的操作:
if(x===undefined||x===null){...}
您还可以利用 undefined 和 null 都被视为 false 的事实
if(!x){...}
false、0、NaN 和 '' 也被视为 false(请参阅真值和假值)。
有两个运算符用于对值进行分类:typeof 主要用于原始值,而 instanceof 用于对象。
typeof 如下所示
typeofvalue
它返回一个描述 value“类型”的字符串。以下是一些示例
> typeof true
'boolean'
> typeof 'abc'
'string'
> typeof {} // empty object literal
'object'
> typeof [] // empty array literal
'object'下表列出了 typeof 的所有结果
| 操作数 | 结果 |
|
|
|
|
布尔值 |
|
数字值 |
|
字符串值 |
|
函数 |
|
所有其他正常值 |
|
(引擎创建的值) | JavaScript 引擎允许创建 |
typeof null 返回 'object' 是一个无法修复的错误,因为它会破坏现有代码。这并不意味着 null 是一个对象。
instanceof 如下所示
valueinstanceofConstr
如果 value 是由构造函数 Constr 创建的对象,则返回 true(请参阅构造函数:对象的工厂)。以下是一些示例
> var b = new Bar(); // object created by constructor Bar
> b instanceof Bar
true
> {} instanceof Object
true
> [] instanceof Array
true
> [] instanceof Object // Array is a subconstructor of Object
true
> undefined instanceof Object
false
> null instanceof Object
false原始布尔类型包含值 true 和 false。以下运算符产生布尔值:
&&(与)、||(或)!(非)比较运算符
===、!==、==、!=>、>=、<、<=每当 JavaScript 需要布尔值时(例如,对于 if 语句的条件),都可以使用任何值。它将被解释为 true 或 false。以下值被解释为 false
undefined、nullfalse0、NaN''所有其他值(包括所有对象!)都被视为 true。被解释为 false 的值称为假值,被解释为 true 的值称为真值。Boolean() 作为函数调用时,会将其参数转换为布尔值。您可以使用它来测试如何解释值
> Boolean(undefined)
false
> Boolean(0)
false
> Boolean(3)
true
> Boolean({}) // empty object
true
> Boolean([]) // empty array
trueJavaScript 中的二元逻辑运算符是短路的。也就是说,如果第一个操作数足以确定结果,则不会评估第二个操作数。例如,在以下表达式中,永远不会调用函数 foo():
false&&foo()true||foo()
此外,二元逻辑运算符返回其操作数之一,该操作数可能是也可能不是布尔值。使用真值检查来确定返回哪个操作数
&&)> NaN && 'abc' NaN > 123 && 'abc' 'abc'
||)> 'abc' || 123 'abc' > '' || 123 123
JavaScript 有两种相等性:
== 和 !==== 和 !==普通相等将(太多)值视为相等(详细信息在普通(宽松)相等 (==, !=) 中解释),这可能会隐藏错误。因此,建议始终使用严格相等。
JavaScript 中的所有数字都是浮点数:
> 1 === 1.0 true
特殊数字包括以下内容:
NaN(“非数字”)错误值
> Number('xyz') // 'xyz' can’t be converted to a number
NaNInfinity
也主要是一个错误值:
> 3 / 0 Infinity > Math.pow(2, 1024) // number too large Infinity
Infinity 大于任何其他数字(NaN 除外)。类似地,-Infinity 小于任何其他数字(NaN 除外)。这使得这些数字可用作默认值(例如,当您正在寻找最小值或最大值时)。
JavaScript 具有以下算术运算符(请参阅算术运算符)
number1 + number2number1 - number2number1 * number2number1 / number2number1 % number2++variable、variable++--variable、variable---value+value全局对象 Math(请参阅数学)通过函数提供更多算术运算。
JavaScript 还具有用于按位运算的运算符(例如,按位与;请参阅按位运算符)。
可以直接通过字符串字面量创建字符串。这些字面量用单引号或双引号分隔。反斜杠 (\) 对字符进行转义并生成一些控制字符。以下是一些示例:
'abc'"abc"'Did she say "Hello"?'"Did she say \"Hello\"?"'That\'s nice!'"That's nice!"'Line 1\nLine 2'// newline'Backlash: \\'
> var str = 'abc'; > str[1] 'b'
属性 length 统计字符串中的字符数:
> 'abc'.length 3
与所有原始类型一样,字符串是不可变的;如果要更改现有字符串,则需要创建一个新字符串。
字符串通过加号 (+) 运算符进行连接,如果其中一个操作数是字符串,则将另一个操作数转换为字符串:
> var messageCount = 3; > 'You have ' + messageCount + ' messages' 'You have 3 messages'
> var str = ''; > str += 'Multiple '; > str += 'pieces '; > str += 'are concatenated.'; > str 'Multiple pieces are concatenated.'
以下部分介绍 JavaScript 中的条件语句和循环。
if 语句有一个 then 子句和一个可选的 else 子句,它们根据布尔条件执行:
if(myvar===0){// then}if(myvar===0){// then}else{// else}if(myvar===0){// then}elseif(myvar===1){// else-if}elseif(myvar===2){// else-if}else{// else}
我建议始终使用大括号(它们表示零个或多个语句的块)。但是,如果子句只是一个语句,则不必这样做(控制流语句 for 和 while 也是如此):
if(x<0)return-x;
以下是一个 switch 语句。fruit 的值决定执行哪个 case:
switch(fruit){case'banana':// ...break;case'apple':// ...break;default:// all other cases// ...}
case 后的“操作数”可以是任何表达式;它通过 === 与 switch 的参数进行比较。
for 循环具有以下格式:
for(⟦«init»⟧;⟦«condition»⟧;⟦«post_iteration»⟧)«statement»
init 在循环开始时执行。condition 在每次循环迭代之前检查;如果它变为 false,则循环终止。post_iteration 在每次循环迭代之后执行。
此示例在控制台上打印数组 arr 的所有元素
for(vari=0;i<arr.length;i++){console.log(arr[i]);}
while 循环在其条件成立时继续循环执行其主体:
// Same as for loop above:vari=0;while(i<arr.length){console.log(arr[i]);i++;}
do-while 循环在其条件成立时继续循环执行其主体。由于条件在主体之后,因此主体始终至少执行一次:
do{// ...}while(condition);
在所有循环中:
break 退出循环。continue 开始新的循环迭代。定义函数的一种方法是通过函数声明
functionadd(param1,param2){returnparam1+param2;}
前面的代码定义了一个函数 add,它有两个参数 param1 和 param2,并返回两个参数的和。以下是调用该函数的方法
> add(6, 1)
7
> add('a', 'b')
'ab'另一种定义 add() 的方法是将函数表达式分配给变量 add:
varadd=function(param1,param2){returnparam1+param2;};
函数表达式产生一个值,因此可以用来直接将函数作为参数传递给其他函数
someOtherFunction(function(p1,p2){...});
函数声明被提升——整体移动到当前作用域的开头。这允许您引用稍后声明的函数:
functionfoo(){bar();// OK, bar is hoistedfunctionbar(){...}}
请注意,虽然 var 声明也被提升(请参阅变量被提升),但它们执行的赋值却没有
functionfoo(){bar();// Not OK, bar is still undefinedvarbar=function(){// ...};}
您可以在 JavaScript 中使用任意数量的参数调用任何函数;该语言永远不会报错。但是,它会将 所有参数通过特殊变量 arguments 提供。 arguments 看起来像一个数组,但没有任何数组方法:
> function f() { return arguments }
> var args = f('a', 'b', 'c');
> args.length
3
> args[0] // read element at index 0
'a'让我们使用 以下函数来探讨 JavaScript 中如何处理参数过多或过少的情况(函数 toArray() 显示在 将参数转换为数组 中)
functionf(x,y){console.log(x,y);returntoArray(arguments);}
额外的参数将被忽略(arguments 除外)
> f('a', 'b', 'c')
a b
[ 'a', 'b', 'c' ]缺少的参数将 获取值 undefined:
> f('a')
a undefined
[ 'a' ]
> f()
undefined undefined
[]以下是 为参数分配默认值的常见模式:
functionpair(x,y){x=x||0;// (1)y=y||0;return[x,y];}
在第 (1) 行中,如果 x 为真值(不是 null、undefined 等),则 || 运算符返回 x。否则,它返回第二个操作数
> pair() [ 0, 0 ] > pair(3) [ 3, 0 ] > pair(3, 5) [ 3, 5 ]
如果您想 强制执行 参数个数(特定数量的参数),您可以检查 arguments.length:
functionpair(x,y){if(arguments.length!==2){thrownewError('Need exactly 2 arguments');}...}
arguments 不是数组,它只是 类数组(请参阅 类数组对象和泛型方法)。它有一个属性 length,您可以通过方括号中的索引访问其元素。 但是,您不能删除元素或在其上调用任何数组方法。因此,您有时需要将 arguments 转换为数组,这就是以下函数的作用(在 类数组对象和泛型方法 中有解释)
functiontoArray(arrayLikeObject){returnArray.prototype.slice.call(arrayLikeObject);}
functiongetPerson(id){if(id<0){thrownewError('ID must not be negative: '+id);}return{id:id};// normally: retrieved from database}functiongetPersons(ids){varresult=[];ids.forEach(function(id){try{varperson=getPerson(id);result.push(person);}catch(exception){console.log(exception);}});returnresult;}
try 子句包围关键代码, 如果在 try 子句内部抛出异常,则执行 catch 子句。使用前面的代码:
> getPersons([2, -5, 137])
[Error: ID must not be negative: -5]
[ { id: 2 }, { id: 137 } ]严格模式(请参阅 严格模式)启用更多警告,并使 JavaScript 成为更简洁的语言(非严格模式有时称为“草率模式”)。 要启用它,请在 JavaScript 文件或 <script> 标记中首先键入以下行:
'use strict';
您也可以 为每个函数启用严格模式:
functionfunctionInStrictMode(){'use strict';}
在 JavaScript 中,您在使用 变量之前通过 var 声明它们:
> var x; > x undefined > y ReferenceError: y is not defined
您可以使用单个 var 语句声明和初始化多个变量
varx=1,y=2,z=3;
但我建议每个变量使用一个语句(原因在 语法 中有解释)。因此,我将把前面的语句重写为
varx=1;vary=2;varz=3;
由于提升(请参阅 变量被提升),通常最好在函数的开头声明变量。
变量的作用域始终是完整的函数(而不是当前块)。 例如:
functionfoo(){varx=-512;if(x<0){// (1)vartmp=-x;...}console.log(tmp);// 512}
我们可以看到变量 tmp 不限于从第 (1) 行开始的块;它一直存在到函数结束。
每个变量声明都被 提升: 声明被移动到函数的开头,但它进行的赋值保持不变。例如,请考虑以下函数中第 (1) 行的变量声明:
functionfoo(){console.log(tmp);// undefinedif(false){vartmp=3;// (1)}}
在内部,前面的函数是这样执行的
functionfoo(){vartmp;// hoisted declarationconsole.log(tmp);if(false){tmp=3;// assignment stays put}}
每个函数 都与其周围函数的变量保持连接,即使它离开了创建它的作用域也是如此。例如:
functioncreateIncrementor(start){returnfunction(){// (1)start++;returnstart;}}
从第 (1) 行开始的函数离开了创建它的上下文,但与 start 的活动版本保持连接
> var inc = createIncrementor(5); > inc() 6 > inc() 7 > inc() 8
闭包 是一个函数加上与其周围作用域的变量的连接。因此,createIncrementor() 返回的是一个闭包。
有时您想引入一个新的 变量作用域——例如,为了防止变量变成全局变量。在 JavaScript 中,您不能使用块来做到这一点;您必须使用函数。但是,有一种模式可以以类似块的方式使用函数。它被称为 IIFE(立即调用函数表达式,发音为“iffy”)
(function(){// open IIFEvartmp=...;// not a global variable}());// close IIFE
请确保完全按照所示键入前面的示例(注释除外)。IIFE 是一个函数表达式,在您定义它之后立即调用。在函数内部,存在一个新的作用域,防止 tmp 变成全局变量。有关 IIFE 的详细信息,请参阅 通过 IIFE 引入新的作用域。
闭包保持与外部变量的 连接,这有时不是您想要的:
varresult=[];for(vari=0;i<5;i++){result.push(function(){returni});// (1)}console.log(result[1]());// 5 (not 1)console.log(result[3]());// 5 (not 3)
第 (1) 行中返回的值始终是 i 的当前值,而不是创建函数时的值。循环结束后,i 的值为 5,这就是为什么数组中的所有函数都返回该值。如果您希望第 (1) 行中的函数接收 i 当前值的快照,您可以使用 IIFE
for(vari=0;i<5;i++){(function(){vari2=i;// copy current iresult.push(function(){returni2});}());}
本节介绍 JavaScript 的两种基本 面向对象机制:单个对象和 构造函数(它们是对象的工厂,类似于其他语言中的类)。
像所有值一样,对象也有属性。 事实上,您可以将对象视为一组属性,其中每个属性都是一个(键,值)对。键是一个字符串,值是任何 JavaScript 值。
在 JavaScript 中,您可以通过 对象字面量 直接创建普通对象
'use strict';varjane={name:'Jane',describe:function(){return'Person named '+this.name;}};
前面的对象具有属性 name 和 describe。您可以读取(“获取”)和写入(“设置”)属性
> jane.name // get 'Jane' > jane.name = 'John'; // set > jane.newProperty = 'abc'; // property created automatically
函数值属性(如 describe)称为 方法。它们使用 this 来引用用于调用它们的对象:
> jane.describe() // call method 'Person named John' > jane.name = 'Jane'; > jane.describe() 'Person named Jane'
in 运算符检查 属性是否存在:
> 'newProperty' in jane true > 'foo' in jane false
如果您读取一个不存在的属性,您将获得值 undefined。因此,前面的两个检查也可以像这样执行:[2]
> jane.newProperty !== undefined true > jane.foo !== undefined false
delete 运算符 删除属性:
> delete jane.newProperty true > 'newProperty' in jane false
属性键可以是任何字符串。 到目前为止,我们已经在对象字面量和点运算符之后看到了属性键。但是,只有当它们是 标识符时,您才能以这种方式使用它们(请参阅 标识符和变量名)。如果要使用其他字符串作为键,则必须在对象字面量中将其引起来,并使用方括号 获取和设置属性:
> var obj = { 'not an identifier': 123 };
> obj['not an identifier']
123
> obj['not an identifier'] = 456;方括号还允许您 计算属性的键:
> var obj = { hello: 'world' };
> var x = 'hello';
> obj[x]
'world'
> obj['hel'+'lo']
'world'如果提取方法,它将失去与对象的连接。 就其本身而言,该函数不再是方法 ,并且 this 的值为 undefined(在严格模式下)。
例如,让我们回到前面的对象 jane
'use strict';varjane={name:'Jane',describe:function(){return'Person named '+this.name;}};
我们想从 jane 中提取方法 describe,将其放入变量 func 中,然后调用它。但是,这不起作用
> var func = jane.describe; > func() TypeError: Cannot read property 'name' of undefined
解决方案是使用 所有函数都具有的方法 bind()。它创建一个新函数,其 this 始终具有给定的值:
> var func2 = jane.describe.bind(jane); > func2() 'Person named Jane'
每个函数都有 其自己的特殊变量 this。如果您在方法内部嵌套函数,这很不方便,因为您无法从函数访问方法的 this。以下是一个示例,我们在其中使用函数调用 forEach 来迭代数组:
varjane={name:'Jane',friends:['Tarzan','Cheeta'],logHiToFriends:function(){'use strict';this.friends.forEach(function(friend){// `this` is undefined hereconsole.log(this.name+' says hi to '+friend);});}}
调用 logHiToFriends 会产生错误
> jane.logHiToFriends() TypeError: Cannot read property 'name' of undefined
让我们看看解决这个问题的两种方法。首先,我们可以将 this 存储在不同的变量中
logHiToFriends:function(){'use strict';varthat=this;this.friends.forEach(function(friend){console.log(that.name+' says hi to '+friend);});}
或者,forEach 有第二个参数,允许您为 this 提供一个值
logHiToFriends:function(){'use strict';this.friends.forEach(function(friend){console.log(this.name+' says hi to '+friend);},this);}
函数表达式通常用作 JavaScript 中函数调用中的参数。 当您从其中一个函数表达式引用 this 时,请务必小心。
到目前为止,您可能认为 JavaScript 对象 只是 从字符串到值的映射,JavaScript 的对象字面量暗示了这一点,它看起来像其他语言中的映射/字典字面量。但是,JavaScript 对象还支持一项真正面向对象的特性:继承。本节没有完全解释 JavaScript 继承的工作原理,但它向您展示了一个简单的模式来帮助您入门。如果您想了解更多信息,请参阅 第 17 章。
除了是“真正的”函数和方法之外,函数 在 JavaScript 中还扮演着另一个角色:如果通过 new 运算符调用,它们就变成了 构造函数——对象的工厂。因此,构造函数大致类似于其他语言中的类。按照惯例,构造函数的名称以大写字母开头。例如:
// Set up instance datafunctionPoint(x,y){this.x=x;this.y=y;}// MethodsPoint.prototype.dist=function(){returnMath.sqrt(this.x*this.x+this.y*this.y);};
我们可以看到构造函数有两部分。首先,函数 Point 设置实例数据。其次,属性 Point.prototype 包含一个具有方法的对象。 前面的数据特定于每个实例,而后面的数据在所有实例之间共享。
要使用 Point,我们 通过 new 运算符调用它:
> var p = new Point(3, 5); > p.x 3 > p.dist() 5.830951894845301
p 是 Point 的一个实例
> p instanceof Point true
数组字面量便于 创建数组:
> var arr = [ 'a', 'b', 'c' ];
前面的数组有三个元素:字符串 'a'、'b' 和 'c'。您可以通过整数索引访问它们
> arr[0] 'a' > arr[0] = 'x'; > arr [ 'x', 'b', 'c' ]
length 属性指示数组有多少个元素。 您可以使用它来追加元素和删除元素:
> var arr = ['a', 'b']; > arr.length 2 > arr[arr.length] = 'c'; > arr [ 'a', 'b', 'c' ] > arr.length 3 > arr.length = 1; > arr [ 'a' ]
in 运算符也适用于 数组:
> var arr = [ 'a', 'b', 'c' ]; > 1 in arr // is there an element at index 1? true > 5 in arr // is there an element at index 5? false
请注意,数组是对象,因此可以具有对象属性:
> var arr = []; > arr.foo = 123; > arr.foo 123
> var arr = [ 'a', 'b', 'c' ];
> arr.slice(1, 2) // copy elements
[ 'b' ]
> arr.slice(1)
[ 'b', 'c' ]
> arr.push('x') // append an element
4
> arr
[ 'a', 'b', 'c', 'x' ]
> arr.pop() // remove last element
'x'
> arr
[ 'a', 'b', 'c' ]
> arr.shift() // remove first element
'a'
> arr
[ 'b', 'c' ]
> arr.unshift('x') // prepend an element
3
> arr
[ 'x', 'b', 'c' ]
> arr.indexOf('b') // find the index of an element
1
> arr.indexOf('y')
-1
> arr.join('-') // all elements in a single string
'x-b-c'
> arr.join('')
'xbc'
> arr.join()
'x,b,c'有几种数组 方法可用于迭代元素(参见 迭代(非破坏性))。最重要的两个是 forEach 和 map。
forEach 迭代数组并将当前元素及其索引传递给函数
['a','b','c'].forEach(function(elem,index){// (1)console.log(index+'. '+elem);});
前面的代码产生以下输出
0. a 1. b 2. c
请注意,第 (1) 行中的函数可以随意忽略参数。例如,它可以只有参数 elem。
map 通过将 函数应用于现有数组的每个元素来创建新数组:
> [1,2,3].map(function (x) { return x*x })
[ 1, 4, 9 ]JavaScript 内置支持正则表达式(第 19 章 涉及教程并更详细地解释了它们的工作原理)。它们由 斜杠分隔:
/^abc$//[A-Za-z0-9]+/
> /^a+b+$/.test('aaab')
true
> /^a+b+$/.test('aaa')
false> /a(b+)a/.exec('_abbba_aba_')
[ 'abbba', 'bbb' ]返回的 数组在索引 0 处包含完全匹配,在索引 1 处包含第一个组的捕获,依此类推。有一种方法(在 RegExp.prototype.exec:捕获组 中讨论)可以重复调用此方法以获取所有匹配项。
> '<a> <bbb>'.replace(/<(.*?)>/g, '[$1]') '[a] [bbb]'
replace 的第一个参数必须是带有 /g 标志的正则表达式;否则,只会替换第一个匹配项。还有一种方法(如 String.prototype.replace:搜索和替换 中所述)可以使用函数来计算替换。
Math(参见 第 21 章)是一个具有算术 函数的对象。以下是一些示例:
> Math.abs(-2) 2 > Math.pow(3, 2) // 3 to the power of 2 9 > Math.max(2, -1, 5) 5 > Math.round(1.9) 2 > Math.PI // pre-defined constant for π 3.141592653589793 > Math.cos(Math.PI) // compute the cosine for 180° -1
JavaScript 的标准库 相对简单,但您可以使用更多内容: