大话 JavaScript(Speaking JavaScript):第一章到第五章

第一部分:JavaScript 快速入门

原文:I. JavaScript Quick Start

译者:飞龙

协议:CC BY-NC-SA 4.0

这部分是 JavaScript 的一个独立快速介绍。你可以在不阅读本书中的其他内容的情况下理解它,本书的其他部分也不依赖于它的内容。然而,阅读本书的提示在阅读本书的提示中适用。

第一章:基本 JavaScript

原文:1. Basic JavaScript

译者:飞龙

协议:CC BY-NC-SA 4.0

本章是关于“基本 JavaScript”,这是我为 JavaScript 的一个子集选择的名称,尽可能简洁,同时仍然能让你高效地工作。当你开始学习 JavaScript 时,我建议你在学习其他语言之前先在其中编程一段时间。这样,你就不必一次学习所有内容,这可能会让人困惑。

背景

本节简要介绍了 JavaScript 的背景,以帮助你理解它为什么是这样的。

JavaScript 与 ECMAScript

ECMAScript是 JavaScript 的官方名称。之所以需要一个新名称,是因为Java有商标(最初由 Sun 持有,现在由 Oracle 持有)。目前,Mozilla 是少数几家被允许正式使用JavaScript名称的公司之一,因为它很久以前就获得了许可证。对于常见用法,以下规则适用:

  • JavaScript意味着编程语言。

  • ECMAScript是语言规范的官方名称。因此,每当提到语言的版本时,人们都说ECMAScript。JavaScript 的当前版本是 ECMAScript 5;ECMAScript 6 目前正在开发中。

影响和语言的性质

JavaScript 的创造者 Brendan Eich 别无选择,只能很快地创建这种语言(否则,Netscape 可能会采用其他更糟糕的技术)。他从几种编程语言中借鉴了一些东西:Java(语法,原始值与对象),Scheme 和 AWK(一级函数),Self(原型继承),以及 Perl 和 Python(字符串,数组和正则表达式)。

JavaScript 在 ECMAScript 3 之前没有异常处理,这就解释了为什么语言经常自动转换值并经常悄悄失败:最初它无法抛出异常。

一方面,JavaScript 有一些怪癖,缺少相当多的功能(块作用域变量,模块,支持子类等)。另一方面,它有几个强大的功能,可以让你解决这些问题。在其他语言中,你学习语言特性。在 JavaScript 中,你经常学习模式而不是语言特性。

鉴于它的影响,毫不奇怪 JavaScript 可以实现一种混合了函数式编程(高阶函数;内置的mapreduce等)和面向对象编程(对象,继承)的编程风格。

语法

本节解释了 JavaScript 的基本语法原则。

语法概述

一些语法的例子:

// Two slashes start single-line comments

var x;  // declaring a variable

x = 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 statement
if (x === 0) {  // Is `x` equal to zero?
    x = 123;
}

// Defining function `baz` with parameters `a` and `b`
function baz(a, b) {
    return a + b;
}

注意等号的两种不同用法:

  • 单个等号(=)用于将值赋给变量。

  • 三个等号(===)用于比较两个值(参见相等运算符)。

语句与表达式

要理解 JavaScript 的语法,你应该知道它有两个主要的语法类别:语句和表达式:

  • 语句“做事情”。程序是一系列语句。这是一个语句的例子,它声明(创建)一个变量foo

    var foo;
    
  • 表达式产生值。它们是函数参数,赋值的右侧等等。这是一个表达式的例子:

    3 * 7
    

语句和表达式之间的区别最好通过 JavaScript 有两种不同的if-then-else的方式来说明——作为语句:

var x;
if (y >= 0) {
    x = y;
} else {
    x = -y;
}

或作为一个表达式:

var x = y >= 0 ? y : -y;

你可以将后者用作函数参数(但不能使用前者):

myFunction(y >= 0 ? y : -y)

最后,无论 JavaScript 在哪里期望一个语句,你也可以使用一个表达式;例如:

foo(7, 1);

整行是一个语句(所谓的表达式语句),但函数调用foo(7, 1)是一个表达式。

分号

在 JavaScript 中,分号是可选的。但是,我建议始终包括它们,因为否则 JavaScript 可能会错误猜测语句的结束。详细信息请参见自动分号插入。

分号终止语句,但不终止块。有一种情况下,您会在块后看到一个分号:函数表达式是以块结尾的表达式。如果这样的表达式出现在语句的最后,它后面会跟着一个分号:

// Pattern: var _ = ___;
var x = 3 * 7;
var f = function () { };  // function expr. inside var decl.

注释

JavaScript 有两种注释:单行注释和多行注释。单行注释以//开头,并在行尾终止:

x++; // single-line comment

多行注释由/**/界定:

/* This is
 a multiline
 comment.
 */

变量和赋值

在 JavaScript 中,变量在使用之前被声明:

var foo;  // declare variable `foo`

赋值

您可以声明一个变量并同时赋值:

var foo = 6;

您也可以给现有变量赋值:

foo = 4;  // change variable `foo`

复合赋值运算符

有复合赋值运算符,比如+=。以下两个赋值是等价的:

x += 1;
x = x + 1;

标识符和变量名

标识符是在 JavaScript 中扮演各种语法角色的名称。例如,变量的名称是标识符。标识符区分大小写。

大致而言,标识符的第一个字符可以是任何 Unicode 字母、美元符号($)或下划线(_)。随后的字符还可以是任何 Unicode 数字。因此,以下都是合法的标识符:

arg0
_tmp
$elem
π

以下标识符是保留字——它们是语法的一部分,不能用作变量名(包括函数名和参数名):

arguments break case catch
class const continue debugger
default delete do else
enum export extends false
finally for function if
implements import in instanceof
interface let new null
package private protected public
return static super switch
this throw true try
typeof var void while

以下三个标识符不是保留字,但您应该将它们视为保留字:

| Infinity |
| NaN |
| undefined |

最后,您还应该避免使用标准全局变量的名称(参见第二十三章)。您可以将它们用于局部变量而不会破坏任何东西,但您的代码仍然会变得混乱。

JavaScript 有许多我们从编程语言中期望的值:布尔值、数字、字符串、数组等等。JavaScript 中的所有值都有属性。每个属性都有一个(或名称)和一个。您可以将属性视为记录的字段。您可以使用点(.)运算符来读取属性:

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 在值之间做了一个相当武断的区分:

  • 原始值是布尔值、数字、字符串、nullundefined

  • 所有其他值都是对象

两者之间的一个主要区别是它们的比较方式;每个对象都有唯一的标识,并且只有(严格)等于自身:

> 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

接下来的两节将更详细地解释原始值和对象。

原始值

以下是所有原始值(或简称原始值):

  • 布尔值:truefalse(参见布尔值)

  • 数字:17361.351(参见数字)

  • 字符串:'abc'"abc"(参见字符串)

  • 两个“非值”:undefinednull(参见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'
    }
    

前面的对象有两个属性:属性firstName的值为'Jane',属性lastName的值为'Doe'

  • 数组,可以通过数组字面量创建(参见数组):

    [ 'apple', 'banana', 'cherry' ]
    

前面的数组有三个元素,可以通过数字索引访问。例如,'apple’的索引是 0。

  • 正则表达式,可以通过正则表达式字面量创建(参见正则表达式):

    /^a+b+$/
    

对象具有以下特征:

按引用比较

进行身份比较;每个值都有自己的身份:

> {} === {}  // two different empty objects
false

> var obj1 = {};
> var obj2 = obj1;
> obj1 === obj2
true

默认可变

通常可以自由更改,添加和删除属性(参见单个对象):

> var obj = {};
> obj.foo = 123; // add property `foo`
> obj.foo
123

undefined 和 null

大多数编程语言都有表示缺少信息的值。JavaScript 有两个这样的“非值”,undefinednull

  • undefined表示“没有值”。未初始化的变量是undefined

    > var foo;
    > foo
    undefined
    

缺少参数是undefined

    > function f(x) { return x }
    > f()
    undefined
    ```

如果读取不存在的属性,将得到`undefined````js
    > var obj = {}; // empty object
    > obj.foo
    undefined
    ```

+   `null`表示“没有对象”。每当期望对象时(参数,对象链中的最后一个等),它被用作非值。

### 警告

`undefined``null`没有属性,甚至没有标准方法,如`toString()`。

#### 检查 undefined 或 null

函数通常允许您通过`undefined``null`指示缺少值。您可以通过显式检查来做相同的事情:

```js
if (x === undefined || x === null) {
    ...
}

您还可以利用undefinednull都被视为false的事实:

if (!x) {
    ...
}

警告

false0NaN''也被视为false(参见真值和假值)。

使用 typeof 和 instanceof 对值进行分类

有两个用于对值进行分类的运算符:typeof主要用于原始值,而instanceof用于对象。

typeof看起来像这样:

typeof value

它返回描述value“类型”的字符串。以下是一些示例:

> typeof true
'boolean'
> typeof 'abc'
'string'
> typeof {} // empty object literal
'object'
> typeof [] // empty array literal
'object'

以下表列出了typeof的所有结果:

操作数 结果
undefined 'undefined'
null 'object'
布尔值 'boolean'
数字值 'number'
字符串值 'string'
函数 'function'
所有其他正常值 'object'
(引擎创建的值) JavaScript 引擎允许创建值,其typeof返回任意字符串(与此表中列出的所有结果都不同)。

typeof null返回'object'是一个无法修复的错误,因为这会破坏现有的代码。这并不意味着null是一个对象。

instanceof看起来像这样:

value instanceof Constr

如果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

布尔值

原始布尔类型包括值truefalse。以下运算符产生布尔值:

  • 二进制逻辑运算符:&&(与),||(或)

  • 前缀逻辑运算符:!(非)

  • 比较运算符:

  • 相等运算符:===!====!=

  • 排序运算符(用于字符串和数字):>, >=, <, <=

真值和假值

每当 JavaScript 期望布尔值(例如if语句的条件)时,可以使用任何值。它将被解释为truefalse。以下值被解释为false

  • undefinednull

  • 布尔值:false

  • 数字:-0NaN

  • 字符串:''

所有其他值(包括所有对象!)都被认为是true。被解释为false的值称为假值,被解释为true的值称为真值Boolean()作为函数调用,将其参数转换为布尔值。您可以使用它来测试值的解释方式:

> Boolean(undefined)
false
> Boolean(0)
false
> Boolean(3)
true
> Boolean({}) // empty object
true
> Boolean([]) // empty array
true

二进制逻辑运算符

JavaScript 中的二进制逻辑运算符是短路的。也就是说,如果第一个操作数足以确定结果,第二个操作数将不会被评估。例如,在以下表达式中,函数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
NaN

Infinity

也是大多数错误值:

> 3 / 0
Infinity
> Math.pow(2, 1024)  // number too large
Infinity

Infinity大于任何其他数字(除了NaN)。同样,-Infinity小于任何其他数字(除了NaN)。这使得这些数字在作为默认值时非常有用(例如,当你正在寻找最小值或最大值时)。

运算符

JavaScript 有以下算术运算符(参见算术运算符):

  • 加法:number1 + number2

  • 减法:number1 - number2

  • 乘法:number1 * number2

  • 除法:number1 / number2

  • 余数:number1 % number2

  • 增量:++variable, variable++

  • 递减:--variable, variable--

  • 否定:-value

  • 转换为数字:+value

全局对象Math(参见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.'

字符串方法

字符串有许多有用的方法(参见字符串原型方法)。以下是一些例子:

> 'abc'.slice(1)  // copy a substring
'bc'
> 'abc'.slice(1, 2)
'b'

> '\t xyz  '.trim()  // trim whitespace
'xyz'

> 'mjölnir'.toUpperCase()
'MJÖLNIR'

> 'abc'.indexOf('b')  // find a string
1
> 'abc'.indexOf('x')
-1

语句

JavaScript 中的条件和循环在以下部分介绍。

条件

if语句有一个then子句和一个可选的else子句,根据布尔条件执行:

if (myvar === 0) {
    // then
}

if (myvar === 0) {
    // then
} else {
    // else
}

if (myvar === 0) {
    // then
} else if (myvar === 1) {
    // else-if
} else if (myvar === 2) {
    // else-if
} else {
    // else
}

我建议始终使用大括号(它们表示零个或多个语句的块)。但如果一个子句只是一个语句,你不必这样做(对于控制流语句forwhile也是如此):

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 (var i=0; i < arr.length; i++) {
    console.log(arr[i]);
}

while循环在其条件成立时继续循环其主体:

// Same as for loop above:
var i = 0;
while (i < arr.length) {
    console.log(arr[i]);
    i++;
}

do-while循环在其条件成立时继续循环其主体。由于条件跟随主体,因此主体始终至少执行一次:

do {
    // ...
} while (condition);

在所有循环中:

  • break离开循环。

  • continue开始新的循环迭代。

函数

定义函数的一种方式是通过函数声明

function add(param1, param2) {
    return param1 + param2;
}

前面的代码定义了一个函数add,它有两个参数param1param2,并返回这两个参数的总和。这是如何调用该函数的:

> add(6, 1)
7
> add('a', 'b')
'ab'

定义add()的另一种方式是通过将函数表达式分配给变量add

var add = function (param1, param2) {
    return param1 + param2;
};

函数表达式产生一个值,因此可以直接用于将函数作为参数传递给其他函数:

someOtherFunction(function (p1, p2) { ... });

函数声明被提升

函数声明是提升的-完整地移动到当前范围的开头。这允许您引用稍后声明的函数:

function foo() {
    bar();  // OK, bar is hoisted
    function bar() {
        ...
    }
}

请注意,虽然var声明也被提升(参见变量被提升),但是它们执行的赋值不会:

function foo() {
    bar();  // Not OK, bar is still undefined
    var bar = 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()显示在将参数转换为数组中):

function f(x, y) {
    console.log(x, y);
    return toArray(arguments);
}

将忽略额外的参数(除了arguments):

> f('a', 'b', 'c')
a b
[ 'a', 'b', 'c' ]

缺少参数将获得值undefined

> f('a')
a undefined
[ 'a' ]
> f()
undefined undefined
[]

可选参数

以下是为参数分配默认值的常见模式:

function pair(x, y) {
    x = x || 0;  // (1)
    y = y || 0;
    return [ x, y ];
}

在第(1)行,||运算符返回x,如果它是真值(不是nullundefined等)。否则,它将返回第二个操作数:

> pair()
[ 0, 0 ]
> pair(3)
[ 3, 0 ]
> pair(3, 5)
[ 3, 5 ]

强制参数个数

如果要强制执行arity(特定数量的参数),可以检查arguments.length

function pair(x, y) {
    if (arguments.length !== 2) {
        throw new Error('Need exactly 2 arguments');
    }
    ...
}

将参数转换为数组

arguments不是数组,它只是类似数组(参见类似数组对象和通用方法)。它有一个length属性,您可以通过方括号中的索引访问其元素。但是,您无法删除元素或调用其中任何数组方法。因此,有时需要将arguments转换为数组,这就是以下函数所做的事情(它在类似数组对象和通用方法中有解释):

function toArray(arrayLikeObject) {
    return Array.prototype.slice.call(arrayLikeObject);
}

异常处理

处理异常的最常见方法(参见第十四章)如下:

function getPerson(id) {
    if (id < 0) {
        throw new Error('ID must not be negative: '+id);
    }
    return { id: id }; // normally: retrieved from database
}

function getPersons(ids) {
    var result = [];
    ids.forEach(function (id) {
        try {
            var person = getPerson(id);
            result.push(person);
        } catch (exception) {
            console.log(exception);
        }
    });
    return result;
}

try子句包围关键代码,如果在try子句内抛出异常,则执行catch子句。使用前面的代码:

> getPersons([2, -5, 137])
[Error: ID must not be negative: -5]
[ { id: 2 }, { id: 137 } ]

严格模式

严格模式(参见严格模式)启用更多警告,并使 JavaScript 成为一种更干净的语言(非严格模式有时被称为“松散模式”)。要打开它,请首先在 JavaScript 文件或

你可能感兴趣的:(javascript,javascript,udp,开发语言)