语法

基础

JavaScript 借鉴了 Java 的大部分语法,但同时也受到 Awk,Perl Python的影响。
JavaScript 是区分大小写的,并使用 Unicode 字符集。

  1. var param = [];
  2. var paRam = '';

因为js大小写敏感,所以param和paRam是不同的变量。
在 JavaScript 中,指令被称为语句 Statement,并用分号(;)进行分隔。

如果一条语句独占一行的话,那么分号是可以省略的。(译者注:并不建议这么做。)但如果一行中有多条语句,那么这些语句必须以分号分开。 ECMAScript 规定了在语句的末尾自动插入分号(ASI)。(如果想要了解更多信息,请参阅 JavaScript 词法语法 。)虽然不是必需的,但是在一条语句的末尾加上分号是一个很好的习惯。这个习惯可以大大减少代码中产生 bug 的可能性。
Javascript 源码从左往右被扫描并转换成一系列由 token 、控制字符、行终止符、注释和空白字符组成的输入元素。空白字符指的是空格、制表符和换行符等。

注释

Javascript 注释的语法和Java 或许多其他语言类似:

  1. // 单行注释
  2. /* 这是一个更长的,
  3. 多行注释
  4. */
  5. /* 然而, 你不能, /* 嵌套注释 */ 语法错误 */

声明

JavaScript有三种声明方式。后面两种是ES6新增。
var声明一个变量,可选初始化一个值。
let声明一个块作用域的局部变量,可选初始化一个值。
const声明一个块作用域的只读常量。

声明变量

  1. var a = 20;
  2. var b = 30;
  3. var a = 15;
  4. a = 'test';

变量的作用域

在函数之外声明的变量,叫做全局变量,因为它可被当前文档中的任何其他代码所访问。在函数内部声明的变量,叫做局部变量,因为它只能在当前函数的内部访问。
ECMAScript 6 之前的 JavaScript 没有 语句块 作用域;相反,语句块中声明的变量将成为语句块所在函数(或全局作用域)的局部变量。例如,如下的代码将在控制台输出 5,因为 x 的作用域是声明了 x 的那个函数(或全局范围),而不是 if 语句块。

  1. if (true) {
  2. var x = 5;
  3. }
  4. console.log(x); // 5

如果使用 ECMAScript 6 中的 let 声明,上述行为将发生变化。

  1. if (true) {
  2. let y = 5;
  3. }
  4. console.log(y); // ReferenceError: y 没有被声明

变量提升

JavaScript 变量的另一个不同寻常的地方是,你可以先使用变量稍后再声明变量而不会引发异常。这一概念称为变量提升;JavaScript 变量感觉上是被“提升”或移到了函数或语句的最前面。但是,提升后的变量将返回 undefined 值。因此在使用或引用某个变量之后进行声明和初始化操作,这个被提升的变量仍将返回 undefined 值。

  1. /**
  2. * 例子1
  3. */
  4. console.log(x === undefined); // true
  5. var x = 3;
  6. /**
  7. * 例子2
  8. */
  9. // will return a value of undefined
  10. var myvar = "my value";
  11. (function() {
  12. console.log(myvar); // undefined
  13. var myvar = "local value";
  14. })();

上面的例子,也可写作:

  1. /**
  2. * 例子1
  3. */
  4. var x;
  5. console.log(x === undefined); // true
  6. x = 3;
  7. /**
  8. * 例子2
  9. */
  10. var myvar = "my value";
  11. (function() {
  12. var myvar;
  13. console.log(myvar); // undefined
  14. myvar = "local value";
  15. })();

由于存在变量提升,一个函数中所有的var语句应尽可能地放在接近函数顶部的地方。这个习惯将大大提升代码的清晰度。
在 ECMAScript 6 中,let(const)将不会提升变量到代码块的顶部。因此,在变量声明之前引用这个变量,将抛出引用错误(ReferenceError)。这个变量将从代码块一开始的时候就处在一个“暂时性死区”,直到这个变量被声明为止。

  1. console.log(x); // ReferenceError
  2. let x = 3;

流程控制与错误处理

语句块

最基本的语句是用于组合语句的语句块。该块由一对大括号界定:

  1. {
  2. statement_1;
  3. statement_2;
  4. statement_3;
  5. .
  6. .
  7. .
  8. statement_n;
  9. }

语句块通常用于流程控制,如ifforwhile等等。

  1. while (x < 10) {
  2. x++;
  3. }

这里{ x++; }就是语句块。

条件判断语句

条件判断语句指的是根据指定的条件所返回的结果(真或假或其它预定义的),来执行特定的语句。JavaScript 支持两种条件判断语句:if...elseswitch

if…else 语句

当一个逻辑条件为真,用if语句执行一个语句。当这个条件为假,使用可选择的 else 从句来执行这个语句。if 语句如下所示:

  1. if (condition) {
  2. statement_1;
  3. }else {
  4. statement_2;
  5. } //推荐使用严格的语句块模式,语句else可选

条件可以是任何返回结果被计算为true 或 false的表达式。如果条件表达式返回的是 true,statement_1 语句会被执行;否则,statement_2 被执行。statement_1 和 statement_2 可以是任何语句,甚至你可以将另一个if语句嵌套其中。
你也可以组合语句通过使用 else if 来测试连续多种条件判断,就像下面一样:

  1. if (condition_1) {
  2. statement_1;
  3. }else if (condition_2) {
  4. statement_2;
  5. }else if (condition_n_1) {
  6. statement_n;
  7. }else {
  8. statement_last;
  9. }

要执行多个语句,可以使用语句块({ … }) 来分组这些语句。通常,总是使用语句块是一个好的习惯,特别是在代码涉及比较多的 if 语句时:

  1. if (条件) {
  2. 当条件为真的时候,执行语句1;
  3. 当条件为真的时候,执行语句2;
  4. } else {
  5. 当条件为假的时候,执行语句3;
  6. 当条件为假的时候,执行语句4;
  7. }

错误的值
**下面这些值将被计算出 false (also known as Falsy values):

  • false
  • undefined
  • null
  • 0
  • NaN
  • 空字符串(""

当传递给条件语句所有其他的值,包括所有对象会被计算为真 。

  1. if (false || undefined || null || 0 || NaN || '' ) {
  2. console.log(true);
  3. } else {
  4. console.log(false);
  5. }

请不要混淆原始的布尔值truefalseBoolean对象的真和假。例如:

  1. var b = new Boolean(false);
  2. if (b) //结果视为真
  3. if (b == true) // 结果视为假

switch 语句

switch 语句允许一个程序求一个表达式的值并且尝试去匹配表达式的值到一个 case 标签。如果匹配成功,这个程序执行相关的语句。switch 语句如下所示:

  1. switch (expression) {
  2. case label_1:
  3. statements_1
  4. [break;]
  5. case label_2:
  6. statements_2
  7. [break;]
  8. ...
  9. default:
  10. statements_def
  11. [break;]
  12. }

程序首先查找一个与 expression匹配的 case语句,然后将控制权转移到该子句,执行相关的语句。如果没有匹配值, 程序会去找 default语句,如果找到了,控制权转移到该子句,执行相关的语句。如果没有找到 default,程序会继续执行 switch语句后面的语句。default 语句通常出现在switch语句里的最后面,当然这不是必须的。
可选的 break 语句与每个 case 语句相关联, 保证在匹配的语句被执行后程序可以跳出 switch并且继续执行 switch 后面的语句。如果break被忽略,则程序将继续执行switch语句中的下一条语句。

示例
在如下示例中, 如果 fruittype 等于 “Bananas”, 程序匹配到对应 “Bananas” 的case 语句,并执行相关语句。 当执行到 break 时,程序结束了 switch 并执行 switch 后面的语句。 如果不写 break ,那么程序将会执行 case "Cherries" 下的语句。

  1. function showPrice(fruittype) {
  2. switch (fruittype) {
  3. case "Oranges":
  4. document.write("Oranges are $0.59 a pound.<br>");
  5. break;
  6. case "Apples":
  7. document.write("Apples are $0.32 a pound.<br>");
  8. break;
  9. case "Bananas":
  10. document.write("Bananas are $0.48 a pound.<br>");
  11. break;
  12. case "Cherries":
  13. document.write("Cherries are $3.00 a pound.<br>");
  14. break;
  15. case "Mangoes":
  16. case "Papayas":
  17. document.write("Mangoes and papayas are $2.79 a pound.<br>");
  18. break;
  19. default:
  20. document.write("Sorry, we are out of " + fruittype + ".<br>");
  21. }
  22. document.write("Is there anything else you'd like?<br>");
  23. }

异常处理语句

你可以用 throw 语句抛出一个异常并且用 try...catch 语句捕获处理它。

异常类型

JavaScript 可以抛出任意对象。然而,不是所有对象能产生相同的结果。尽管抛出数值或者字母串作为错误信息十分常见,但是通常用下列其中一种异常类型来创建目标更为高效:

throw 语句

使用throw语句抛出一个异常。当你抛出异常,你规定一个含有值的表达式要被抛出。

throw expression;

你可以抛出任意表达式而不是特定一种类型的表达式。下面的代码抛出了几个不同类型的表达式:

throw "Error2";   // String type
throw 42;         // Number type
throw true;       // Boolean type
throw {toString: function() { return "I'm an object!"; } };

注意:你可以在抛出异常时声明一个对象。那你就可以在catch块中查询到对象的属性。

// Create an object type UserException
function UserException (message){
  this.message=message;
  this.name="UserException";
}
// Make the exception convert to a pretty string when used as
// a string (e.g. by the error console)
UserException.prototype.toString = function (){
  return this.name + ': "' + this.message + '"';
}
// Create an instance of the object type and throw it
throw new UserException("Value too high");

try…catch 语句

try...catch 语句标记一块待尝试的语句,并规定一个以上的响应应该有一个异常被抛出。如果我们抛出一个异常,try...catch语句就捕获它。
try...catch 语句有一个包含一条或者多条语句的try代码块,0个或多个的catch代码块,catch代码块中的语句会在try代码块中抛出异常时执行。 换句话说,如果你在try代码块中的代码如果没有执行成功,那么你希望将执行流程转入catch代码块。如果try代码块中的语句(或者try 代码块中调用的方法)一旦抛出了异常,那么执行流程会立即进入catch 代码块。如果try代码块没有抛出异常,catch代码块就会被跳过。finally 代码块总会紧跟在try和catch代码块之后执行,但会在try和catch代码块之后的其他代码之前执行。
下面的例子使用了try...catch语句。示例调用了一个函数用于从一个数组中根据传递值来获取一个月份名称。如果该值与月份数值不相符,会抛出一个带有"InvalidMonthNo"值的异常,然后在捕捉块语句中设monthName变量为unknown

function myMonthName(myMonth) {
    function getMonthName(mo) {
      mo = mo - 1; // Adjust month number for array index (1 = Jan, 12 = Dec)
      var months = ["Jan","Feb","Mar","Apr","May","Jun","Jul",
                    "Aug","Sep","Oct","Nov","Dec"];
      if (months[mo]) {
        return months[mo];
      } else {
        throw "InvalidMonthNo"; //throw keyword is used here
      }
    }
    try { // statements to try
      monthName = getMonthName(myMonth); // function could throw exception
    }
    catch (e) {
      monthName = "unknown";
      console.error(e); // pass exception object to error handler -> your own function
    }
    return monthName;
}

catch

你可以使用catch块来处理所有可能在try块中产生的异常。

catch (catchID) {
  statements
}

捕捉块指定了一个标识符(上述语句中的catchID)来存放抛出语句指定的值;你可以用这个标识符来获取抛出的异常信息。在插入throw块时JavaScript创建这个标识符;标识符只存在于catch块的存续期间里;当catch块执行完成时,标识符不再可用。
举个例子,下面代码抛出了一个异常。当异常出现时跳到catch块。

try {
   throw "myException" // generates an exception
}
catch (e) {
// statements to handle any exceptions
   logMyErrors(e) // pass exception object to error handler
}

finally

finally块包含了在try和catch块完成后、下面接着try…catch的语句之前执行的语句。finally块无论是否抛出异常都会执行。如果抛出了一个异常,就算没有异常处理,finally块里的语句也会执行。
你可以用finally块来令你的脚本在异常发生时优雅地退出;举个例子,你可能需要在绑定的脚本中释放资源。接下来的例子用文件处理语句打开了一个文件(服务端的JavaScript允许你进入文件)。如果在文件打开时一个异常抛出,finally块会在脚本错误之前关闭文件。

openMyFile();
try {
    writeMyFile(theData); //This may throw a error
}catch(e){
    handleError(e); // If we got a error we handle it
}finally {
    closeMyFile(); // always close the resource
}

如果finally块返回一个值,该值会是整个try-catch-finally流程的返回值,不管在trycatch块中语句返回了什么:

function f() {
  try {
    console.log(0);
    throw "bogus";
  } catch(e) {
    console.log(1);
    return true; // this return statement is suspended
                 // until finally block has completed
    console.log(2); // not reachable
  } finally {
    console.log(3);
    return false; // overwrites the previous "return"
    console.log(4); // not reachable
  }
  // "return false" is executed now  
  console.log(5); // not reachable
}
f(); // console 0, 1, 3; returns false

finally块覆盖返回值也适用于在catch块内抛出或重新抛出的异常:

function f() {
  try {
    throw 'bogus';
  } catch(e) {
    console.log('caught inner "bogus"');
    throw e; // this throw statement is suspended until 
             // finally block has completed
  } finally {
    return false; // overwrites the previous "throw"
  }
  // "return false" is executed now
}
try {
  f();
} catch(e) {
  // this is never reached because the throw inside
  // the catch is overwritten
  // by the return in finally
  console.log('caught outer "bogus"');
}
// OUTPUT
// caught inner "bogus"

嵌套 try…catch 语句

你可以嵌套一个或多个try ... catch语句。如果一个内部try ... catch语句没有catch块,它需要有一个finally块,并且封闭的try ... catch语句的catch块被检查匹配。

使用Error对象

根据错误类型,你也许可以用’name’和’message’获取更精炼的信息。’name’提供了常规的错误类(如 ‘DOMException’ 或 ‘Error’),而’message’通常提供了一条从错误对象转换成字符串的简明信息。
在抛出你个人所为的异常时,为了充分利用那些属性(比如你的catch块不能分辨是你个人所为的异常还是系统的异常时),你可以使用 Error 构造函数。比如:

function doSomethingErrorProne () {
  if (ourCodeMakesAMistake()) {
    throw (new Error('The message'));
  } else {
    doSomethingToGetAJavascriptError();
  }
}
....
try {
  doSomethingErrorProne();
}
catch (e) {
  console.log(e.name); // logs 'Error'
  console.log(e.message); // logs 'The message' or a JavaScript error message)
}

循环和迭代

循环提供了一种快速和简单的方式去做一些重复的事。JavaScript循环有很多种类,但本质上它们都做的是同一件事:它们把一个动作重复了很多次(实际上重复的次数有可能为0)。各种循环机制提供了不同的方法去确定循环的开始和结束。不同情况下,某一种类型循环会比其它的循环用起来更简单。
JavaScript中提供了这些循环语句:

  • for 语句
  • do…while 语句
  • while 语句
  • labeled 语句 (类似于C中的GOTO)
  • break 语句
  • continue 语句
  • for…in 语句
  • for…of 语句 (ES6)

for 语句

一个for循环会一直重复执行,直到指定的循环条件为fasle。 JavaScript的for循环和Java与C的for循环是很相似的。一个for语句是这个样子的:
for ([initialExpression]; [condition]; [incrementExpression])
statement
当一个for循环执行的时候,会发生以下事件:

  1. 如果有初始化表达式initialExpression,它将被执行。这个表达式通常会初始化一个或多个循环计数器,但语法上是允许一个任意复杂度的表达式的。这个表达式也可以声明变量。
  2. 计算condition表达式的值。如果condition的值是true,循环中的statement会被执行。如果condition的值是false,for循环终止。如果condition表达式整个都被省略掉了,condition的值会被认为是true。
  3. 循环中的 statement被执行。如果需要执行多条语句,可以使用块 ({ ... })来包裹这些语句。
  4. 如果有更新表达式incrementExpression,执行它.
  5. 然后流程回到步骤2。

例子
下面的函数包含一个含有for循环去计算一个滑动列表中被选中项目的个数(一个 <select> 元素允许选择多项)。for循环声明了变量i并将它的初始值设为0。它检查i比 <select> 元素中的选项数量少,执行了随后的if语句,然后在每次完成循环以后i的值增加1。

<form name="selectForm">
  <p>
    <label for="musicTypes">Choose some music types, then click the button below:</label>
    <select id="musicTypes" name="musicTypes" multiple="multiple">
      <option selected="selected">R&B</option>
      <option>爵士</option>
      <option>布鲁斯</option>
      <option>新纪元</option>
      <option>古典</option>
      <option>歌剧</option>
    </select>
  </p>
  <p><input id="btn" type="button" value="选择了多少个选项?" /></p>
</form>
<script>
function howMany(selectObject) {
  var numberSelected = 0;
  for (var i = 0; i < selectObject.options.length; i++) {
    if (selectObject.options[i].selected) {
      numberSelected++;
    }
  }
  return numberSelected;
}
var btn = document.getElementById("btn");
btn.addEventListener("click", function(){
  alert('选择选项的数量是: ' + howMany(document.selectForm.musicTypes))
});
</script>

do...while 语句

[do...while](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/do...while) 语句一直重复直到指定的条件求值得到假(false)。 一个 do…while 语句看起来像这样:
do
statement
while (condition);
statement 在检查条件之前会执行一次。要执行多条语句(语句块),要使用块语句 ({ … }) 包括起来。 如果 condition 为真(true),statement 将再次执行。 在每个执行的结尾会进行条件的检查。当 condition 为假(false),执行会停止并且把控制权交回给 do…while 后面的语句。

例子
在下面的例子中, 这个 do 循环将至少重复一次并且一直重复直到 i 不再小于 5。

do {
  i += 1;
  console.log(i);
} while (i < 5);

while 语句

一个 [while](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/while) 语句只要指定的条件求值为真(true)就会一直执行它的语句块。一个 while 语句看起来像这样:
while (condition)
statement
如果这个条件变为假,循环里的 statement 将会停止执行并把控制权交回给 while 语句后面的代码。
条件检测会在每次 statement 执行之前发生。如果条件返回为真, statement 会被执行并紧接着再次测试条件。如果条件返回为假,执行将停止并把控制权交回给 while 后面的语句。
要执行多条语句(语句块),要使用块语句 ({ … }) 包括起来。

例子 1
下面的 while 循环只要 n 小于 3就会一直执行:

var n = 0;
var x = 0;
while (n < 3) {
  n++;
  x += n;
}

在每次循环里, n 会增加1并被加到 x 上。所以, x 和 n 的变化是:

  • 第一次完成后: n = 1 和 x = 1
  • 第二次完成后: n = 2 和 x = 3
  • 第三次完成后: n = 3 和 x = 6

在三次完成后, 条件 n < 3 结果不再为真,所以循环终止了。

例子 2
避免无穷循环(无限循环)。保证循环的条件结果最终会变成假;否则,循环永远不会停止。下面这个 while 循环会永远执行因为条件永远不会变成假:

while (true) {
  console.log("Hello, world");
}

labeled 语句

一个 label 提供了一个可以让你引用到您程序别的位置的标识符。例如,你可以用 label 标识一个循环, 然后使用 break 或者 continue 来指出程序是否该停止循环还是继续循环。
label 语句的语法看起来像这样:
label :
statement
label 的值可以是任何的非保留字的 JavaScript 标识符, statement 可以是任意你想要标识的语句(块)。

例子
在这个例子里,标记 markLoop 标识了一个 while 循环。

markLoop:
while (theMark == true) {
   doSomething();
}


Label 语句:
Label: statement
如: begin: for (var i = 0; i < 10 ; i++ ){
alert(i);
}

举一个比较典型的例子,看完后即明白 Label 的应用:(未添加 Label)

var num = 0;
        for (var i = 0 ; i < 10 ; i++){
             for (var j = 0 ; j < 10 ; j++){
                  if( i == 5 && j == 5 ){
                        break;
                  }
             num++;
             }
        }
       alert(num); // 循环在 i 为5,j 为5的时候跳出 j循环,但会继续执行 i 循环,输出 95


对比使用了 Label 之后的程序:(添加 Label 后)

var num = 0;
    outPoint:
    for (var i = 0 ; i < 10 ; i++){
         for (var j = 0 ; j < 10 ; j++){
              if( i == 5 && j == 5 ){
                    break outPoint;
              }
         num++;
         }
    }
    alert(num); // 循环在 i 为5,j 为5的时候跳出双循环,返回到outPoint层继续执行,输出 55


对比使用了break、continue语句:

var num = 0;  
  outPoint:  
  for(var i = 0; i < 10; i++)  
  {  
     for(var j = 0; j < 10; j++)  
      {  
          if(i == 5 && j == 5)  
          {  
              continue outPoint;  
          }  
          num++;  
      }  
  }  
  alert(num);  //95

从alert(num)的值可以看出,continue outPoint;语句的作用是跳出当前循环,并跳转到outPoint(标签)下的for循环继续执行。

break 语句

使用 [break](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/break) 语句来终止循环,switch, 或者是链接到 label 语句。

  • 当你使用不带 label 的 break 时, 它会立即终止当前所在的 whiledo-whilefor,或者 switch 并把控制权交回这些结构后面的语句。
  • 当你使用带 label 的 break 时,它会终止指定的标记(label)了的语句。

break 语句的语法看起来像这样:

  1. break;
  2. break _label_;

第一种形式的语法终止当前所在的循环或 switch; 第二种形式的语法终止指定的 label 语句。

例子 1
下面的例子循环数组里的元素直到找到一个值是等于 theValue 的:

for (i = 0; i < a.length; i++) {
  if (a[i] == theValue) {
    break;
  }
}

例子 2: 终止一个 label

var x = 0;
var z = 0
labelCancelLoops: while (true) {
  console.log("外部循环: " + x);
  x += 1;
  z = 1;
  while (true) {
    console.log("内部循环: " + z);
    z += 1;
    if (z === 10 && x === 10) {
      break labelCancelLoops;
    } else if (z === 10) {
      break;
    }
  }
}

continue 语句

这个 [continue](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/continue) 语句可以用来继续执行(跳过代码块的剩余部分并进入下一循环)一个 while, do-while, for, 或者 label 语句。

  • 当你使用不带 label 的 continue 时, 它终止当前 while,do-while,或者 for 语句到结尾的这次的循环并且继续执行下一次循环。
  • 当你使用带 label 的 continue 时, 它会应用被 label 标识的循环语句。

continue 的语法看起来像这样:

  1. continue;
  2. continuelabel;

例子 1
下面的例子展示了带有一个当 i 等于 3的 continue 语句的循环。 于是, n 取到的值是 1, 3, 7, 12。不带有一个当 i 等于 3的 continue 语句的循环。 于是, n 取到的值是 1, 3, 6, 10,15。

var i = 0;
var n = 0;
while (i < 5) {
  i++;
  if (i == 3) {
    continue;
  }
  n += i;
  console.log(n);
}
//1,3,7,12
var i = 0; 
var n = 0; 
while (i < 5) { 
  i++; 
  if (i == 3) { 
     // continue; 
  } 
  n += i; 
  console.log(n);
}
// 1,3,6,10,15

例子 2
一个被标签为checkiandj 的语句包含了一个标签为checkj 的语句。如果遇到continue语句,程序会结束当前chechj的迭代并开始下一轮的迭代。每次遇到continue语句,checkj 语句会一直重复执行直到checkj语句的条件为false。. 当返回false后,checkiandj的剩余语句将会执行,checkiandj会一直执行指导checkiandj的条件为false。当checkiandj的返回值为false时,将会执行checkiandj 的下面的语句。
如果 continue 有一个标记 checkiandj, 程序将会从 checkiandj 语句块的顶部继续执行。

var i = 0;
var j = 10;
checkiandj:
  while (i < 4) {
    console.log(i);
    i += 1;
    checkj:
      while (j > 4) {
        console.log(j);
        j -= 1;
        if ((j % 2) == 0) {
          continue checkj;
        }
        console.log(j + ' is odd.');
      }
      console.log('i = ' + i);
      console.log('j = ' + j);
  }

for...in 语句

这个 for...in 语句循环一个指定的变量来循环一个对象所有可枚举的属性。JavaScript 会为每一个不同的属性执行指定的语句。
for (variable in object) {
statements
}

例子
下面的函数通过它的参数得到一个对象和这个对象的名字。然后循环这个对象的所有属性并且返回一个列出属性名和该属性值的字符串。

function dump_props(obj, obj_name) {
  var result = "";
  for (var i in obj) {
    result += obj_name + "." + i + " = " + obj[i] + "<br>";
  }
  result += "<hr>";
  return result;
}

对于一个对象拥有 make 和 model 属性的 car 对象来说,执行结果是:

car.make = Ford
car.model = Mustang

数组
虽然用for…in来迭代Array元素很诱人,但是它返回的除了数字索引外还有可能是你自定义的属性名字。因此还是用带有数字索引的传统的[for](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for) 循环来迭代一个数组比较好,因为如果你想改变数组对象,比如添加属性或者方法,for…in 语句迭代的是 自定义的属性而不是数组的元素。

for...of statement

[for...of](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of)语句在可迭代的对象上创建了一个循环(包括Array, Map, Set, 参数对象( arguments) 等等) ,对值的每一个独特的属性调用一个将被执行的自定义的和语句挂钩的迭代。
for (variable of object) {
statement
}
下面的这个例子展示了 for...of 和 [for...in](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in) 两种循环语句之间的区别。与 for...in 循环遍历的结果是数组元素的下标不同的是, for...of 遍历的结果是元素的值:

let arr = [3, 5, 7];
arr.foo = "hello";
for (let i in arr) {
   console.log(i); // logs "0", "1", "2", "foo"
}
for (let i of arr) {
   console.log(i); // logs "3", "5", "7" // 注意这里没有 hello
}

表达式和运算符

运算符

JavaScript 拥有如下类型的运算符。

JavaScript 拥有二元和一元运算符, 和一个特殊的三元运算符(条件运算符)。一个二元运算符需要两个操作数,分别在运算符的前面和后面:

操作数1 运算符 操作数2

例如, 3+4x*y
一个一元运算符需要一个操作数,在运算符前面或后面:

运算符 操作数

操作数 运算符

例如, x++++x

赋值运算符

一个 赋值运算符(assignment operator) 将它右边操作数的值赋给它左边的操作数。最简单的赋值运算符是等于(=),它将右边的操作数值赋给左边的操作数。那么 x = y 就是将 y 的值赋给 x。
还有一些复合赋值操作符,它们是下表列出的这些操作的缩写:
复合赋值运算符

名字 简写的操作符 含义
赋值(Assignment) x = y x = y
加法赋值(Addition assignment) x += y x = x + y
减法赋值(Subtraction assignment) x -= y x = x - y
乘法赋值(Multiplication assignment) x *= y x = x * y
除法赋值(Division assignment) x /= y x = x / y
求余赋值(Remainder assignment) x %= y x = x % y
求幂赋值(Exponentiation assignment) x **= y x = x ** y
左移位赋值(Left shift assignment) x <<= y x = x << y
右移位赋值(Right shift assignment) x >>= y x = x >> y
无符号右移位赋值(Unsigned right shift assignment) x >>>= y x = x >>> y
按位与赋值(Bitwise AND assignment) x &= y x = x & y
按位异或赋值(Bitwise XOR assignment) x ^= y x = x ^ y
按位或赋值(Bitwise OR assignment) `x = y` `x = x y`

比较运算符

比较运算符比较它的操作数并返回一个基于表达式是否为真的逻辑值。操作数可以是数字,字符串,逻辑,对象值。字符串比较是基于标准的字典顺序,使用Unicode值。在多数情况下,如果两个操作数不是相同的类型, JavaScript 会尝试转换它们为恰当的类型来比较。这种行为通常发生在数字作为操作数的比较。类型转换的例外是使用 ===!== 操作符,它们会执行严格的相等和不相等比较。这些运算符不会在检查相等之前转换操作数的类型。下面的表格描述了该示例代码中的各比较运算符

var var1 = 3;
var var2 = 4;
运算符 描述 返回true的示例
等于 Equal (==) 如果两边操作数相等时返回true。 3 == var1``"3" == var1``3 == '3'
不等于 Not equal (!=) 如果两边操作数不相等时返回true var1 != 4<br />var2 != "3"
全等 Strict equal (===) 两边操作数相等且类型相同时返回true。 参见 Object.is and sameness in JS. 3 === var1
不全等 Strict not equal (!==) 两边操作数不相等或类型不同时返回true。 var1 !== "3"<br />3 !== '3'
大于 Greater than (>) 左边的操作数大于右边的操作数返回true var2 > var1<br />"12" > 2
大于等于 Greater than or equal (>=) 左边的操作数大于或等于右边的操作数返回true var2 >= var1<br />var1 >= 3
小于 Less than (<) 左边的操作数小于右边的操作数返回true var1 < var2<br />"2" < 12
小于等于 Less than or equal (<=) 左边的操作数小于或等于右边的操作数返回true var1 <= var2<br />var2 <= 5

注意: =>) 不是运算符,而是箭头函数的标记符号 。

算术运算符

算术运算符使用数值(字面量或者变量)作为操作数并返回一个数值.标准的算术运算符就是加减乘除(+ - * /)。当操作数是浮点数时,这些运算符表现得跟它们在大多数编程语言中一样(特殊要注意的是,除零会产生Infinity)。例如:

1 / 2;  // 0.5
1 / 2 == 1.0 / 2.0; // true

除了标准的算术运算符(+, - ,* /),JavaScript还提供了下表中的算术运算符。

Operator Description Example
求余(%) 二元运算符. 返回相除之后的余数. 12 % 5 返回 2。
自增(++) 一元运算符. 将操作数的值加一. 如果放在操作数前面 (++x), 则返回加一后的值; 如果放在操作数后面 (x++), 则返回操作数原值,然后再将操作数加一. var x=3;
console.log(++x); //4
console.log(x); //4
var y=3;
console.log(y++); //3
console.log(y); //4
自减(--) 一元运算符. 将操作数的值减一. 前后缀两种用法的返回值类似自增运算符. var x=3; console.log(—x); //输入2,x=2
var y=3;console.log(y—);//输出3,x=2;
一元负值符(-) 一元运算符,返回操作数的负值. var x=3; console.log(-x); //输入-3
一元正值符(+)
一元运算符, 如果操作数在之前不是number,试图将其转换为number console.log( +'3' ); // 3
console.log( '3' ); // '3'
console.log(+true); // 1
指数运算符(**) 计算 base(底数)exponent(指数)次方, 表示为base 2 ** 3 returns 8.
10 ** -1 returns 0.1.

位运算符

位运算符将它的操作数视为32位元的二进制串(0和1组成)而非十进制八进制或十六进制数。例如:十进制数字9用二进制表示为1001,位运算符就是在这个二进制表示上执行运算,但是返回结果是标准的JavaScript数值。
下表总结了 JavaScript 的位运算符。

Operator Usage Description
按位与 AND a & b 在a,b的位表示中,每一个对应的位都为1则返回1, 否则返回0.
按位或 OR `a b` 在a,b的位表示中,每一个对应的位,只要有一个为1则返回1, 否则返回0.
按位异或 XOR a ^ b 在a,b的位表示中,每一个对应的位,两个不相同则返回1,相同则返回0.
按位非 NOT ~ a 反转被操作数的位。
左移 shift a << b 将a的二进制串向左移动b位,右边移入0.
算术右移 a >> b 把a的二进制表示向右移动b位,丢弃被移出的所有位.
(译注:算术右移左边空出的位是根据最高位是0和1来进行填充的)
无符号右移
(左边空出位用0填充)
a >>> b 把a的二进制表示向右移动b位,丢弃被移出的所有位,并把左边空出的位都填充为0

位逻辑运算符

概念上来讲, 位逻辑运算符工作流程如下:

  • 操作数被转换为32bit整數,以位序列(0和1组成)表示.若超過32bits,則取低位32bit,如下所示:

    Before: 11100110111110100000000000000110000000000001
    After:              10100000000000000110000000000001
    
  • 第一个操作数的每一位都与第二个操作数的对应位组对: 第一位对应第一位,第二位对应第二位,以此类推.

  • 运算符被应用到每一对”位”上, 最终的运算结果由每一对“位”的运算结果组合起来.

例如,十进制数9的二进制表示是1001,十进制数15的二进制表示是1111.因此,当位运算符应用到这两个值时,结果如下:
位运算符范例

表达式 结果 二进制描述
15 & 9 9 1111 & 1001 = 1001
`15 9` 15 `1111 1001 = 1111`
15 ^ 9 6 1111 ^ 1001 = 0110
~15 -16 ~``00000000...``00001111 = ``1111``1111``...``11110000
~9 -10 ~``00000000``...``0000``1001 = ``1111``1111``...``1111``0110

注意位运算符“非”将所有的32位取反,而值的最高位(最左边的一位)为1则表示负数(2-补码表示法)。

移位运算符

移位运算符带两个操作数:第一个是待移位的数,第二个是指定第一个数要被移多少位的数。移位的方向由运算符来控制.
移位运算符把操作数转为32bit整数,然后得出一个与待移位数相同种类的值。
移位运算符列表如下。

运算符 描述 范例
<<
(左移位)
将第一个操作数向左移动指定数量的位. 左边移出位被抛弃. 左边移出的几位被丢弃.右边多出的空位由0补齐 9<<2产生36,因为1001移位2比特向左变为100100,它是36。
>>
(带符号右移)
将第一个操作数向右移动指定数量的位. 右边移出位被抛弃. 左边多出的空位由原值的最左边数字补齐. 9>>2产生2,因为1001移位2位向右变为10,其是2。同样,-9>>2产生-3,由于符号被保留。
>>>
(补零右移)
将第一个操作数向右移动指定数量的位. 右边移出位被抛弃. 左边多出的空位由0补齐. 19>>>2产生4,因为10011移位2位向右变为100,它是4。对非负数值,补零右移和带符号右移产生相同结果。

逻辑运算符

逻辑运算符常用于布尔(逻辑)值之间; 当操作数都是布尔值时,返回值也是布尔值。 不过实际上&&||返回的是一个特定的操作数的值,所以当它用于非布尔值的时候,返回值就可能是非布尔值。 逻辑运算符的描述如下。

运算符 范例 描述
逻辑与(&&) expr1 && expr2 (逻辑与) 如果expr1能被转换为false,那么返回expr1;否则,返回expr2。因此,&&用于布尔值时,当操作数都为true时返回true;否则返回false.
逻辑或 (` `) `expr1 expr2` (逻辑或) 如果expr1能被转换为true,那么返回expr1;否则,返回expr2。因此,||用于布尔值时,当任何一个操作数为true则返回true;如果操作数都是false则返回false。
逻辑非(!) !expr (逻辑非) 如果操作数能够转换为true则返回false;否则返回true。

能被转换为false的值有null, 0, NaN, 空字符串(“”)和undefined。(译者注:也可以称作”falsy“)
下面是&&(逻辑”与”)操作符的示例。

var a1 =  true && true;     // t && t returns true
var a2 =  true && false;    // t && f returns false
var a3 = false && true;     // f && t returns false
var a4 = false && (3 == 4); // f && f returns false
var a5 = "Cat" && "Dog";    // t && t returns Dog
var a6 = false && "Cat";    // f && t returns false
var a7 = "Cat" && false;    // t && f returns false

下面是||(逻辑”或”)操作符的示例。

var o1 =  true || true;     // t || t returns true
var o2 = false || true;     // f || t returns true
var o3 =  true || false;    // t || f returns true
var o4 = false || (3 == 4); // f || f returns false
var o5 = "Cat" || "Dog";    // t || t returns Cat
var o6 = false || "Cat";    // f || t returns Cat
var o7 = "Cat" || false;    // t || f returns Cat

下面是!(逻辑”非”)操作符的示例。

var n1 = !true;  // !t returns false
var n2 = !false; // !f returns true
var n3 = !"Cat"; // !t returns false

短路求值

作为逻辑表达式进行求值是从左到右,它们是为可能的“短路”的出现而使用以下规则进行测试:

  • false && anything // 被短路求值为false
  • true || anything // 被短路求值为true

逻辑的规则,保证这些评估是总是正确的。请注意,上述表达式的anything部分不会被求值,所以这样做不会产生任何副作用。

字符串运算符

除了比较操作符,它可以在字符串值中使用,连接操作符(+)连接两个字符串值相连接,返回另一个字符串,它是两个操作数串的结合。
例如,

console.log("my " + "string"); // console logs the string "my string".

简写操作符 += 也可以用来拼接字符串,例如:

var myString = "alpha";
myString += "bet"; // 返回 "alphabet"

条件(三元)运算符

条件运算符是JavaScript中唯一需要三个操作数的运算符。运算的结果根据给定条件在两个值中取其一。语法为:

条件 ? 值1 : 值2

如果条件为真,则结果取值1。否则为值2。你能够在任何允许使用标准运算符的地方使用条件运算符。
例如,

var status = (age >= 18) ? "adult" : "minor";

age 大于等于18的时候,将“adult”赋值给status;否则将“minor”赋值给 status

逗号操作符

逗号操作符,)对两个操作数进行求值并返回最终操作数的值。它常常用在 for 循环中,在每次循环时对多个变量进行更新。
例如,假如 a 是一个二维数组,每个维度各有10个元素,以下代码利用逗号操作符来同时改变两个变量的值。这段代码的功能是打印出该二维数组的对角线元素的值:

var x = [0,1,2,3,4,5,6,7,8,9]
var a = [x, x, x, x, x];
for (var i = 0, j = 9; i <= j; i++, j--)
  console.log('a[' + i + '][' + j + ']= ' + a[i][j]);

一元操作符

一元操作符仅对应一个操作数。

delete

[delete](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete)操作符,删除一个对象或一个对象的属性或者一个数组中某一个键值。语法如下:

delete objectName;
delete objectName.property;
delete objectName[index];
delete property; // legal only within a with statement

objectName是一个对象名,property 是一个已经存在的属性,index是数组中的一个已经存在的键值的索引值。
第四行的形式只在[with](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with)声明的状态下是合法的, 从对象中删除一个属性。
你能使用 delete 删除各种各样的隐式声明, 但是被var声明的除外。
如果 delete 操作成功,属性或者元素会变成 undefined。如果 delete可行会返回true,如果不成功返回false

x = 42;
var y = 43;
myobj = new Number();
myobj.h = 4;    // create property h
delete x;       // returns true (can delete if declared implicitly)
delete y;       // returns false (cannot delete if declared with var)
delete Math.PI; // returns false (cannot delete predefined properties)
delete myobj.h; // returns true (can delete user-defined properties)
delete myobj;   // returns true (can delete if declared implicitly)

删除数组元素

删除数组中的元素时,数组的长度是不变的,例如删除a[3], a[4]a[4]``和a[3] 仍然存在变成了undefined
delete 删除数组中的一个元素,这个元素就不在数组中了。例如,trees[3]被删除,trees[3] 仍然可寻址并返回undefined

var trees = new Array("redwood", "bay", "cedar", "oak", "maple");
delete trees[3];
if (3 in trees) {
  // 不会被执行
}

如果想让数组中存在一个元素但是是undefined值,使用undefined关键字而不是delete操作. 如下: trees[3]分配一个undefined,但是这个数组元素仍然存在:

var trees = new Array("redwood", "bay", "cedar", "oak", "maple");
trees[3] = undefined;
if (3 in trees) {
  // this gets executed(会被执行)
}

typeof

typeof操作符 可通过下面2种方式使用:

typeof operand
typeof (operand)

typeof 操作符返回一个表示 operand 类型的字符串值。operand 可为字符串、变量、关键词或对象,其类型将被返回。operand 两侧的括号为可选。
假设你定义了如下的变量:

var myFun = new Function("5 + 2");
var shape = "round";
var size = 1;
var today = new Date();

typeof 操作符将会返回如下的结果:

typeof myFun;     // returns "function"
typeof shape;     // returns "string"
typeof size;      // returns "number"
typeof today;     // returns "object"
typeof dontExist; // returns "undefined"

对于关键词 truenull, ``typeof 操作符将会返回如下结果:

typeof true; // returns "boolean"
typeof null; // returns "object"

对于一个数值或字符串, ``typeof 操作符将会返回如下结果:

typeof 62;            // returns "number"
typeof 'Hello world'; // returns "string"

对于属性值,typeof 操作符将会返回属性所包含值的类型:

typeof document.lastModified; // returns "string"
typeof window.length;         // returns "number"
typeof Math.LN2;              // returns "number"

对于方法和函数,typeof 操作符将会返回如下结果:

typeof blur;        // returns "function"
typeof eval;        // returns "function"
typeof parseInt;    // returns "function"
typeof shape.split; // returns "function"

对于预定义的对象,typeof 操作符将会返回如下结果:

typeof Date;     // returns "function"
typeof Function; // returns "function"
typeof Math;     // returns "object"
typeof Option;   // returns "function"
typeof String;   // returns "function"

void

void 运算符运用方法如下:

void (expression)
void expression

void运算符,表明一个运算没有返回值。expression是javaScript表达式,括号中的表达式是一个可选项,当然使用该方式是一种好的形式。
你可以使用void运算符指明一个超文本链接。该表达式是有效的,但是并不会在当前文档中进行加载。
如下创建了一个超链接文本,当用户单击该文本时,不会有任何效果。

<a href="javascript:void(0)">Click here to do nothing</a>

下面的代码创建了一个超链接,当用户单击它时,提交一个表单。

<a href="javascript:void(document.form.submit())">
Click here to submit</a>

关系操作符

关系操作符对操作数进行比较,根据比较结果真或假,返回相应的布尔值。

in

in操作符,如果所指定的属性确实存在于所指定的对象中,则会返回true,语法如下:

propNameOrNumber in objectName

在这里 propNameOrNumber可以是一个代表着属性名的字符串或者是一个代表着数组索引的数值表达式,而objectName则是一个对象名。
下面的例子是 in 操作的常见用法。

// Arrays
var trees = new Array("redwood", "bay", "cedar", "oak", "maple");
0 in trees;        // returns true
3 in trees;        // returns true
6 in trees;        // returns false
"bay" in trees;    // returns false (you must specify the index number,
                   // not the value at that index)
"length" in trees; // returns true (length is an Array property)
// Predefined objects
"PI" in Math;          // returns true
var myString = new String("coral");
"length" in myString;  // returns true
// Custom objects
var mycar = {make: "Honda", model: "Accord", year: 1998};
"make" in mycar;  // returns true
"model" in mycar; // returns true

instanceof

如果所判别的对象确实是所指定的类型,则返回true。其语法如下:

objectName instanceof objectType

objectName 是需要做判别的对象的名称,而objectType是假定的对象的类型, 例如DateArray.
当你需要确认一个对象在运行时的类型时,可使用instanceof. 例如,需要 catch 异常时,你可以针对抛出异常的类型,来做不同的异常处理。
例如, 下面的代码使用instanceof去判断 theDay是否是一个 Date 对象. 因为theDay是一个Date对象, 所以if中的代码会执行.

var theDay = new Date(1995, 12, 17);
if (theDay instanceof Date) {
  // statements to execute
}

JSON

JSON 英文全称 JavaScript Object Notation,它是一种轻量级的数据交换格式,虽然名字中含有JavaScript,但是它已经不仅仅用于JavaScript中了,在其他场景中也有很多应用。

JSON语法

JSON语法很简单,就下面几点:

  • 数据为 键/值 对。
  • 数据由逗号分隔。
  • 大括号保存对象
  • 方括号保存数组

看几个例子:

{
    name:"zoumm",
    job :"it",
    age :25
}

{
    "name": "zoumm",
    "job": "it",
    "age": 23,
    "school": {
        "name": "大学名",
        "add": "中国"
    }
}

[
    {"name":"Runoob", "url":"www.runoob.com"}, 
    {"name":"Google", "url":"www.google.com"},
    {"name":"Taobao", "url":"www.taobao.com"}
]

相比于XML,同样体积的JSON数据可以携带更多的数据,并且语义更加清晰。

变量类型

  • 基本类型
  • 引用类型

基本类型包括 undefinednullbooleannumberstring (ES6新增Symbol)
除去基本类型之外的都是引用类型了。
两种类型的区别有下面几点:

  1. 基本类型都是不可变的,而引用类型的值可以变化。
  2. 基本类型的比较是值的比较,引用类型的比较是引用的比较。
  3. 基本类型的值存放在内存的栈区中,引用类型的值需要栈区栈区共同完成,栈区保存指向堆内存中该对象的指针。

实际的可以用下面代码做演示。

var strA = 'testA';
var strB = strA;
console.log(strA, strB); //testA testA

strB = 'testB';
console.log(strA, strB); //testA testB

var strC = 'testA';
console.log(strA == strC, strA === strC); //true true

strA.testNum = 1;
console.log(strA, strA.testNum); //undefined

var person = {};//创建一个对象 --引用类型
person.name = 'Chris';
person.age = 28;
person.sayName = function(){console.log(person.name);} 

person.sayName();// 'Chris'
console.log(person); // {name: "Chris", age: 28, sayName: ƒ}

var person2 = person;
console.log(person2); // {name: "Chris", age: 28, sayName: ƒ}

person2.name = 'Minawa';
console.log(person1); // {name: "Minawa", age: 28, sayName: ƒ}

delete person2.name; //删除person对象的name属性
person.sayName(); // undefined

var obj1 = {};
var obj2 = {};
console.log(obj1 == obj2, obj1 === obj2); // false false

所以在使用对象的过程中,要千万注意,不要让当前对象的修改影响到不相关的功能。

基本类型

undefined

undefined类型就一个值:undefined,当申明一个变量但是没有赋初值时,这个变量的值就是undefined,意思是未定义的变量,使用typeof运算符检查时返回undefined;

var a;
console.log(typeof(a));
>>> undefined

null

null类型也只有一个值:null,它代表一个空的对象,使用typeof运算符检查时返回object;

var a = null;
console.log(typeof(a));
>>> object

boolean

布尔(逻辑)类型,存在true和false两个值;

var t = true;
var f = false;
console.log(t, f);
>>> true false
console.log(typeof(t))
>>> boolean

number

数值类型,除了我们在java中已经接触的整型和浮点数,js中还存在一个特殊值NaN(Not a Number),表示不是一个合法的数字,任何NaN参与的运算,最终结果都会是NaN,另外,NaN不和任何值相等,甚至是它自身,真正的“疯起来连自己都打”。另外NaN甚至不是一个关键字,可以给它赋值,虽然没啥卵用(´∀`)唯一判断NaN的方法是使用isNaN函数。

var nan = NaN;
console.log(nan == NaN);
>>> false
console.log(nan === NaN);
>>> false
nan/1
>>> NaN
var NaN = 1;
console.log(NaN);
>>> NaN
typeof(nan)
>>> number
isNaN(nan)
>>> true

下面是number的各种形式

// 通常表示
1
12.0
1.
.1
2.1
// 科学计数法
1E3
2e6
0.1e2
1e-5
// 二/八/十六进制
0b100000
0B0000111
0o10 //es6
0xFFFFFF
0XA

不同进制间可以使用Number的toString()和parseInt进行转换(小数没有多进制表示形式)

// toString转换,输入为Number,返回为String
var num = 120;
num.toString(); // "120"
num.toString(2); // "1111000"
num.toString(8); // "170"
num.toString(16); // "78"
num.toString(20); // "60"
0x11.toString(); // "17"
0b111.toString(); // "7"
0x11.toString(12);// "15"

// parseInt转换,输入为String,返回为Number
parseInt('110'); // 110
parseInt('110', 2); // 6
parseInt('110', 8); // 72
parseInt('110', 16); // 272
parseInt('110', 26); // 702
parseInt('09'); // IE8下会报错,其他浏览器返回9
parseInt('a1'); // NaN

// toString和parseInt结合使用可以在两两进制之间转换
// 将 a 从36进制转为12进制
var a = 'ra'; // 36进制表示的数
parseInt(a, 36).toString(12); // "960"

// 和parseInt对应的,有将字符串转换为浮点数的parseFloat
parseFloat(1200); // 1200
parseFloat('100'); // 100
parseFloat('12.34'); // 12.34

另外Number也有一些特殊的属性:

属性 描述
Number.MAX_VALUE 可表示的最大值
Number.MIN_VALUE 可表示的最小值
Number.NaN 特指“非数字”
Number.NEGATIVE_INFINITY 特指 “负无穷”; 在溢出时返回
Number.POSITIVE_INFINITY 特指 “正无穷”; 在溢出时返回
Number.EPSILON 表示 1 和比最接近 1 且大于 1 的最小 Number 之间的差别,或者说浮点数最小精度
Number.MIN_SAFE_INTEGER JavaScript 最小安全整数
Number.MAX_SAFE_INTEGER JavaScript 最大安全整数

Number也有一些常用的方法对数字精度进行取舍:

方法 描述
toExponential() 返回一个数字的指数形式的字符串
toFixed() 返回指定小数位数的表示形式
toPrecision() 返回一个指定精度的数字
var num = 12000;
num.toExponential();
>>> '1.2e+4'
num.toFixed(2);
>>> '12000.00'
num.toPrecision(2);
>>> '1.2e+4'

*问题

12000.toFixed(2); //为什么这里会报错呢
>>> Uncaught SyntaxError: Invalid or unexpected token
12000..toFixed(2);
>>> '12000.00'
0.1 + 0.2 === 0.3
>>> false
0.1 + 0.2
>>> 0.30000000000000004
console.log(Math.abs(0.1+0.2-0.3) <= Number.EPSILON);
>>> true

同Number紧密相关的对象Math提供了很多有用的数学方法。

属性列表:

属性 描述
Math.E 欧拉数,也是自然对数的底数, 约等于 2.718
Math.LN2 2 的自然对数, 约等于 0.693
Math.LN10 10 的自然对数, 约等于 2.303
Math.LOG2E 以 2 为底 E 的对数, 约等于 1.443
Math.LOG10E 以 10 为底 E 的对数, 约等于 0.434
Math.PI 圆周率,一个圆的周长和直径之比,约等于 3.14159
Math.SQRT2 2 的平方根, 约等于 1.414
Math.SQRT1_2 1/2 的平方根, 约等于 0.707

Math 中的三角函数 sin 等参数为弧度,如果是角度可以使用 (Math.PI / 180)

方法 描述
Math.abs(x) 返回 x 的绝对值
Math.sign(x) 返回 x 的符号函数, 判定 x 是正数, 负数还是 0
Math.random() 返回 0 到 1 之间的伪随机数
Math.floor(x) 返回 x 向下取整后的值
Math.ceil(x) 返回 x 向上取整后的值
Math.round(x) 返回四舍五入后的整数.
Math.fround(x) 返回数字的最接近的单精度浮点型表示
Math.trunc(x) 返回 x 的整数部分, 去除小数
Math.sqrt(x) 返回 x 的平方根
Math.cbrt(x) 返回 x 的立方根
Math.hypot([x[,y[,…]]]) 返回其参数平方和的平方根
Math.pow(x,y) 返回 x 的 y 次幂
min(), max() 返回一个以逗号间隔的数字参数列表中的较小或较大值 (分别地)
sin(), cos(), tan() 标准三角函数; 参数为弧度
asin(), acos(), atan(), atan2() 反三角函数; 返回值为弧度
sinh(), cosh(), tanh() 双曲三角函数; 返回值为弧度.
asinh(), acosh(), atanh() 反双曲三角函数; 返回值为弧度.
pow(), exp(), expm1(), log10(), log1p(), log2() 指数与对数函数

string

字符串类型,和java中的字符串类似,我们常用的一边有它的下面几种方法。

'Hello World'.indexOf('o'); // 4
'Hello World'.lastIndexOf('o'); // 7
'Hello World'.replace('o', 'O'); // 'HellO World'
'Hello World'.split(''); // ["H", "e", "l", "l", "o", " ", "W", "o", "r", "l", "d"]
'Hello World'.toLowerCase(); // 'hello world'
'Hello World'.toUpperCase(); // 'HELLO WORLD'
'Hello'.concat(' World'); // 'Hello World' 等同于'Hello' + ' World'

字符串本身并没有太多好说的,但是和和字符串密切相关的正则表达式则可以大书特书了,如果能够熟练掌握它,能够让你在工作生活中获得巨大的收益。我们在之后将会学习它。(具体参考下面两个链接):
http://javascript.ruanyifeng.com/stdlib/regexp.html
http://deerchao.net/tutorials/regex/regex.htm

引用类型

引用类型一般是使用new关键字创建实例的类型,比如Object,Array,Function等。

Object 对象

Object(对象)类型是最常见的引用类型,如下示例:

var person = new Object();
// 或者 var person = {};
person.name = 'Chris';
person.age = '28';
person.isMale = true;
person; // {name: "Chris", age: "28", isMale: true}
person.name; // 'Chris'
person['age']; // 28
delete person.isMale
person; // {name: "Chris", age: "28"}

从上面的例子里,可以看出,我们可以随意往Object上添加删除属性,并且我们可以使用两种方式访问对象的属性:

  • 直接使用 . 来访问对象的属性,例如person.name;
  • 使用中括号[]加属性名称来访问,实际上我们使用点运算符添加的属性,标识符最终都会变成字符串形式保存在对象中。

Array 数组

数组其实可以看做特殊的对象,它包含了一个能随数组长度自动变换的length属性,并且属性名都是正整数类型。

var arr = new Array;
// 或者var arr = [];
arr.push('a');
arr.push(1);
arr.push({name: 'Chris'});
arr; // ["a", 1, {…}]
arr.length; // 3
delete arr[2];
arr; // ["a", 1, empty]
arr.length; // 3
arr.pop();
arr; // ["a", 1]
arr.join('-'); // 'a-1'

// 字面量赋值和构造函数赋值
arr = new Array(5);
arr; // [empty × 5]
arr = new Array('a');
arr; // ['a']
arr = new Array(5, 'a');
arr; // [5, 'a']

arr = [5];
arr; // [5]
arr = ['a'];
arr; // ['a']
arr = [5, 'a'];
arr; // [5, 'a']

特别注意,使用字面量时,末尾最好不要带上多余的逗号,虽然在现代浏览器上,最后的结果符合你的预期,在某些版本的IE下可能会多追加一个值为undefined的对象。

var arr = [1,2,3,4,5,];
arr; // 现代浏览器 [1,2,3,4,5] , 低版本IE [1,2,3,4,5,undefined]

另外,因为js的对象可以随意修改,你也可以直接修改数组的数据,例如下面的:

var arr = [1,2,3,4,5];
arr[-1] = 0
arr; // [1, 2, 3, 4, 5, -1: 0]
arr.length; // 5

arr['x'] = 'x';
arr; // [1, 2, 3, 4, 5, -1: 0, x: "x"]
arr.length; // 5

arr.length = 10;
arr; // [1, 2, 3, 4, 5, empty × 5, -1: 0, x: "x"]
arr.length = 1;
arr; // [1, -1: 0, x: "x"]
arr[1] = 2;
arr; // [1, 2, -1: 0, x: "x"]
arr[3] = 4;
arr; // [1, 2, empty, 4, -1: 0, x: "x"]

从上面例子可以看出,向数组中手动添加非0和正整数的属性时,数组的length不会发生变化,而手动添加能成为索引的属性,length会自动发生变化,并且数组是连续的,自动将为空的部分留出来。

数组还有很多常用方法,具体可以分为下面几类:

// 栈方法
var arr = [1,2,3];
arr.push(0); // [1, 2, 3, 0] 末尾添加元素
var a = arr.pop(); // a = 0; arr = [1, 2, 3]; 末尾弹出元素

// 队列方法
arr.unshift(0); // [0, 1, 2, 3] 开头添加元素
a = arr.shift(); // a = 0; arr = [1, 2, 3]; 开头弹出元素

// 特别方法splice(start, ?deleteCount, items...) 第一个参数是操作起始位置,第二个参数是删除几个元素,
// 之后输入的参数都会被塞入数组中,并且方法会将删除的对象返回
arr.splice(2, 0, 5); // [1, 2, 5, 3]
arr.splice(1, 2); // [1, 3]

// join(char) 使用字符串将数组里的值连起来
['Hello', 'World'].join(','); // 'Hello,World'

// slice(start,end) 返回数组的一段,不改变数组
[1,2,3,4,5,6].slice(2, 4); // [3, 4]

// concat(array) 返回两个数组的合并,同样不改变原数组
[1, 3, 5].concat([2, 4, 6]); // [1, 3, 5, 2, 4, 6]

// reverse() 将数组逆序,会改变原数组
[1, 3, 5, 2, 4, 6].reverse(); // [6, 4, 2, 5, 3, 1]
var arr = [];
arr[2]=2;
arr[3]=3;
arr[7]=4;
arr[8]=5; // [empty × 2, 2, 3, empty × 3, 4, 5]
arr.reverse(); // [5, 4, empty × 3, 3, 2, empty × 2]

// sort(?compareFn) 可以对数组进行排序,默认按字母表排序,你也可以自定义排序方法;
[1, 3, 5, 4, 6, 2].sort(); // [1, 2, 3, 4, 5, 6]
[1, 3, 5, 4, 6, 2].sort((a, b) => b - a); // [6, 5, 4, 3, 2, 1]

// 此外还有forEach, fill, filter, reduce, map等非常实用的方法,推荐大家课后参考MDN去学习。
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array
[1, 2, 3, 4, 5, 6].forEach(a=>console.log(a)); // 打印数组全部元素
new Array(10).fill('a'); //填充 ["a", "a", "a", "a", "a", "a", "a", "a", "a", "a"]
[1, 2, 3, 4, 5, 6].filter(a => a>3 ); // [4, 5, 6] 过滤
[1, 2, 3, 4, 5, 6].reduce((a, b)=>a+b, 0); // 21 汇总
[1, 2, 3, 4, 5, 6].map(a=>a*2); // [2, 4, 6, 8, 10, 12] 映射

类数组对象

  • arguments(函数参数)
  • NodeList(DOM节点集合)
  • String(字符串)
  • TypedArray(ES8 类型数组)

Date 日期

Date是基于Unix时间戳表示某个时刻的对象,一般可以通过以下方式创建;

var now = new Date;
var t1 = new Date('2019-1-1 0:0:0'); // ios的safari浏览器不支持
var t2 = new Date('2019/1/1 12:30:0'); // 都支持
var t3 = new Date(1557915453973);
var t4 = new Date(2019, 1-1, 1);

另外Date自身做了很多容错性的设置,比如下面代码,可以用于获得一个月,一年的最后一天之类。

var t = new Date(2019, 5-1, 0); // Tue Apr 30 2019 00:00:00 GMT+0800 (中国标准时间)
t = new Date(2019, 5-1, -3); // Sat Apr 27 2019 00:00:00 GMT+0800 (中国标准时间)
t = new Date(); // Wed May 15 2019 19:11:45 GMT+0800 (中国标准时间)
t.setDate(0); // Tue Apr 30 2019 19:11:45 GMT+0800 (中国标准时间)

Date常用的方法如下。

方法 描述
getDate() 从 Date 对象返回一个月中的某一天 (1 ~ 31)。
getDay() 从 Date 对象返回一周中的某一天 (0 ~ 6)。
getFullYear() 从 Date 对象以四位数字返回年份。
getHours() 返回 Date 对象的小时 (0 ~ 23)。
getMilliseconds() 返回 Date 对象的毫秒(0 ~ 999)。
getMinutes() 返回 Date 对象的分钟 (0 ~ 59)。
getMonth() 从 Date 对象返回月份 (0 ~ 11)。
getSeconds() 返回 Date 对象的秒数 (0 ~ 59)。
getTime() 返回 1970 年 1 月 1 日至今的毫秒数。
getTimezoneOffset() 返回本地时间与格林威治标准时间 (GMT) 的分钟差。
getUTCDate() 根据世界时从 Date 对象返回月中的一天 (1 ~ 31)。
getUTCDay() 根据世界时从 Date 对象返回周中的一天 (0 ~ 6)。
getUTCFullYear() 根据世界时从 Date 对象返回四位数的年份。
getUTCHours() 根据世界时返回 Date 对象的小时 (0 ~ 23)。
getUTCMilliseconds() 根据世界时返回 Date 对象的毫秒(0 ~ 999)。
getUTCMinutes() 根据世界时返回 Date 对象的分钟 (0 ~ 59)。
getUTCMonth() 根据世界时从 Date 对象返回月份 (0 ~ 11)。
getUTCSeconds() 根据世界时返回 Date 对象的秒钟 (0 ~ 59)。
parse() 返回1970年1月1日午夜到指定日期(字符串)的毫秒数。
setDate() 设置 Date 对象中月的某一天 (1 ~ 31)。
setFullYear() 设置 Date 对象中的年份(四位数字)。
setHours() 设置 Date 对象中的小时 (0 ~ 23)。
setMilliseconds() 设置 Date 对象中的毫秒 (0 ~ 999)。
setMinutes() 设置 Date 对象中的分钟 (0 ~ 59)。
setMonth() 设置 Date 对象中月份 (0 ~ 11)。
setSeconds() 设置 Date 对象中的秒钟 (0 ~ 59)。
setTime() setTime() 方法以毫秒设置 Date 对象。
setUTCDate() 根据世界时设置 Date 对象中月份的一天 (1 ~ 31)。
setUTCFullYear() 根据世界时设置 Date 对象中的年份(四位数字)。
setUTCHours() 根据世界时设置 Date 对象中的小时 (0 ~ 23)。
setUTCMilliseconds() 根据世界时设置 Date 对象中的毫秒 (0 ~ 999)。
setUTCMinutes() 根据世界时设置 Date 对象中的分钟 (0 ~ 59)。
setUTCMonth() 根据世界时设置 Date 对象中的月份 (0 ~ 11)。
setUTCSeconds() setUTCSeconds() 方法用于根据世界时 (UTC) 设置指定时间的秒字段。
toDateString() 把 Date 对象的日期部分转换为字符串。
toISOString() 使用 ISO 标准返回字符串的日期格式。
toJSON() 以 JSON 数据格式返回日期字符串。
toLocaleDateString() 根据本地时间格式,把 Date 对象的日期部分转换为字符串。
toLocaleTimeString() 根据本地时间格式,把 Date 对象的时间部分转换为字符串。
toLocaleString() 据本地时间格式,把 Date 对象转换为字符串。
toString() 把 Date 对象转换为字符串。
toTimeString() 把 Date 对象的时间部分转换为字符串。
toUTCString() 根据世界时,把 Date 对象转换为字符串。
UTC() 根据世界时返回 1970 年 1 月 1 日 到指定日期的毫秒数。
valueOf() 返回 Date 对象的原始值。

RegExp 正则表达式

正则表达式是用于匹配字符串中字符组合的模式。在 JavaScript中,正则表达式也是对象。这些模式被用于 RegExpexectest 方法, 以及 Stringmatchreplacesearchsplit 方法。

有下面两种方式来创建正则表达式:

var regex = /ab+c/;
var    regex = /^[a-zA-Z]+[0-9]*\W?_$/gi;

var regex = new RegExp("ab+c");
var regex = new RegExp(/^[a-zA-Z]+[0-9]*\W?_$/, "gi");
var regex = new RegExp("^[a-zA-Z]+[0-9]*\\W?_$", "gi");

观察正则表达式,实际上正则表达式一般由两部分组成,表达式主体标志(修饰符)

先来看看最简单的正则表达式:

var reg = /java/i;
reg.test('abcdef'); // false
reg.test('java'); // true
reg.test('Java'); // true
reg.exec('I am study java and Javascript'); // ["java", index: 11, input: "I am study java and Javascript", groups: undefined]

'I am study java and Javascript'.match(reg); // ["java", index: 11, input: "I am study java and Javascript", groups: undefined]
'I am study java and Javascript'.replace(reg, 'lesson'); // "I am study lesson and Javascript"
'I am study java and Javascript'.search(reg); // 11
'I am study java and Javascript'.split(reg); // ["I am study ", " and ", "script"]

// 将修饰符变成g,或者去掉i
var reg = /java/
var reg = /java/g
var reg = /java/gi

通过上面的例子,我们了解到了修饰符的用法,一般有三种修饰符:

  • g : 表示全局(global)模式,即模式将被应用于所有字符串,而非在发现第一个匹配项时立即停止;
  • i : 表示不区分大小写(case-insensitive)模式,即在确定匹配项时忽略模式与字符串的大小写;
  • m : 表示多行(multiline)模式,即在到达一行文本末尾时还会继续查找下一行中是否存在与模式匹配的项。

对于表达式主体,除了使用ASCII字符,下面还有一些特殊的用法。

字符 含义
\ 匹配将依照下列规则:
在非特殊字符之前的反斜杠表示下一个字符是特殊的,不能从字面上解释。例如,前面没有’\‘的’b’通常匹配小写’b’,无论它们出现在哪里。如果加了’\‘,这个字符变成了一个特殊意义的字符,意思是匹配一个字符边界
反斜杠也可以将其后的特殊字符,转义为字面量。例如,模式 /a/ 代表会匹配 0 个或者多个 a。相反,模式 /a\/ 将 ‘‘ 的特殊性移除,从而可以匹配像 “a“ 这样的字符串。
使用 new RegExp(“pattern”) 的时候不要忘记将 \ 进行转义,因为 \ 在字符串里面也是一个转义字符。
^ 匹配输入的开始。如果多行标志被设置为true,那么也匹配换行符后紧跟的位置。
例如,/^A/ 并不会匹配 “an A” 中的 ‘A’,但是会匹配 “An E” 中的 ‘A’。
当 ‘^’ 作为第一个字符出现在一个字符集合模式时,它将会有不同的含义。
$ 匹配输入的结束。如果多行标示被设置为true,那么也匹配换行符前的位置。
例如,/t$/ 并不会匹配 “eater” 中的 ‘t’,但是会匹配 “eat” 中的 ‘t’。
* 匹配前一个表达式0次或多次。等价于 {0,}。
例如,/bo*/会匹配 “A ghost boooooed” 中的 ‘booooo’ 和 “A bird warbled” 中的 ‘b’,但是在 “A goat grunted” 中将不会匹配任何东西。
+ 匹配前面一个表达式1次或者多次。等价于 {1,}。
例如,/a+/匹配了在 “candy” 中的 ‘a’,和在 “caaaaaaandy” 中所有的 ‘a’。
? 匹配前面一个表达式0次或者1次。等价于 {0,1}。
例如,/e?le?/ 匹配 “angel” 中的 ‘el’,和 “angle” 中的 ‘le’ 以及”oslo’ 中的’l’。
如果紧跟在任何量词 *、 +、? 或 {} 的后面,将会使量词变为非贪婪的(匹配尽量少的字符),和缺省使用的贪婪模式(匹配尽可能多的字符)正好相反。
例如,对 “123abc” 应用 /\d+/ 将会返回 “123”,如果使用 /\d+?/,那么就只会匹配到 “1”。
还可以运用于先行断言,如本表的 x(?=y)x(?!y) 条目中所述。
. (小数点)匹配除换行符之外的任何单个字符。
例如,/.n/将会匹配 “nay, an apple is on the tree” 中的 ‘an’ 和 ‘on’,但是不会匹配 ‘nay’。
(x) 匹配 ‘x’ 并且记住匹配项,就像下面的例子展示的那样。括号被称为 捕获括号
模式/(foo) (bar) \1 \2/中的 ‘(foo)’ 和 ‘(bar)’ 匹配并记住字符串 “foo bar foo bar” 中前两个单词。模式中的 \1 和 \2 匹配字符串的后两个单词。注意 \1、\2、\n 是用在正则表达式的匹配环节。在正则表达式的替换环节,则要使用像 $1、$2、$n 这样的语法,例如,’bar foo’.replace( /(…) (…)/, ‘$2 $1’ )。
(?:x) 匹配 ‘x’ 但是不记住匹配项。这种叫作非捕获括号,使得你能够定义为与正则表达式运算符一起使用的子表达式。来看示例表达式 /(?:foo){1,2}/。如果表达式是 /foo{1,2}/,{1,2}将只对 ‘foo’ 的最后一个字符 ’o‘ 生效。如果使用非捕获括号,则{1,2}会匹配整个 ‘foo’ 单词。
x(?=y) 匹配’x’仅仅当’x’后面跟着’y’.这种叫做先行断言。
例如,/Jack(?=Sprat)/会匹配到’Jack’仅仅当它后面跟着’Sprat’。/Jack(?=Sprat|Frost)/匹配‘Jack’仅仅当它后面跟着’Sprat’或者是‘Frost’。但是‘Sprat’和‘Frost’都不是匹配结果的一部分。
(?<=y)x 匹配’x’仅仅当’x’前面是’y’.这种叫做后行断言。
例如,/(?<=Jack)Sprat/会匹配到’ Sprat ‘仅仅当它前面是’ Jack ‘。/(?<=Jack|Tom)Sprat/匹配‘ Sprat ’仅仅当它前面是’Jack’或者是‘Tom’。但是‘Jack’和‘Tom’都不是匹配结果的一部分。
x(?!y) 匹配’x’仅仅当’x’后面不跟着’y’,这个叫做正向否定查找。
例如,/\d+(?!\.)/匹配一个数字仅仅当这个数字后面没有跟小数点的时候。正则表达式/\d+(?!\.)/.exec(“3.141”)匹配‘141’而不是‘3.141’
[`x y`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions#special-or) 匹配‘x’或者‘y’。
例如,/green|red/匹配“green apple”中的‘green’和“red apple”中的‘red’
{n} n是一个正整数,匹配了前面一个字符刚好发生了n次。
比如,/a{2}/不会匹配“candy”中的’a’,但是会匹配“caandy”中所有的a,以及“caaandy”中的前两个’a’。
{n,m} n 和 m 都是整数。匹配前面的字符至少n次,最多m次。如果 n 或者 m 的值是0, 这个值被忽略。
例如,/a{1, 3}/ 并不匹配“cndy”中的任意字符,匹配“candy”中的a,匹配“caandy”中的前两个a,也匹配“caaaaaaandy”中的前三个a。注意,当匹配”caaaaaaandy“时,匹配的值是“aaa”,即使原始的字符串中有更多的a。
[xyz] 一个字符集合。匹配方括号中的任意字符,包括转义序列。你可以使用破折号(-)来指定一个字符范围。对于点(.)和星号(*)这样的特殊符号在一个字符集中没有特殊的意义。他们不必进行转义,不过转义也是起作用的。
例如,[abcd] 和[a-d]是一样的。他们都匹配”brisket”中的‘b’,也都匹配“city”中的‘c’。/[a-z.]+/ 和/[\w.]+/与字符串“test.i.ng”匹配。
[^xyz] 一个反向字符集。也就是说, 它匹配任何没有包含在方括号中的字符。你可以使用破折号(-)来指定一个字符范围。任何普通字符在这里都是起作用的。
例如,[^abc] 和 [^a-c] 是一样的。他们匹配”brisket”中的‘r’,也匹配“chop”中的‘h’。
[\b] 匹配一个退格(U+0008)。(不要和\b混淆了。)
\b 匹配一个词的边界。一个词的边界就是一个词不被另外一个“字”字符跟随的位置或者没有其他“字”字符在其前面的位置。注意,一个匹配的词的边界并不包含在匹配的内容中。换句话说,一个匹配的词的边界的内容的长度是0。(不要和[\b]混淆了)
例子:
/\bm/匹配“moon”中的‘m’;
/oo\b/并不匹配”moon”中的’oo’,因为’oo’被一个“字”字符’n’紧跟着。
/oon\b/匹配”moon”中的’oon’,因为’oon’是这个字符串的结束部分。这样他没有被一个“字”字符紧跟着。
/\w\b\w/将不能匹配任何字符串,因为在一个单词中间的字符永远也不可能同时满足没有“字”字符跟随和有“字”字符跟随两种情况。
注意: JavaScript的正则表达式引擎将特定的字符集定义为“字”字符。不在该集合中的任何字符都被认为是一个断词。这组字符相当有限:它只包括大写和小写的罗马字母,十进制数字和下划线字符。不幸的是,重要的字符,例如“é”或“ü”,被视为断词。
\B 匹配一个非单词边界。他匹配一个前后字符都是相同类型的位置:都是“字”字符或者都不是“字”字符。一个字符串的开始和结尾都被认为不是“字”字符,或者空字符串。
例如,/\B../匹配”noonday”中的’oo’, 而/y\B../匹配”possibly yesterday”中的’yes‘
\c_X_ 当X是处于A到Z之间的字符的时候,匹配字符串中的一个控制符。
例如,/\cM/ 匹配字符串中的 control-M (U+000D)。
\d 匹配一个数字
等价于[0-9]
例如, /\d/ 或者 /[0-9]/ 匹配”B2 is the suite number.”中的’2’。
\D 匹配一个非数字字符
等价于[^0-9]
例如, /\D/ 或者 /[^0-9]/ 匹配”B2 is the suite number.”中的’B’ 。
\f 匹配一个换页符 (U+000C)。
\n 匹配一个换行符 (U+000A)。
\r 匹配一个回车符 (U+000D)。
\s 匹配一个空白字符,包括空格、制表符、换页符和换行符。
等价于[ \f\n\r\t\v\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]。
例如, /\s\w*/ 匹配”foo bar.”中的’ bar’。
\S 匹配一个非空白字符。
等价于[^\f\n\r\t\v\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]
例如, /\S\w*/ 匹配”foo bar.”中的’foo’。
\t 匹配一个水平制表符 (U+0009)。
\v 匹配一个垂直制表符 (U+000B)。
\w 匹配一个单字字符(字母、数字或者下划线)。
等价于[A-Za-z0-9_]
例如, /\w/ 匹配 “apple,” 中的 ‘a’,”$5.28,”中的 ‘5’ 和 “3D.” 中的 ‘3’。
\W 匹配一个非单字字符。
等价于[^A-Za-z0-9_]
例如, /\W/ 或者 /[^A-Za-z0-9_]/ 匹配 “50%.” 中的 ‘%’。
\_n_ 在正则表达式中,它返回最后的第n个子捕获匹配的子字符串(捕获的数目以左括号计数)。
比如 /apple(,)\sorange\1/ 匹配”apple, orange, cherry, peach.”中的’apple, orange,’ 。
\0 匹配 NULL (U+0000) 字符, 不要在这后面跟其它小数,因为 \0<digits> 是一个八进制转义序列。
\xhh 与代码 hh 匹配字符(两个十六进制数字)
\uhhhh 与代码 hhhh 匹配字符(四个十六进制数字)。

来看几个例子:

/^[A-Za-z0-9]+$/   // 由大小写字母及数字组成的字符串
/^[A-Za-z0-9_]{3,16}$/  // 由大小写字母数字及下划线组成的3-16位的字符串(用户名)
/\d{15}(\d{2}[0-9xX])?/  // 身份证号(旧15位和新18位)
实用性:
输入校验
文本替换
将大段SQL替换成java的sb.append;
将下划线表示法替换成驼峰表示法

Function 函数

函数是 JavaScript 中的基本组件之一。 一个函数是 JavaScript 过程 — 一组执行任务或计算值的语句。
一个JavaScript 函数用function关键字定义,后面跟着函数名和圆括号。

一个函数定义(也称为函数声明,或函数语句)由一系列的function关键字组成,依次为:

  • 函数的名称。
  • 函数参数列表,包围在括号中并由逗号分隔。
  • 定义函数的 JavaScript 语句,用大括号{}括起来。

    函数声明

    例如,以下的代码定义了一个简单的square函数:
    function square(number) {
    return number * number;
    }
    
    函数square使用了一个参数,叫作number。这个函数只有一个语句,它说明该函数将函数的参数(即number)自乘后返回。函数的return语句确定了函数的返回值:
return number * number;

原始参数(比如一个具体的数字)被作为传递给函数;值被传递给函数,如果被调用函数改变了这个参数的值,这样的改变不会影响到全局或调用函数。
如果你传递一个对象(即一个非原始值,例如Array或用户自定义的对象)作为参数,而函数改变了这个对象的属性,这样的改变对函数外部是可见的,如下面的例子所示:

function myFunc(theObject) {
  theObject.make = "Toyota";
}

var mycar = {make: "Honda", model: "Accord", year: 1998};
var x, y;

x = mycar.make;     // x获取的值为 "Honda"

myFunc(mycar);
y = mycar.make;     // y获取的值为 "Toyota"
                    // (make属性被函数改变了)

函数表达式

虽然上面的函数声明在语法上是一个语句,但函数也可以由函数表达式创建。这样的函数可以是匿名的;它不必有一个名称。例如,函数square也可这样来定义:

var square = function(number) { return number * number; };
var x = square(4); // x gets the value 16

然而,函数表达式也可以提供函数名,并且可以用于在函数内部代指其本身,或者在调试器堆栈跟踪中识别该函数:

var factorial = function fac(n) {return n<2 ? 1 : n*fac(n-1)};
console.log(factorial(3));

当将函数作为参数传递给另一个函数时,函数表达式很方便。下面的例子演示了一个叫map的函数如何被定义,而后使用一个表达式函数作为其第一个参数进行调用:

function map(f,a) {
  var result = [],i; //创建一个新数组
  for (i = 0; i != a.length; i++)
    result[i] = f(a[i]);
  return result;
}

下面的代码:

function map(f, a) {
  var result = []; // 创建一个数组
  var i; // 声明一个值,用来循环
  for (i = 0; i != a.length; i++)
    result[i] = f(a[i]);
      return result;
}
var f = function(x) {
   return x * x * x; 
}
var numbers = [0,1, 2, 5,10];
var cube = map(f,numbers);
console.log(cube);

返回 [0, 1, 8, 125, 1000]。
在 JavaScript 中,可以根据条件来定义一个函数。比如下面的代码,当num 等于 0 的时候才会定义 myFunc

var myFunc;
if (num == 0){
  myFunc = function(theObject) {
    theObject.make = "Toyota"
  }
}

调用函数

定义一个函数并不会自动的执行它。定义了函数仅仅是赋予函数以名称并明确函数被调用时该做些什么。调用函数才会以给定的参数真正执行这些动作。例如,一旦你定义了函数square,你可以如下这样调用它:

square(5);

上述语句通过提供参数 5 来调用函数。函数执行完它的语句会返回值25。
函数一定要处于调用它们的域中,但是函数的声明可以被提升(出现在调用语句之后),如下例:

console.log(square(5));
/* ... */
function square(n) { return n*n }

函数域是指函数声明时的所在的地方,或者函数在顶层被声明时指整个程序。
提示:注意只有使用如上的语法形式(即 function funcName(){})才可以。而下面的代码是无效的。就是说,函数提升仅适用于函数声明,而不适用于函数表达式。

console.log(square); // square is hoisted with an initial value undefined.
console.log(square(5)); // TypeError: square is not a function
var square = function (n) { 
  return n * n; 
}

函数的参数并不局限于字符串或数字。你也可以将整个对象传递给函数。
函数可以被递归,就是说函数可以调用其本身。例如,下面这个函数就是用递归计算阶乘:

function factorial(n){
  if ((n == 0) || (n == 1))
    return 1;
  else
    return (n * factorial(n - 1));
}

你可以计算1-5的阶乘如下:

var a, b, c, d, e;
a = factorial(1); // 1赋值给a
b = factorial(2); // 2赋值给b
c = factorial(3); // 6赋值给c
d = factorial(4); // 24赋值给d
e = factorial(5); // 120赋值给e

还有其它的方式来调用函数。常见的一些情形是某些地方需要动态调用函数,或者函数的实参数量是变化的,或者调用函数的上下文需要指定为在运行时确定的特定对象。显然,函数本身就是对象,因此这些对象也有方法(参考Function )。作为此中情形之一,apply()方法可以实现这些目的。

函数作用域

在函数内定义的变量不能在函数之外的任何地方访问,因为变量仅仅在该函数的域的内部有定义。相对应的,一个函数可以访问定义在其范围内的任何变量和函数。换言之,定义在全局域中的函数可以访问所有定义在全局域中的变量。在另一个函数中定义的函数也可以访问在其父函数中定义的所有变量和父函数有权访问的任何其他变量。

// 下面的变量定义在全局作用域(global scope)中
var num1 = 20,
    num2 = 3,
    name = "Chamahk";

// 本函数定义在全局作用域
function multiply() {
  return num1 * num2;
}
multiply(); // 返回 60

// 嵌套函数的例子
function getScore() {
  var num1 = 2,
      num2 = 3;
  function add() {
    return name + " scored " + (num1 + num2);
  }
  return add();
}
getScore(); // 返回 "Chamahk scored 5"