From:菜鸟教程 JavaScripthttps://www.runoob/

W3School JavaScript 教程:http://www.w3school/js/index.asp    https://www.w3cschool/javascript/

廖雪峰官网 JavaScript 教程:https://www.liaoxuefeng/wiki/1022910821149312

MDN JavaScript:https://developer.mozilla/zh-CN/docs/Web/JavaScript

知乎 JavaScript :https://www.zhihu/search?type=content&q=javascript

Node.js 教程:http://www.runoob/nodejs/nodejs-tutorial.html

400多个JavaScript特效大全:https://wwwblogs/xiaoyunxiao/archive/2012/04/04/2431583.html

前言

  • JavaScript 是 Web 的编程语言,也是一种轻量级的编程语言。所有现代的 HTML 页面都使用 JavaScript。
  • JavaScript 可以嵌入到 HTML 页面。在 HTML 页面中插入 JavaScript 时使用 <script> 标签。<script> 和 </script> 会告诉 JavaScript 在何处开始和结束。<script> 和 </script> 之间的代码行包含了 JavaScript 代码。
  • JavaScript 插入 HTML 页面后,可以在 HTML 页面的任何地方调用 JS 函数 (必须用英文双引号 "js函数" 或者单引号 'j函数' 包括),然后由所有的现代浏览器执行JavaScript 语句是发给浏览器的命令。这些命令的作用是告诉浏览器要做的事情。
            HTML事件是发生在 HTML 元素上的事情。当在 HTML 页面中使用 JavaScript 时, JavaScript 可以触发这些事件。
            HTML 事件可以是浏览器行为,也可以是用户行为。
            HTML 事件的部分实例:HTML 页面完成加载、HTML input 字段改变时、HTML 按钮被点击 等。
            通常,当事件发生时,你可以做些事情。即事件触发时 JavaScript 可以执行一些代码。HTML 元素中可以添加事件属性。
            单引号:<some-HTML-element some-event='JavaScript 代码'>
            双引号:<some-HTML-element some-event="JavaScript 代码">
  • 经常看到 document.getElementById("some id")。这个方法是 HTML DOM 中定义的。DOM (Document Object Model)(文档对象模型)是用于访问 HTML 元素的正式 W3C 标准。
  • HTML 中的 js 脚本必须位于 <script> 与 </script> 标签之间。但是 js 脚本的调用可以在HTML 页面的任何部分。
  • JavaScript 对大小写敏感。函数 getElementById 与 getElementbyID 是不同的。同样,变量 myVariable 与 MyVariable 也是不同的。

示例:

<html>
<head>
    <script>
        function myfunction(){
            document.getElementById("demo").innerHTML = "onclick事件触发";
        }
    </script>
</head>
<body>
    <h1 id="demo">一个段落</h1>
    <button onClick="myfunction()" type="button">点击这里</button>
</body>
</html>
<!DOCTYPE html>
<html>
<head> 
<meta charset="utf-8"> 
<title>菜鸟教程(runoob)</title> 
</head>
<body>
	
<h1>我的第一个 Web 页面</h1>
<p>我的第一个段落。</p>
<button onclick="myFunction()">点我_1</button>
<button onclick="window.alert('test');">点我_2</button>
<script>
	function myFunction(){document.write(Date());}
</script>

</body>
</html>

HTML 输出流中使用 document.write,相当于添加在原有html代码中添加一串html代码。而如果在文档加载后使用(如使用函数),会覆盖整个文档。

使用函数来执行 document.write 代码如下:

<script>
function myfunction(){
    document.write("使用函数来执行doucment.write,即在文档加载后再执行这个操作,会实现文档覆盖");
}
document.write("<h1>这是一个标题</h1>");
document.write("<p>这是一个段落。</p>");
</script>
<p >
只能在 HTML 输出流中使用 <strong>document.write</strong>。
如果在文档已加载后使用它(比如在函数中),会覆盖整个文档。
</p>
<button type="button" onclick="myfunction()">点击这里</button>

作用域

理解JavaScript中的作用域和上下文:https://www.h5w3/57138.html
关于词法作用域的小疑惑:https://www.h5w3/62830.html
JavaScript 预编译与作用域:https://www.h5w3/70576.html
深入理解js作用域:https://www.h5w3/34296.html
js深入理解] 作用域一系列问题:https://www.h5w3/32056.html
JavaScript进阶-执行上下文栈和变量对象:https://juejin/post/6844904033308655623

Window 对象

window对象:window对象表示一个浏览器窗口或一个框架。在客户端JavaScript中,window对象是全局对象,所有的表达式都在当前的环境中计算。也就是说,要引用当前窗口根本不需要特殊的语法,可以把那个窗口的属性当作全局变量来使用。例如,可以只写document,而不必写window.document。

  • 所有浏览器都支持 window 对象。它表示浏览器窗口。
  • 所有 JavaScript 全局对象、函数以及变量均自动成为 window 对象的成员。
  • 全局变量是 window 对象的属性。
  • 全局函数是 window 对象的方法。
        甚至 HTML DOM 的 document 也是 window 对象的属性之一:
                window.document.getElementById("header");
        与此相同:
                document.getElementById("header");

JS 中的概念区分:global对象、window对象、document对象

1、Global Object (全局对象, global对象)

  • ①JavaScript 中有一个特殊的对象,称为全局对象(Global Object),它及其所有属性都可以在程序的任何地方访问,即全局变量。
  • ② 全局对象是预定义的对象,作为 JavaScript 的全局函数和全局属性的占位符。通过使用全局对象,可以访问所有其他所有预定义的对象、函数和属性。全局对象不是任何对象的属性,所以它没有名称。
  • 在顶层 JavaScript 代码中,可以用关键字 this 引用全局对象。但通常不必用这种方式引用全局对象,因为全局对象是作用域链的头,这意味着所有非限定性的变量和函数名都会作为该对象的属性来查询。例如,当JavaScript 代码引用 parseInt() 函数时,它引用的是全局对象的 parseInt 属性。全局对象是作用域链的头,还意味着在顶层 JavaScript 代码中声明的所有变量,都将成为全局对象的属性。
  • 全局对象只是一个对象,而不是类。既没有构造函数,也无法用new实例化一个新的全局对象。
  • ⑤ 实际上,ECMAScript 标准没有规定全局对象的类型,而在客户端 JavaScript 中,全局对象就是 Window 对象,即运行 JavaScript 代码的 Web 浏览器窗口。浏览器把全局对象作为 window 对象 的一部分实现了,因此,所有的全局属性和函数都是window对象的属性和方法。 

通俗化理解:

  • 《JavaScript高级程序设计》中谈到,global 对象可以说是 ECMAScript 中最特别的一个对象了,因为不管你从什么角度上看,这个对象都是不存在的。从某种意义上讲,它是一个终极的“兜底儿对象”,换句话说呢,就是不属于任何其他对象的属性和方法,最终都是它的属性和方法。我理解为,这个global对象呢,就是整个JS的“老祖宗”,找不到归属的那些“子子孙孙”都可以到它这里来认祖归宗。所有在全局作用域中定义的属性和函数,都是global对象的属性和方法,比如isNaN()、parseInt()以及parseFloat()等,实际都是它的方法;还有就是常见的一些特殊值,如:NaN、undefined等都是它的属性,以及一些构造函数Object、Array等也都是它的方法。总之,记住一点:global对象就是“老祖宗”,所有找不到归属的就都是它的。

注意:

  • "global" 这个单词本身是不可以直接访问的,本身就是一个概念,就是中文 "全局" 的英文原义,"Global Object" 翻译就是 "全局对象" 或 "global对象"。比如:global.Math.abs(1) 就是错误的,访问全局对象的属性时往往省略前缀,如:Math.abs(1) 就行了。而 window对象 是浏览器的JavaScript中具体存在的一个全局对象,是可以直接访问的。

示例 1: ( test.html ):

<script type="text/javascript">
    console.log(this);
</script>

<body>
    <h1>console.log(this);</h1>
</body>

打开 chrome 浏览器的 控制台,看到输出什么了吗?

window对象! 因为在全局作用域下,this 指向全局对象。在浏览器中全局对象就是 window 对象。

示例 2:

<script type="text/javascript">
    console.log(window); 
</script>

<body>
    <h1>console.log(window);</h1>
</body>

打开 chrome 浏览器的控制台:

输出 window对象,说明 "window"这个单词在浏览器JS中代表的就是一个真实存在的全局的对象,可以直接访问window这对象的。

示例 3:

<script type="text/javascript">
    console.log(global); 
</script>

<body>
    <h1>console.log(global);</h1>
</body>

运行结果:

显示 "Uncaught ReferenceError:global is not defined"。即global未定义,说明"global"这个单词不是一个默认的全局对象,不能在JS中直接访问的。正如前面所说,“global”只是中文”全局“的英语原义,不是一个对象,而是一个概念。

global 的属性和方法:http://blog.csdn/qq_32758013/article/details/74781788

2、window 对象

在全局作用域中声明的变量、函数都是window对象的属性和方法。

window对象 是相对于web浏览器而言的,依赖于浏览器,在浏览器中全局对象指的就是window对象,可以说window对象是全局对象在浏览器中的具体表现。

window 并不是ECMAScript规定的内置对象。内置对象的定义是:“由ECMAScript实现提供的、不依赖于宿主环境的对象,这些对象在ECMAScript程序执行之前就已经存在了。”  而 window对象就是宿主对象。而全局对象是在任何环境中都存在的。window对象具体也就是指浏览器打开的那个窗口。如果HTML文档包含框架(frame 或 iframe 标签),浏览器会为 HTML 文档创建一个 window 对象,并为每个框架创建一个额外的 window 对象。

因此,在全局作用域中声明的所有变量和函数,就都成为了window对象的属性。

来看下面的例子:

<script type="text/javascript">
    var name = "chunlynn";
    var color = "green"
    function sayName(){
        console.log(name);
    }
    function sayColor(){
        console.log(window.color);
    }
    sayName();         // output:chunlynn
    window.sayName();  // output:chunlynn
    sayColor();        // output:green
    window.sayColor(); //output:green
</script>


<body>
    <h1>"Test"</h1>
</body>

打开 chrome 浏览器的控制台:

这里定义了一个名为color 的全局变量和一个名为sayColor()的全局函数。在sayColor()内部,我们通过window.color 来访问color 变量,说明全局变量是window 对象的属性。然后又使用window.sayColor()来直接通过window 对象调用这个函数,说明全局作用域中声明的函数是window对象的方法。window对象一般是省略的

window 对象的属性和方法:http://wwwblogs/zengwei/archive/2007/11/02/946520.html

window的属性和方法比较多,这个链接可能说的不全仅供参考,具体的请在 chrome 后台中,输入window查看

3、document对象

document对象是window对象的一个属性,是显示于窗口内的一个文档。而 window 对象则是一个顶层对象,它不是另一个对象的属性。

document 可以理解为文档,就是你的网页,而 window 是你的窗口,就是你的浏览器包含的。

区别:

  • ① window 指窗体。document指页面。document是window的一个属性。 
  • ② 用户不能改变 document.location(因为这是当前显示文档的位置)。但是,可以改变window.location (用其它文档取代当前文档)window.location本身也是一个对象,而document.location不是对象
  • ③ W3C官方说明文档
        Window对象:http://www.w3school/htmldom/dom_obj_window.asp
        Document对象:http://www.w3school/htmldom/dom_obj_document.asp

在各种 JavaScript 运行环境中取得全局对象 global 的方法

From:https://wwwblogs/emmet7life/p/7691681.html

ES5 的顶层对象,本身也是一个问题,因为它在各种实现里面是不统一的。

  • 浏览器里面,顶层对象是window,但 Node 和 Web Worker 没有window
  • 浏览器和 Web Worker 里面,self也指向顶层对象,但是 Node 没有self
  • Node 里面,顶层对象是global,但其他环境都不支持。

同一段代码为了能够在各种环境,都能取到顶层对象,现在一般是使用this变量,但是有局限性。

  • 全局环境中,this会返回顶层对象。但是,Node 模块和 ES6 模块中,this返回的是当前模块。
  • 函数里面的this,如果函数不是作为对象的方法运行,而是单纯作为函数运行,this会指向顶层对象。但是,严格模式下,这时this会返回undefined
  • 不管是严格模式,还是普通模式,new Function('return this')(),总是会返回全局对象。但是,如果浏览器用了CSP(Content Security Policy,内容安全政策),那么evalnew Function这些方法都可能无法使用。

综上所述,很难找到一种方法,可以在所有情况下,都取到顶层对象。下面是两种勉强可以使用的方法。

// 方法一
(typeof window !== 'undefined'
   ? window
   : (typeof process === 'object' &&
      typeof require === 'function' &&
      typeof global === 'object')
     ? global
     : this);
 
// 方法二
var getGlobal = function () {
  if (typeof self !== 'undefined') { return self; }
  if (typeof window !== 'undefined') { return window; }
  if (typeof global !== 'undefined') { return global; }
  throw new Error('unable to locate global object');
};

现在有一个提案,在语言标准的层面,引入global作为顶层对象。也就是说,在所有环境下,global都是存在的,都可以从它拿到顶层对象。

垫片库system.global模拟了这个提案,可以在所有环境拿到global

// CommonJS 的写法
require('system.global/shim')();
 
// ES6 模块的写法
import shim from 'system.global/shim'; shim();

上面代码可以保证各种环境里面,global对象都是存在的。

// CommonJS 的写法
var global = require('system.global')();
 
// ES6 模块的写法
import getGlobal from 'system.global';
const global = getGlobal();

上面代码将顶层对象放入变量 global

this 对象 详解参考

this 永远指向 最后调用它 的 那个对象。。。 

但是 箭头函数this 是由 外层作用域决定的

【1】JS/JavaScript中 this 关键字对象详解:http://blog.csdn/chenchunlin526/article/details/78889301

this 概述

this 是面向对象语言中的一个重要概念,在 JAVA,C# 等大型语言中,this 固定指向运行时的当前对象。但是在 JavaScript中,由于 JavaScript 的动态性(解释执行,当然也有简单的预编译过程),this 的指向在运行时才确定。这个特性在给我们带来迷惑的同时,也带来了编程上的自由和灵活,结合 apply、call、bind 方法,可以使 JS 变得异常强大。this 是 Javascript 语言的一个关键字,它代表函数运行时自动生成的一个内部对象,一般在函数内部使用。

  • ① this 的值通常是由所在函数的执行环境决定,也就是说要看函数是如何被调用的;
  • ② 同一个函数每一次调用,this 都可能指向不同的对象;

先看下面一段简单的代码:

<script type="text/javascript">
    var name = "javascript";
    var obj = {
        name:'chunlynn',
        foo: function(){
            console.log(this.name);
        }
    }
    obj.foo(); //output:chunlynn
    console.log(this.name)    
</script>

问题1:obj是JavaScript中的什么类型?(想一想再往下看)

  • 答案为:obj是js中的对象类型(Object类型)。对象就是一些属性和方法的集合。

问题2:如果执行obj.foo(),会在控制台输出什么呢?

  • 答案为:chunlynn。

通过上面的这个小例子简单的认识下this:this 是 JS 对象中的一个特殊指针,它的指向根据环境不同而会发生改变。

面向对象语言中 this 表示当前对象的一个引用。

但在 JavaScript 中 this 不是固定不变的,它会随着执行环境的改变而改变。

  • 在方法中,this 表示该方法所属的对象。
  • 如果单独使用,this 表示全局对象。
  • 在函数中,this 表示全局对象。
  • 在函数中,在严格模式下,this 是未定义的(undefined)。
  • 在事件中,this 表示接收事件的元素。
  • 类似 call() 和 apply() 方法可以将 this 引用到任何对象。

示例:

var person = {
    firstName: "John",
    lastName : "Doe",
    id       : 5566,
    fullName : function() {
        return this.firstName + " " + this.lastName;
    }
};
 
console.log(person.fullName());
  • 方法中的 this。在对象方法中, this 指向调用它所在方法的对象。上面实例中,this 表示 person 对象。fullName 方法所属的对象就是 person。
  • 单独使用 this。单独使用 this 时则它指向 全局(Global)对象。在浏览器中,window 就是该全局对象为 [object Window]:

    示例:var x = this;  严格模式下,如果单独使用,this 也是指向全局(Global)对象。示例:"use strict";  var x = this;

  • 事件中的 this。在 HTML 事件句柄中,this 指向了接收事件的 HTML 元素:
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <title>菜鸟教程(runoob)</title>
    </head>
    <body>
     
    <h2>JavaScript <b>this</b> 关键字</h2>
     
    <button onclick="this.style.display='none'">点我后我就消失了</button>
     
    </body>
    </html>
  • 对象方法中绑定。下面实例中,this 是 person 对象,person 对象是函数的所有者。示例:

    var person = {
        firstName  : "John",
        lastName   : "Doe",
        id         : 5566,
        myFunction : function() {
            return this;
        }
    };

    示例:( 说明: this.firstName 表示 this (person) 对象的 firstName 属性。 )

    var person = {
      firstName: "John",
      lastName : "Doe",
      id       : 5566,
      fullName : function() {
        return this.firstName + " " + this.lastName;
      }
    };
  • 显式函数绑定。在 JavaScript 中函数也是对象,对象则有方法,apply 和 call 就是函数对象的方法。这两个方法异常强大,他们允许切换函数执行的上下文环境(context),即 this 绑定的对象。在下面实例中,当我们使用 person2 作为参数来调用 person1.fullName 方法时, this 将指向 person2, 即便它是 person1 的方法:

    var person1 = {
        fullName: function() {
            return this.firstName + " " + this.lastName;
        }
    }
    var person2 = {
        firstName:"John",
        lastName: "Doe",
    }
    person1.fullName.call(person2);  // 返回 "John Doe"

this 指向 的 用法分类详解

1、对象构造函数中的this (new关键字构造对象)

当使用new关键字构建一个新的对象,this会绑定到这个新对象。

<script type="text/javascript">
    var name = "chunlynn";
    //构造函数
    function Foo(){
        this.name = 'linda';
        this.foo = function(){
            console.log(this.name);
        }
    }
    var obj = new Foo(); // 实例化对象
    obj.foo(); //output:linda
</script>

注意构造函数和普通函数的区别,构造的属性用 this.name="chunlynn",普通函数用 var name = 'linda'。构造函数函数名称大写。

2、 普通函数中的this

 作为普通函数直接调用时,this 指向 window对象. 

<script type="text/javascript">
    var name = "chunlynn";
    //普通函数
    function foo(){
        var name = 'linda';
        console.log(this.name);
    }
    foo(); //output:chunlynn
</script>

下面再来看一个有些难度的:

<script type="text/javascript">
    var name = "chunlynn";
    //构造函数
    function Foo (){
        this.name = 'linda';
        this.foo = function(){
            var name = 'doudou';
            return function(){
                console.log(this.name);
            };
        }
    }
    
    var obj = new Foo();  // 实例化对象
    obj.foo()(); //output:chunlynn

    // 拆开分步执行如下:
    // var re = obj.foo();
    // re(); ---> window.re();
    // 这里是window调用的foo()函数,所以this指向window对象, this.name指的就是chunlynn
</script>

3、onclick(this)中的this指向

对于一个onclick属性,它为它所属的HTML元素所拥有,this指向该HTML元素。

<a href="javascript:void(0);" title="这个是一个标题!!" onclick="test(this);">A标签测试</a>
<script type="text/javascript">
    function test(obj){
        var value = obj.title;
        console.log(value);  //这是一个标题!!
    }
</script>

onclick 为它所属的 <a> 元素所有,this 指向 <a> 元素。

4、定时器 setTimeout 中的 this 指向

定时器中的this指向window对象。因为定时器就是全局函数,由window调用

先来看一个定时器的例子:

<script type="text/javascript">
    var name = "chunlynn";
    //普通函数
    function foo(){
        var name = 'linda';
        console.log(this.name);
    }
    setTimeout(foo, 5000); //output:chunlynn
    // this指向:谁调用或者哪个对象调用this所在的函数,this就指向谁。
    // 定时器为全局函数,由window调用,因此定时器中的this指向window对象。
</script>

下题输出结果是多少,说明为什么。

<script type="text/javascript">
    var name = "chunlynn";
    var obj = {
        name:'linda',
        fn: function(){
            console.log(this.name);
        },
        foo:function(){
            setTimeout(this.fn, 5000);
        }
    }
    obj.foo(); //output:chunlynn
    // this指向:谁调用或者哪个对象调用this所在的函数,this就指向谁。
    // 嵌套带入进去最终执行的就是
    // window.setTimeout(function (){console.log(this.name)}, 5000); this指向的就是window对象
</script>

换个思路,上面最终执行的代码为:

<script type="text/javascript">
    window.setTimeout(
        function (){
        // this指向的就是window对象,直接包含this.name的为setTimeout函数
        console.log(this.name);
        }
    , 5000);
</script>

这样答案一目了然了吧,this 绑定到了 window 对象。

5、使用 apply、call 或 bind 方法时 this 的指向

使用apply 、call 或 bind方法时,this指向所传递的对象。 

<script type="text/javascript">
    var name = "chunlynn";
    var obj = {
        name:'linda',
        foo:function(){
            console.log(this.name);
        }
    }
    var f = {};
    f.name = "doudou";
    f.fun = obj.foo; //将obj对象的foo函数赋值给f对象的fun属性。本质为复制。
    f.fun(); //output:doudou
    f.fun.apply(obj); //output:linda
    f.fun.apply(window); //output:doudou
    f.fun.apply(); //output:chunlynn
    // this指向:谁调用或者哪个对象调用this所在的函数,this就指向谁。
</script>

apply() 的参数为空时,默认调用全局对象 window。

查看 chrome 浏览器控制台:

6、ES6 箭头函数this

箭头函数的特征就是:定义在哪,this 就指向那。

//es6中的双箭头操作符,类似于java中的lamada表达式 
<script type="text/javascript">
    var author = "chunlynn";
    var book = {
        name: 'linda',
        init: function(){
            setTimeout(ev=>{
                console.log(this.name);
            },3000);
        }
    }
    book.init(); //output:linda
    // this指向:谁调用或者哪个对象调用this所在的函数,this就指向谁。
</script>

箭头函数的特征就是:定义在哪,this 就指向那。即箭头函数定义在一个对象里,那箭头函数里的 this 就指向该对象。

总结

  • ① 谁调用 或者 哪个对象调用 this 所在的函数,this 就指向谁。 如果有嵌套调用,则其值会被绑定到调用 this 所在函数的最近的父对象 ,不论这个 this 出现在什么样的函数中,层次有多深,结构多复杂,只要看直接包含它的函数即可
  • “this” always refers to the “owner” of the function we're executing。
  •  this 通常指向的是我们正在执行的函数本身,或者是,指向该函数所属的对象。
  • ④ this 是 Javascript 语言的一个关键字,它代表函数运行时自动生成的一个内部对象,只能在函数内部使用。

JavaScript 输出

JavaScript 没有任何打印或者输出的函数。JavaScript 可以通过不同的方式来输出数据:

  • 使用 window.alert() 弹出警告框。
  • 使用 document.write() 方法将内容写到 HTML 文档中。
  • 使用 innerHTML 写入到 HTML 元素。
  • 使用 console.log() 写入到浏览器的控制台。

JavaScript 变量

JavaScript 变量均为 对象。当声明一个变量时,就创建了一个新的对象。 JavaScript  一切皆对象。

声明(创建)

在 JavaScript 中创建变量通常称为"声明"变量。使用 var 关键词来声明变量:var carname;

变量声明之后,该变量是空的(它没有值)。如需向变量赋值,请使用等号:carname="Volvo";

也可以在声明变量时对其赋值:var carname="Volvo";

示例:创建名为 carname 的变量,并向其赋值 "Volvo",然后把它放入 id="demo" 的 HTML 段落中:

var carname="Volvo";
document.getElementById("demo").innerHTML=carname;

一条语句,多个变量

可以在一条语句中声明很多变量。该语句以 var 开头,并使用逗号分隔变量即可:var lastname="Doe", age=30, job="carpenter";

声明也可横跨多行:

var lastname="Doe",
age=30,
job="carpenter";

一条语句中声明的多个变量不可以同时赋同一个值:

var x,y,z=1;  // x,y 为 undefined, z 为 1。

Value = undefined

在计算机程序中,经常会声明无值的变量。未使用值来声明的变量,其值实际上是 undefined。

在执行过以下语句后,变量 carname 的值将是 undefined:var carname;

重新声明 JavaScript 变量

如果重新声明 JavaScript 变量,该变量的值不会丢失:

在以下两条语句执行后,变量 carname 的值依然是 "Volvo":

var carname="Volvo";
var carname;

变量提升

JavaScript 严格模式(strict mode)不允许使用未声明的变量。

JavaScript 中,函数及变量的声明都将被提升到函数的最顶部。

JavaScript 中,变量可以在使用后声明,也就是 变量可以先使用再声明

示例 1:( 提升 变量y )

var x = 10;  // 初始化 x
y = 20;
console.log(x + "" + y);  // y 是先使用,后面声明
var y;                    // 声明 变量 y
console.log(x + "" + y);

示例 2:

var x; // 声明 x
x = 10; // 变量 x 设置为 10

console.log(x)

要理解以上实例就需要理解 "hoisting(变量提升)"。

变量提升函数声明变量声明 总是会被解释器悄悄地被 "提升" 到 方法体的最顶部

JavaScript 初始化不会提升:即 JavaScript 只有声明的变量会提升初始化的不会

JavaScript 只有声明的变量会提升,初始化的不会。示例说明:

示例 1:初始化变量

var x = 5; // 初始化 x
var y = 7; // 初始化 y
console.log(x + y);

示例 2:(  y 输出了 undefined,这是因为变量声明 (var y) 提升了,但是初始化(y = 7) 并不会提升,所以 y 变量是一个未定义的变量。 )

var x = 5; // 初始化 x
console.log(x + "" + y);  // y 是先使用,后面声明和初始化

var y = 7; // 初始化 y

JavaScript 严格模式(use strict)

JavaScript 严格模式(strict mode)即在严格的条件下运行。

"use strict" 指令只允许出现在 脚本函数 的开头。

使用 "use strict" 指令

  • "use strict" 指令在 JavaScript 1.8.5 (ECMAScript5) 中新增。它不是一条语句,但是是一个字面量表达式,在 JavaScript 旧版本中会被忽略。
  • "use strict" 的目的是指定代码在严格条件下执行。严格模式下你不能使用未声明的变量
  • 严格模式的声明:严格模式通过在脚本或函数的头部添加 use strict; 表达式来声明。

示例代码:

"use strict";
x = 3.14;       // 报错 (x 未定义)
"use strict";
myFunction();

function myFunction() {
    y = 3.14;   // 报错 (y 未定义)
}

在函数内部声明是局部作用域 (只在函数内使用严格模式)。示例:

x = 3.14;       // 不报错
myFunction();

function myFunction() {
   "use strict";
    y = 3.14;   // 报错 (y 未定义)
}

为什么使用严格模式:

  • 消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
  • 消除代码运行的一些不安全之处,保证代码运行的安全;
  • 提高编译器效率,增加运行速度;
  • 为未来新版本的Javascript做好铺垫。

"严格模式"体现了Javascript更合理、更安全、更严谨的发展方向,包括IE 10在内的主流浏览器,都已经支持它,许多大项目已经开始全面拥抱它。

另一方面,同样的代码,在"严格模式"中,可能会有不一样的运行结果;一些在"正常模式"下可以运行的语句,在"严格模式"下将不能运行。掌握这些内容,有助于更细致深入地理解Javascript,让你变成一个更好的程序员。

严格模式的限制

1. 不允许使用未声明的变量。

"use strict";
x = 3.14;                // 报错 (x 未定义)

对象也是一个变量

"use strict";
x = {p1:10, p2:20};      // 报错 (x 未定义)

2. 不允许删除变量或对象。

"use strict";
var x = 3.14;
delete x;                // 报错

3. 不允许删除函数。

"use strict";
function x(p1, p2) {};
delete x;                // 报错

4. 不允许变量重名:

"use strict";
function x(p1, p1) {};   // 报错

5. 不允许使用八进制:

"use strict";
var x = 010;             // 报错

6. 不允许使用转义字符:

"use strict";
var x = \010;            // 报错

7. 不允许对只读属性赋值:

"use strict";
var obj = {};
Object.defineProperty(obj, "x", {value:0, writable:false});

obj.x = 3.14;            // 报错

8. 不允许对一个使用 getter 方法读取的属性进行赋值

"use strict";
var obj = {get x() {return 0} };

obj.x = 3.14;            // 报错

9. 不允许删除一个不允许删除的属性:

"use strict";
delete Object.prototype; // 报错

10. 变量名不能使用 "eval" 字符串:

"use strict";
var eval = 3.14;         // 报错

11. 变量名不能使用 "arguments" 字符串:

"use strict";
var arguments = 3.14;    // 报错

12. 不允许使用以下这种语句:

"use strict";
with (Math){x = cos(2)}; // 报错

13. 由于一些安全原因,在作用域 eval() 创建的变量不能被调用:

"use strict";
eval ("var x = 2");
alert (x);               // 报错

14. 禁止this关键字指向全局对象。

function f(){
    return !this;
} 
// 返回false,因为"this"指向全局对象,"!this"就是false

function f(){ 
    "use strict";
    return !this;
} 
// 返回true,因为严格模式下,this的值为undefined,所以"!this"为true。

因此,使用构造函数时,如果忘了加 new,this 不再指向全局对象,而是报错。

function f(){
    "use strict";
    this.a = 1;
};
f();// 报错,this未定义

15. 保留关键字

为了向将来Javascript的新版本过渡,严格模式新增了一些保留关键字:

  • implements
  • interface
  • let
  • package
  • private
  • protected
  • public
  • static
  • yield
"use strict";
var public = 1500;      // 报错

JavaScript 数据类型

值类型引用类型(即对象类型)

  • 值类型(基本类型):字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol。
  • 引用数据类型:对象(Object)、数组(Array)、函数(Function)。

注:Symbol 是 ES6 引入了一种新的原始数据类型,表示独一无二的值。

JavaScript 拥有动态类型

JavaScript 拥有动态类型。这意味着相同的变量可用作不同的类型,示例:

var x;               // x 为 undefined
var x = 5;           // 现在 x 为数字
var x = "John";      // 现在 x 为字符串

JavaScript 字符串

字符串是存储字符(比如 "Bill Gates")的变量。

字符串可以是引号中的任意文本。您可以使用单引号或双引号。实例:

var carname="Volvo XC60";
var carname='Volvo XC60';

您可以在字符串中使用引号,只要不匹配包围字符串的引号即可。实例:

var answer="It's alright";
var answer="He is called 'Johnny'";
var answer='He is called "Johnny"';

JavaScript 数字

JavaScript 只有一种数字类型。数字可以带小数点,也可以不带。实例:

var x1=34.00;      //使用小数点来写
var x2=34;         //不使用小数点来写

极大或极小的数字可以通过科学(指数)计数法来书写。实例:

var y=123e5;      // 12300000
var z=123e-5;     // 0.00123

JavaScript 布尔

布尔(逻辑)只能有两个值:true 或 false。布尔常用在条件测试中。

var x=true;
var y=false;

JavaScript 数组

下面的代码创建名为 cars 的数组:

var cars=new Array();
cars[0]="Saab";
cars[1]="Volvo";
cars[2]="BMW";

// 或者 (condensed array)
var cars=new Array("Saab","Volvo","BMW");

// 或者 (literal array):
var cars=["Saab","Volvo","BMW"];

数组下标是基于零的,所以第一个项目是 [0],第二个是 [1],以此类推。

JavaScript 对象

对象由花括号分隔。在括号内部,对象的属性以名称和值对的形式 (name : value) 来定义。属性由逗号分隔:

var person={firstname:"John", lastname:"Doe", id:5566};

上面例子中的对象 (person) 有三个属性:firstname、lastname 以及 id。

空格和折行无关紧要。声明可横跨多行:

var person={
    firstname : "John",
    lastname  : "Doe",
    id        :  5566
};

对象属性的两种寻址方式

name=person.lastname;
name=person["lastname"];

对象属性

JavaScript 对象:https://www.runoob/js/js-obj-intro.html

可以说 "JavaScript 中的对象是变量的容器"。( JavaScript的对象属性方法容器。 )

但是,我们通常认为 "JavaScript 对象是键值对的容器"。键值对通常写法为 name : value (键与值以冒号分割)。键值对在 JavaScript 对象通常称为 对象属性

对象键值对的写法类似于:

  • PHP 中的关联数组
  • Python 中的字典
  • C 语言中的哈希表
  • Java 中的哈希映射
  • Ruby 和 Perl 中的哈希表

示例:

<!DOCTYPE html>
<html>
<head> 
<meta charset="utf-8"> 
<title>菜鸟教程(runoob)</title> 
</head>
<body>

<p>创建和使用对象方法。</p>
<p>对象方法作为一个函数定义存储在对象属性中。</p>
<p id="demo"></p>
<script>
var person = {
    firstName: "John",
    lastName : "Doe",
    id : 5566,
    fullName : function() 
	{
       return this.firstName + " " + this.lastName;
    }
};
document.getElementById("demo").innerHTML = person.fullName();
</script>
	
</body>
</html>

name = person.fullName();   // 加上 () 表示调用函数。
name = person.fullName;     // 不加 () 表示属性,属性值 就是 函数体的代码

Undefined 和 Null

Undefined 这个值表示变量不含有值。

可以通过将变量的值设置为 null 来清空变量。实例:

cars=null;
person=null;

声明变量类型

当您声明新变量时,可以使用关键词 "new" 来声明其类型:

var carname=new String;
var x=      new Number;
var y=      new Boolean;
var cars=   new Array;
var person= new Object;

JavaScript 中的 值传递引用传递,共享传递

深入理解javascript按值传递与按引用传递:https://segmentfault/a/1190000012829900

基本类型 是 不可变类型,传递时 是 值传递

对象类型 是 不可变类型,传递时 是 引用传递

JavaScript 对函数形参的赋值是不可变类型,则不会影响实参的值,如果传递给形参是对象类型,由于对象是可变(mutable),当修改形参对象的属性值,也会影响到实参的属性值。

function changeStuff(num, obj1, obj2)
{
    num = num * 10;
    obj1.item = "changed";
    obj2 = {item: "changed"};
}

var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};
changeStuff(num, obj1, obj2);
console.log(num);   // 10
console.log(obj1.item);    // changed
console.log(obj2.item);    // unchanged

值传递

传值的意思就是:传内存拷贝。

函数内的num, obj1, obj2都将是一份新的内存,与调用函数之前定义的三个变量毫无关系。函数内无论怎么修改这三个参数,外部定义的三个变量的值始终不变

引用传递

传引用的意思就是:传内存指针

函数内的num, obj1, obj2都分别指向一块内存,该内存就是调用函数之前定义的三个变量时创建的内存。函数内对这三个参数所做的任何改动,都将反映到外部定义的三个变量上。

ECMAScript中所有函数的参数都是按值传递的。

也就是说,把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样。基本类型的传递如同基本类型的复制一样,而引用类型值的传递,如同引用类型变量的复制一样。

总结

很简单,javascript函数参数都是按值传递(都是栈内数据的拷贝)。
基本类型传的是值本身(因为直接把值存在栈内),引用类型传的是对象在内存里面的地址 (因为复杂对象存在堆内,所以在栈里存对象所在的堆地址)。

共享传递

JS是按值传递还是按引用传递? 在分析这个问题之前,我们需了解什么是按值传递(call by value),什么是按引用传递(call by reference)。在计算机科学里,这个部分叫求值策略(Evaluation Strategy)。它决定变量之间、函数调用时实参和形参之间值是如何传递的。

按值传递 VS. 按引用传递

按值传递(call by value)是最常用的求值策略:函数的形参是被调用时所传实参的副本。修改形参的值并不会影响实参。
按引用传递(call by reference)时,函数的形参接收实参的隐式引用,而不再是副本。这意味着函数形参的值如果被修改,实参也会被修改。同时两者指向相同的值。
按引用传递会使函数调用的追踪更加困难,有时也会引起一些微妙的BUG。
按值传递由于每次都需要克隆副本,对一些复杂类型,性能较低。两种传值方式都有各自的问题。
我们先看一个C的例子来了解按值和引用传递的区别: 

void Modify(int p, int * q)
{
    p = 27; // 按值传递 - p是实参a的副本, 只有p被修改
    *q = 27; // q是b的引用,q和b都被修改
}
int main()
{
    int a = 1;
    int b = 1;
    Modify(a, &b);   // a 按值传递, b 按引用传递,
                     // a 未变化, b 改变了
    return(0);
}

这里我们可以看到:

a => p按值传递时,修改形参p的值并不影响实参a,p只是a的副本。
b => q是按引用传递,修改形参q的值时也影响到了实参b的值。

探究JS值的传递方式

JS的基本类型,是按值传递的。

var a = 1;
function foo(x) {
    x = 2;
}
foo(a);
console.log(a); // 仍为1, 未受x = 2赋值所影响

再来看对象:

var obj = {x : 1};
function foo(o) {
    o.x = 3;
}
foo(obj);
console.log(obj.x); // 3, 被修改了!

说明 o 和 obj 是同一个对象,o 不是 obj 的副本。所以不是按值传递。 但这样是否说明 JS 的对象是按引用传递的呢?我们再看下面的例子:

var obj = {x : 1};
function foo(o) {
    o = 100;
}
foo(obj);
console.log(obj.x); // 仍然是1, obj并未被修改为100.

如果是按引用传递,修改形参o的值,应该影响到实参才对。但这里修改o的值并未影响obj。 因此JS中的对象并不是按引用传递。那么究竟对象的值在JS中如何传递的呢?

按共享传递 call by sharing

准确的说,JS中的基本类型按值传递,对象类型按共享传递的(call by sharing,也叫按对象传递、按对象共享传递)。最早由Barbara Liskov. 在1974年的GLU语言中提出。该求值策略被用于Python、Java、Ruby、JS等多种语言。

该策略的重点是:调用函数传参时,函数接受对象实参引用的副本(既不是按值传递的对象副本,也不是按引用传递的隐式引用)。 它和按引用传递的不同在于:在共享传递中对函数形参的赋值,不会影响实参的值。如下面例子中,不可以通过修改形参o的值,来修改obj的值。

var obj = {x : 1};
function foo(o) {
    o = 100;
}
foo(obj);
console.log(obj.x); // 仍然是1, obj并未被修改为100.

然而,虽然引用是副本,引用的对象是相同的。它们共享相同的对象,所以修改形参对象的属性值,也会影响到实参的属性值。

var obj = {x : 1};
function foo(o) {
    o.x = 3;
}
foo(obj);
console.log(obj.x); // 3, 被修改了!

对于对象类型,由于对象是可变(mutable)的,修改对象本身会影响到共享这个对象的引用和引用副本。而对于基本类型,由于它们都是不可变的(immutable),按共享传递与按值传递(call by value)没有任何区别,所以说JS基本类型既符合按值传递,也符合按共享传递。
var a = 1; // 1是number类型,不可变 var b = a; b = 6;
据按共享传递的求值策略,a和b是两个不同的引用(b是a的引用副本),但引用相同的值。由于这里的基本类型数字1不可变,所以这里说按值传递、按共享传递没有任何区别。
基本类型的不可变(immutable)性质
基本类型是不可变的(immutable),只有对象是可变的(mutable). 例如数字值100, 布尔值true, false,修改这些值(例如把1变成3, 把true变成100)并没有什么意义。比较容易误解的,是JS中的string。有时我们会尝试“改变”字符串的内容,但在JS中,任何看似对string值的”修改”操作,实际都是创建新的string值。

var str = "abc";
str[0]; // "a"
str[0] = "d";
str; // 仍然是"abc";赋值是无效的。没有任何办法修改字符串的内容

而对象就不一样了,对象是可变的。

var obj = {x : 1};
obj.x = 100;
var o = obj;
o.x = 1;
obj.x; // 1, 被修改
o = true;
obj.x; // 1, 不会因o = true改变

这里定义变量obj,值是object,然后设置obj.x属性的值为100。而后定义另一个变量o,值仍然是这个object对象,此时obj和o两个变量的值指向同一个对象(共享同一个对象的引用)。所以修改对象的内容,对obj和o都有影响。但对象并非按引用传递,通过o = true修改了o的值,不会影响obj。

JavaScript 类型转换

Number() 转换为数字, String() 转换为字符串, Boolean() 转换为布尔值。

JavaScript 数据类型

在 JavaScript 中有 6 种不同的数据类型:

  • string
  • number
  • boolean
  • object
  • function
  • symbol

3 种对象类型:

  • Object
  • Date
  • Array

2 个不包含任何值的数据类型:

  • null
  • undefined

typeof 操作符

你可以使用 typeof 操作符来查看 JavaScript 变量的数据类型。

console.log(typeof "John")                 // 返回 string
console.log(typeof 3.14)                   // 返回 number
console.log(typeof NaN)                    // 返回 number
console.log(typeof false)                  // 返回 boolean
console.log(typeof [1,2,3,4])              // 返回 object
console.log(typeof {name:'John', age:34})  // 返回 object
console.log(typeof new Date())             // 返回 object
console.log(typeof function () {})         // 返回 function
console.log(typeof myCar)                  // 返回 undefined (如果 myCar 没有声明)
console.log(typeof null)                   // 返回 object
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob)</title>
</head>
<body>

<p> typeof 操作符返回变量、对象、函数、表达式的类型。</p>
<p id="demo"></p>
<script>
document.getElementById("demo").innerHTML = 
    typeof "john" + "<br>" +
    typeof 3.14 + "<br>" +
    typeof NaN + "<br>" +
    typeof false + "<br>" +
    typeof [1,2,3,4] + "<br>" +
    typeof {name:'john', age:34} + "<br>" +
    typeof new Date() + "<br>" +
    typeof function () {} + "<br>" +
    typeof myCar + "<br>" +
    typeof null;
</script>

</body>
</html>

注意:

  • NaN 的数据类型是 number
  • 数组(Array)的数据类型是 object
  • 日期(Date)的数据类型为 object
  • null 的数据类型是 object
  • 未定义变量的数据类型为 undefined

如果对象是 JavaScript Array 或 JavaScript Date ,我们就无法通过 typeof 来判断他们的类型,因为都是 返回 object。

constructor 属性

constructor 属性返回所有 JavaScript 变量的 构造函数

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob)</title>
</head>
<body>

<p> constructor 属性返回变量或对象的构造函数。</p>
<p id="demo"></p>
<script>
document.getElementById("demo").innerHTML = 
    "john".constructor + "<br>" +
    (3.14).constructor + "<br>" +
    false.constructor + "<br>" +
    [1,2,3,4].constructor + "<br>" +
    {name:'john', age:34}.constructor + "<br>" +
    new Date().constructor + "<br>" +
    function () {}.constructor;
</script>

</body>
</html>

运行结果:

constructor 属性返回变量或对象的构造函数。

function String() { [native code] }
function Number() { [native code] }
function Boolean() { [native code] }
function Array() { [native code] }
function Object() { [native code] }
function Date() { [native code] }
function Function() { [native code] }

nodejs 执行结果:

console.log("John".constructor);                 // [Function: String]
console.log((3.14).constructor);                 // [Function: Number]
console.log(false.constructor);                  // [Function: Boolean]
console.log([1,2,3,4].constructor);              // [Function: Array]
console.log({name:'John', age:34}.constructor);  // [Function: Object]
console.log(new Date().constructor)              // [Function: Date]
console.log(function () {}.constructor)          // [Function: Function]

可以使用 constructor 属性来查看对象是否为数组 (包含字符串 "Array")。示例:判断是否是 array

function isArray(myArray) {
    return myArray.constructor.toString().indexOf("Array") > -1;
}

var str = '';
console.log(isArray(str));

使用 constructor 属性来查看对象是否为日期 (包含字符串 "Date"):

function isDate(myDate) {
    return myDate.constructor.toString().indexOf("Date") > -1;
}

JavaScript 类型转换

JavaScript 变量可以转换为 新变量其他数据类型

  • 通过使用 JavaScript 函数
  • 通过 JavaScript 自身自动转换

数字 转换为 字符串

全局方法 String() 可以将数字转换为字符串。该方法可用于任何类型的数字,字母,变量,表达式:

String(x)         // 将变量 x 转换为字符串并返回
String(123)       // 将数字 123 转换为字符串并返回
String(100 + 23)  // 将数字表达式转换为字符串并返回

Number 方法 toString() 也是有同样的效果。

var x = 12345;
console.log(x.toString());
console.log((123).toString());
console.log((10000 + 23).toString());

在 Number 方法 章节中,你可以找到更多数字转换为字符串的方法:

方法描述
toExponential()把对象的值转换为指数计数法。
toFixed()把数字转换为字符串,结果的小数点后有指定位数的数字。
toPrecision()把数字格式化为指定的长度。

布尔值 转换为 字符串

//全局方法 String() 可以将布尔值转换为字符串。
String(false) // 返回 "false"
String(true) // 返回 "true"

// Boolean 方法toString()也有相同的效果。
false.toString()// 返回 "false"
true.toString() // 返回 "true"

将日期转换为字符串

// Date() 返回字符串。
Date() // 返回 Thu Jul 17 2014 15:38:19 GMT+0200 (W. Europe Daylight Time)

// 全局方法 String() 可以将日期对象转换为字符串。
String(new Date())  // 返回 Thu Jul 17 2014 15:38:19 GMT+0200 (W. Europe Daylight Time)

// Date 方法toString()也有相同的效果。
obj = new Date()
obj.toString() // 返回 Thu Jul 17 2014 15:38:19 GMT+0200 (W. Europe Daylight Time)

在 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 日至今的毫秒数。

字符串 转换为 数字

全局方法 Number() 可以将字符串转换为数字。
字符串包含数字(如 "3.14") 转换为数字 (如 3.14)。
空字符串转换为 0。
其他的字符串会转换为 NaN (不是个数字)。

Number("3.14")  // 返回 3.14
Number(" ")     // 返回 0
Number("")      // 返回 0
Number("99 88") // 返回 NaN

在 Number 方法 章节中,你可以查看到更多关于字符串转为数字的方法:

方法描述
parseFloat()解析一个字符串,并返回一个浮点数。
parseInt()解析一个字符串,并返回一个整数。

一元运算符 +

Operator + 可用于将变量转换为数字。实例:

var y = "5";  // y 是一个字符串
var x = +y; // x 是一个数字
console.log(x)

如果变量不能转换,它仍然会是一个数字,但值为 NaN (不是一个数字)。实例:

var y = "John";   // y 是一个字符串
var x = + y;      // x 是一个数字 (NaN)
console.log(typeof x)
console.log(x)

将布尔值转换为数字

全局方法 Number() 可将布尔值转换为数字。

Number(false)     // 返回 0
Number(true)      // 返回 1

将日期转换为数字

// 全局方法 Number() 可将日期转换为数字。
d = new Date();
console.log(Number(d))  // 返回 时间戳

// 日期方法 getTime() 也有相同的效果。
d = new Date();
d.getTime()        // 返回 时间戳

自动转换类型

当 JavaScript 尝试操作一个 "错误" 的数据类型时,会自动转换为 "正确" 的数据类型。

以下输出结果不是你所期望的:

5 + null    // 返回 5         null 转换为 0
"5" + null  // 返回"5null"    null 转换为 "null"
"5" + 1     // 返回 "51"      1 转换为 "1" 
"5" - 1     // 返回 4         "5" 转换为 5

自动转换为字符串

当尝试输出一个对象或一个变量时 JavaScript 会自动调用变量的 toString() 方法:

// 当你尝试输出一个对象或一个变量时 JavaScript 会自动调用变量的 toString() 方法:

document.getElementById("demo").innerHTML = myVar;
myVar = {name:"Fjohn"}  // toString 转换为 "[object Object]"
myVar = [1,2,3,4]       // toString 转换为 "1,2,3,4"
myVar = new Date()      // toString 转换为 "Fri Jul 18 2014 09:08:55 GMT+0200"

// 数字和布尔值也经常相互转换:
myVar = 123             // toString 转换为 "123"
myVar = true            // toString 转换为 "true"
myVar = false           // toString 转换为 "false"

下表展示了使用不同的数值转换为数字(Number), 字符串(String), 布尔值(Boolean):

原始值转换为数字转换为字符串转换为布尔值实例
false0"false"false尝试一下 »
true1"true"true尝试一下 »
00"0"false尝试一下 »
11"1"true尝试一下 »
"0"0"0"true尝试一下 »
"000"0"000"true尝试一下 »
"1"1"1"true尝试一下 »
NaNNaN"NaN"false尝试一下 »
InfinityInfinity"Infinity"true尝试一下 »
-Infinity-Infinity"-Infinity"true尝试一下 »
""0""false尝试一下 »
"20"20"20"true尝试一下 »
"Runoob"NaN"Runoob"true尝试一下 »
[ ]0""true尝试一下 »
[20]20"20"true尝试一下 »
[10,20]NaN"10,20"true尝试一下 »
["Runoob"]NaN"Runoob"true尝试一下 »
["Runoob","Google"]NaN"Runoob,Google"true尝试一下 »
function(){}NaN"function(){}"true尝试一下 »
{ }NaN"[object Object]"true尝试一下 »
null0"null"false尝试一下 »
undefinedNaN"undefined"false尝试一下 »

let const 全局变量局部变量

总结:

  • 使用var关键字声明的全局作用域变量属于window对象。
  • 使用let关键字声明的全局作用域变量不属于window对象。
  • 使用var关键字声明的变量在任何地方都可以修改。
  • 在相同的作用域或块级作用域中,不能使用let关键字来重置var关键字声明的变量。
  • 在相同的作用域或块级作用域中,不能使用let关键字来重置let关键字声明的变量。
  • let关键字在不同作用域,或不用块级作用域中是可以重新声明赋值的。
  • 在相同的作用域或块级作用域中,不能使用const关键字来重置var和let关键字声明的变量。
  • 在相同的作用域或块级作用域中,不能使用const关键字来重置const关键字声明的变量
  • const 关键字在不同作用域,或不同块级作用域中是可以重新声明赋值的:
  • var关键字定义的变量可以先使用后声明。
  • let关键字定义的变量需要先声明再使用。
  • const关键字定义的常量,声明时必须进行初始化,且初始化后不可再修改。

ECMAScript 2015(ECMAScript 6)

ES2015(ES6) 新增加了两个重要的 JavaScript 关键字: let 和 const

  • let 声明的变量只在 let 命令所在的代码块内有效。
  • const 声明一个只读的常量,一旦声明,常量的值就不能改变。
  • 在 ES6 之前,JavaScript 只有两种作用域: 全局变量 与 函数内的局部变量

全局变量

在函数外声明的变量作用域是全局的,全局变量在 JavaScript 程序的任何地方都可以访问。示例:

var carName = "Volvo";
 
// 这里可以使用 carName 变量
 
function myFunction() {
    // 这里也可以使用 carName 变量
}

局部变量

函数内使用 var 声明的变量只能在函数内容访问,如果不使用 var 则是全局变量。

在函数内声明的变量作用域是局部的(函数内)。示例:

// 这里不能使用 carName 变量
 
function myFunction() {
    var carName = "Volvo";
    // 这里可以使用 carName 变量
}
 
// 这里不能使用 carName 变量

JavaScript 块级作用域(Block Scope)

使用 var 关键字声明的变量不具备块级作用域的特性,它在 {} 外依然能被访问到。

{ 
    var x = 2; 
}
// 这里可以使用 x 变量

在 ES6 之前,是没有块级作用域的概念的。
ES6 可以使用 let 关键字来实现块级作用域。
let 声明的变量只在 let 命令所在的代码块 {} 内有效,在 {} 之外不能访问。

{ 
    let x = 2;
}
// 这里不能使用 x 变量

重新定义变量

使用 var 关键字重新声明变量可能会带来问题。

在块中重新声明变量也会重新声明块外的变量:

示例:

var x = 10;
// 这里输出 x 为 10
{ 
    var x = 2;
    // 这里输出 x 为 2
}
// 这里输出 x 为 2

let 关键字就可以解决这个问题,因为它只在 let 命令所在的代码块 {} 内有效。

示例:

var x = 10;
// 这里输出 x 为 10
{ 
    let x = 2;
    // 这里输出 x 为 2
}
// 这里输出 x 为 10

循环作用域

使用 var 关键字,它声明的变量是全局的,包括循环体内与循环体外。示例:

var i = 5;
for (var i = 0; i < 10; i++) {
    // 一些代码...
}
// 这里输出 i 为 10

使用 let 关键字, 它声明的变量作用域只在循环体内,循环体外的变量不受影响。

let i = 5;
for (let i = 0; i < 10; i++) {
    // 一些代码...
}
// 这里输出 i 为 5

局部变量

在函数体内使用 var 和 let 关键字声明的变量有点类似。

它们的作用域都是 局部的:

// 使用 var
function myFunction() {
    var carName = "Volvo";   // 局部作用域
}

// 使用 let
function myFunction() {
    let carName = "Volvo";   //  局部作用域
}

全局变量

在函数体外或代码块外使用 var 和 let 关键字声明的变量也有点类似。

它们的作用域都是 全局的:

// 使用 var
var x = 2;       // 全局作用域

// 使用 let
let x = 2;       // 全局作用域

HTML 代码中使用全局变量

  • 在 JavaScript 中, 全局作用域是针对 JavaScript 环境。
  • 在 HTML 中, 全局作用域是针对 window 对象。使用 var 关键字声明的全局作用域变量属于 window 对象:

示例:( 示例:尝试一下 )

<!DOCTYPE html>
<html>
<head> 
<meta charset="utf-8"> 
<title>菜鸟教程(runoob)</title> 
</head>
<body>

<h2>JavaScript 全局变量</h2>

<p>使用 var 关键字声明的全局作用域变量属于 window 对象。</p>

<p id="demo"></p>

<script>
var carName = "Volvo";

// 可以使用 window.carName 访问变量
document.getElementById("demo").innerHTML = "I can display " + window.carName;
</script>

</body>
</html>

使用 let 关键字声明的全局作用域变量不属于 window 对象。示例:尝试一下

<!DOCTYPE html>
<html>
<head> 
<meta charset="utf-8"> 
<title>菜鸟教程(runoob)</title> 
</head>
<body>

<h2>JavaScript 全局变量</h2>

<p>使用 let 关键字声明的全局作用域变量不属于 window 对象。</p>

<p id="demo"></p>

<script>
let carName = "Volvo";

// 不能使用 window.carName 访问变量
document.getElementById("demo").innerHTML = "I can not display " + window.carName;
</script>

</body>
</html>

重置变量

使用 var 关键字声明的变量在任何地方都可以修改:

var x = 2;  // x 为 2
var x = 3;  // 现在 x 为 3

在相同的作用域或块级作用域中,不能使用 let 关键字来重置 var 关键字声明的变量:

var x = 2;       // 合法
let x = 3;       // 不合法

{
    var x = 4;   // 合法
    let x = 5   // 不合法
}

在相同的作用域或块级作用域中,不能使用 let 关键字来重置 let 关键字声明的变量:

let x = 2;       // 合法
let x = 3;       // 不合法

{
    let x = 4;   // 合法
    let x = 5;   // 不合法
}

在相同的作用域或块级作用域中,不能使用 var 关键字来重置 let 关键字声明的变量:

let x = 2;       // 合法
var x = 3;       // 不合法

{
    let x = 4;   // 合法
    var x = 5;   // 不合法
}

let 关键字在不同作用域,或不同块级作用域中是可以重新声明赋值的:

let x = 2;       // 合法

{
    let x = 3;   // 合法
}

{
    let x = 4;   // 合法
}

const 关键字

const 用于声明一个或多个常量,声明时必须进行初始化,且初始化后值不可再修改。示例:

const PI = 3.141592653589793;
PI = 3.14;      // 报错
PI = PI + 10;   // 报错

const 定义常量与使用 let  定义的变量相似:

  • 二者都是块级作用域
  • 都不能和它所在作用域内的其他变量或函数拥有相同的名称

两者还有以下两点区别:

  • const声明的常量必须初始化,而let声明的变量不用
  • const 定义常量的值不能通过再赋值修改,也不能再次声明。而 let 定义的变量值可以修改。
var x = 10;
// 这里输出 x 为 10
{ 
    const x = 2;
    // 这里输出 x 为 2
}
// 这里输出 x 为 10

const 声明的常量必须初始化:

// 错误写法
const PI;
PI = 3.14159265359;

// 正确写法
const PI = 3.14159265359;

并非真正的常量

可以看理解为指针,指向的地址不能变,但是地址里面的内容是可以变化的

const 的本质: const 定义的变量并非常量,并非不可变,它定义了一个常量引用一个值。使用 const 定义的对象或者数组,其实是可变的。

下面的代码并不会报错:

// 创建常量对象
const car = {type:"Fiat", model:"500", color:"white"};
 
// 修改属性:
car.color = "red";
 
// 添加属性
car.owner = "Johnson";

但是我们不能对常量对象重新赋值:

const car = {type:"Fiat", model:"500", color:"white"};
car = {type:"Volvo", model:"EX60", color:"red"};    // 错误

以下实例修改常量数组:

// 创建常量数组
const cars = ["Saab", "Volvo", "BMW"];
 
// 修改元素
cars[0] = "Toyota";
 
// 添加元素
cars.push("Audi");

但是我们不能对常量数组重新赋值:

const cars = ["Saab", "Volvo", "BMW"];
cars = ["Toyota", "Volvo", "Audi"];    // 错误

重置变量

使用 var 关键字声明的变量在任何地方都可以修改:

示例:

var x = 2; // 合法 var x = 3; // 合法 x = 4; // 合法

在相同的作用域或块级作用域中,不能使用 const 关键字来重置 var 和 let关键字声明的变量:

var x = 2;         // 合法
const x = 2;       // 不合法
{
    let x = 2;     // 合法
    const x = 2;   // 不合法
}

在相同的作用域或块级作用域中,不能使用 const 关键字来重置 const 关键字声明的变量:

const x = 2;       // 合法
const x = 3;       // 不合法
x = 3;             // 不合法
var x = 3;         // 不合法
let x = 3;         // 不合法

{
    const x = 2;   // 合法
    const x = 3;   // 不合法
    x = 3;         // 不合法
    var x = 3;     // 不合法
    let x = 3;     // 不合法
}

const 关键字在不同作用域,或不同块级作用域中是可以重新声明赋值的:

const x = 2;       // 合法

{
    const x = 3;   // 合法
}

{
    const x = 4;   // 合法
}

变量提升

JavaScript var 关键字定义的变量可以在使用后声明,也就是变量可以先使用再声明(JavaScript 变量提升)。

carName = "Volvo";    // 这里可以使用 carName 变量
var carName;

const 关键字定义的变量则不可以在使用后声明,也就是变量需要先声明再使用。

carName = "Volvo";    // 在这里不可以使用 carName 变量
const carName = "Volvo";

JavaScript 函数

函数就是包裹在花括号中的代码块,前面使用了关键词 function:

function functionname()
{
    // 执行代码
}

当调用该函数时,会执行函数内的代码。

调用带参数的函数

在调用函数时,可以向函数传递值,这些值被称为参数。这些参数可以在函数中使用。可以发送任意多的参数,由逗号 (,) 分隔:

myFunction(argument1,argument2)

当声明函数时,请把参数作为变量来声明:

function myFunction(var1,var2)
{
    // 代码
}

变量和参数必须以一致的顺序出现。第一个变量就是第一个被传递的参数的给定的值,以此类推。实例:

<!DOCTYPE html>
<html>	
<head> 
<meta charset="utf-8"> 
<title>菜鸟教程(runoob)</title> 
</head>
<body>

<p>点击这个按钮,来调用带参数的函数。</p>
<button onclick="myFunction('Harry Potter','Wizard')">点击这里</button>
<script>
function myFunction(name,job){
	alert("Welcome " + name + ", the " + job);
}
</script>

</body>
</html>

上面的函数在按钮被点击时会提示 "Welcome Harry Potter, the Wizard"。

局部 JavaScript 变量

在 JavaScript 函数内部声明的变量(使用 var)是 局部 变量,所以只能在函数内部访问它。(该变量的作用域是局部的)。

您可以在不同的函数中使用名称相同的局部变量,因为只有声明过该变量的函数才能识别出该变量。只要函数运行完毕,本地变量就会被删除。

全局 JavaScript 变量

在函数外声明的变量是 全局 变量,网页上的所有脚本和函数都能访问它。

JavaScript 变量的生存期

  • JavaScript 变量的生命期从它们被声明的时间开始。
  • 局部变量会在函数运行以后被删除。
  • 全局变量会在页面关闭后被删除。

向未声明的 JavaScript 变量分配值

如果您把值赋给尚未声明的变量,该变量将被自动作为 window 的一个属性。

这条语句:

carname="Volvo";  // 将声明 window 的一个属性 carname。

非严格模式下给未声明变量赋值创建的全局变量,是全局对象的可配置属性,可以删除。

var var1 = 1;  // 不可配置全局属性
var2 = 2;      // 没有使用 var 声明,可配置全局属性

console.log(this.var1);   // 1
console.log(window.var1); // 1
console.log(window.var2); // 2

delete var1;        // false 无法删除
console.log(var1);  //1

delete var2; 
console.log(delete var2); // true
console.log(var2);        // 已经删除 报错变量未定义

JavaScript 作用域

JavaScript 局部作用域

变量在函数内声明,变量为局部作用域。局部变量:只能在函数内部访问。

function myFunction() {
    var carName = "Volvo";
    // 函数内可调用 carName 变量
}
// 此处不能调用 carName 变量
console.log(carName)

因为局部变量只作用于函数内,所以不同的函数可以使用相同名称的变量。

局部变量在函数开始执行时创建,函数执行完后局部变量会自动销毁。

JavaScript 全局变量

变量在函数外定义,即为全局变量。全局变量有 全局作用域: 网页中所有脚本和函数均可使用。 

var carName = "Volvo";

// 此处可调用 carName 变量
function myFunction() {
    // 函数内可调用 carName 变量
    console.log(carName)
}
myFunction()

如果变量在函数内没有声明(没有使用 var 关键字),该变量为全局变量。以下实例中 carName 在函数内,但是为全局变量。

// 此处可调用 carName 变量
function myFunction() {
    // 函数内可调用 carName 变量
    carName = "Volvo";
    console.log(carName)
}
myFunction()
console.log(carName)

JavaScript 变量生命周期

  • JavaScript 变量生命周期在它声明时初始化。
  • 局部变量在函数执行完毕后销毁。函数参数只在函数内起作用,是局部变量。
  • 全局变量在页面关闭后销毁。在 HTML 中, 全局变量是 window 对象: 所有数据变量都属于 window 对象。

注意:你的全局变量,或者函数,可以覆盖 window 对象的变量或者函数。局部变量,包括 window 对象可以覆盖全局变量和函数。

<!DOCTYPE html>
<html>
<head> 
<meta charset="utf-8"> 
<title>菜鸟教程(runoob)</title> 
</head>
<body>

<p>
在 HTML 中, 所有全局变量都会成为 window 变量。
</p>
<p id="demo"></p>
<script>
myFunction();
document.getElementById("demo").innerHTML =
	"我可以显示 " + window.carName;
function myFunction() 
{
    carName = "Volvo";
}
</script>

</body>
</html>

JavaScript 代码规范

规范的代码可以更易于阅读与维护。代码规范一般在开发前规定,可以跟你的团队成员来协商设置。

代码规范通常包括以下几个方面:

  • 变量函数 的命名规则
  • 空格,缩进,注释 的使用规则。
  • 其他常用规范……

变量名。变量名推荐使用驼峰法来命名(camelCase):

firstName = "John";
lastName = "Doe";
price = 19.90;
tax = 0.20;
fullPrice = price + (price * tax);

空格与运算符。通常运算符 ( = + - * / ) 前后需要添加空格。实例:

var x = y + z;
var values = ["Volvo", "Saab", "Fiat"];

代码缩进。通常使用 4 个空格符号来缩进代码块。不推荐使用 TAB 键来缩进,因为不同编辑器 TAB 键的解析不一样。函数缩进

function toCelsius(fahrenheit) {
    return (5 / 9) * (fahrenheit - 32);
}

语句规则。简单语句的通用规则:一条语句通常以分号作为结束符。实例:

varvalues = ["Volvo", "Saab", "Fiat"];

varperson = {
    firstName: "John",
    lastName: "Doe",
    age: 50,
    eyeColor: "blue"
};

复杂语句的通用规则:

  • 将左花括号放在第一行的结尾。
  • 左花括号前添加一空格。
  • 将右花括号独立放在一行。
  • 不要以分号结束一个复杂的声明。

函数:

function toCelsius(fahrenheit) {
    return (5 / 9) * (fahrenheit - 32);
}

toCelsius()

var x=0, y;
for (var i = 0; i < 5; i++) {
    x += i;
    console.log(x.toString())
    console.log(y)
}

var time = 10
if (time < 20) {
    greeting = "Good day";
} else {
    greeting = "Good evening";
}
console.log(greeting)

对象定义的规则:

  • 将左花括号与类名放在同一行。
  • 冒号与属性值间有个空格。
  • 字符串使用双引号,数字不需要。
  • 最后一个属性-值对后面不要添加逗号。
  • 将右花括号独立放在一行,并以分号作为结束符号。

实例:

var person = {
    firstName: "John",
    lastName: "Doe",
    age: 50,
    eyeColor: "blue"
};

短的对象代码可以直接写成一行。实例:

var person = {firstName: "John", lastName: "Doe", age: 50, eyeColor: "blue"};

命名规则

一般很多代码语言的命名规则都是类似的,例如:

  • 变量和函数为小驼峰法标识, 即除第一个单词之外,其他单词首字母大写( lowerCamelCase)
  • 全局变量为大写 (UPPERCASE )
  • 常量 (如 PI) 为大写 (UPPERCASE )

变量命名你是否使用这几种规则: hyp-henscamelCase, 或 under_scores ?

HTML 和 CSS 的横杠(-)字符:

HTML5 属性可以以 data- (如:data-quantity, data-price) 作为前缀。

CSS 使用 - 来连接属性名 (font-size)。

- 通常在 JavaScript 中被认为是减法,所以不允许使用。

变量名不要以 $ 作为开始标记,会与很多 JavaScript 库冲突。

下划线:

  • 很多程序员比较喜欢使用下划线(如:date_of_birth), 特别是在 SQL 数据库中。PHP 语言通常都使用下划线。
  • 帕斯卡拼写法(PascalCase):帕斯卡拼写法(PascalCase) 在 C 语言中语言较多。
  • 驼峰法:JavaScript 中通常推荐使用驼峰法,jQuery 及其他 JavaScript 库都使用驼峰法。

HTML 载入外部 JavaScript 文件

使用简洁的格式载入 JavaScript 文件 ( type 属性不是必须的):<script src="myscript.js">

使用 JavaScript 访问 HTML 元素

一个糟糕的 HTML 格式可能会导致 JavaScript 执行错误。以下两个 JavaScript 语句会输出不同结果。实例:

var obj = getElementById("Demo")
var obj = getElementById("demo")尝试一下 »

HTML 与 JavaScript 尽量使用相同的命名规则。访问 HTML(5) 代码规范。

文件扩展名

  • HTML 文件后缀可以是 .html (或 .htm)。
  • CSS 文件后缀是 .css 。
  • JavaScript 文件后缀是 .js 。

使用小写文件名

  • 大多 Web 服务器 (Apache, Unix) 对大小写敏感: london.jpg 不能通过 London.jpg 访问。
  • 其他 Web 服务器 (Microsoft, IIS) 对大小写不敏感: london.jpg 可以通过 London.jpg 或 london.jpg 访问。
  • 你必须保持统一的风格,建议统一使用小写的文件名。

JavaScript 事件

HTML 事件是发生在 HTML 元素上的事情。当在 HTML 页面中使用 JavaScript 时, JavaScript 可以触发这些事件。

HTML 事件可以是浏览器行为,也可以是用户行为。以下是 HTML 事件的实例:

  • HTML 页面完成加载
  • HTML input 字段改变时
  • HTML 按钮被点击

通常,当事件发生时,你可以做些事情。在事件触发时 JavaScript 可以执行一些代码。

HTML 元素中可以添加事件属性,使用 JavaScript 代码来添加 HTML 元素。

  • 单引号:<some-HTML-element some-event='JavaScript 代码'>
  • 双引号:<some-HTML-element some-event="JavaScript 代码">

按钮元素中添加了 onclick 属性:<button οnclick="getElementById('demo').innerHTML=Date()">现在的时间是?</button>

代码将修改自身元素的内容 (使用 this.innerHTML):<button οnclick="this.innerHTML=Date()">现在的时间是?</button>

常见的 HTML 事件

下面是一些常见的HTML事件的列表:

事件描述
onchangeHTML 元素改变
onclick用户点击 HTML 元素
onmouseover用户在一个HTML元素上移动鼠标
onmouseout用户从一个HTML元素上移开鼠标
onkeydown用户按下键盘按键
onload浏览器已完成页面的加载

更多事件列表: JavaScript 参考手册 - HTML DOM 事件。

JavaScript 可以做什么?

事件可以用于处理表单验证,用户输入,用户行为及浏览器动作:

  • 页面加载时触发事件
  • 页面关闭时触发事件
  • 用户点击按钮执行动作
  • 验证用户输入内容的合法性
  • 等等 ...

可以使用多种方法来执行 JavaScript 事件代码:

  • HTML 事件属性可以直接执行 JavaScript 代码
  • HTML 事件属性可以调用 JavaScript 函数
  • 你可以为 HTML 元素指定自己的事件处理程序
  • 你可以阻止事件的发生。
  • 等等 ...

在 HTML DOM 章节中你将会学到更多关于事件及事件处理程序的知识。

JavaScript 字符串

字符串用于存储和处理文本。

  • 字符串可以是插入到引号中的任何字符。可以使用 单引号双引号。
  • 字符串的索引从 0 开始,这意味着第一个字符索引值为 [0],第二个为 [1], 以此类推。
var carname = "Volvo XC60";
var carname = 'Volvo XC60';

// 可以使用索引位置来访问字符串中的每个字符。
var character = carname[7];

可以在字符串中使用引号,字符串中的引号不要与字符串的引号相同,实例:

var answer = "It's alright";
var answer = "He is called 'Johnny'";
var answer = 'He is called "Johnny"';

你也可以在字符串添加转义字符来使用引号。实例:

var x = 'It\'s alright';
var y = "He is called \"Johnny\"";

字符串长度

可以使用内置属性 length 来计算字符串的长度:

var txt = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var txt_len = txt.length;
console.log('txt_len = ' + txt_len)
for (var i = 0; i < txt_len; i++) {
    console.log(txt[i])
}

特殊字符

在 JavaScript 中,字符串写在单引号或双引号中。因为这样,以下实例 JavaScript 无法解析: "We are the so-called "Vikings" from the north."

字符串 "We are the so-called " 被截断。

如何解决以上的问题呢?可以使用反斜杠 (\) 来转义 "Vikings" 字符串中的双引号,如下: "We are the so-called \"Vikings\" from the north."

 反斜杠是一个转义字符。 转义字符将特殊字符转换为字符串字符。转义字符 (\) 可以用于转义撇号,换行,引号,等其他特殊字符。

下表中列举了在字符串中可以使用转义字符转义的特殊字符:

代码输出
\'单引号
\"双引号
\\反斜杠
\n换行
\r回车
\ttab(制表符)
\b退格符
\f换页符

字符串可以是对象

  • 通常, JavaScript 字符串是原始值,可以使用字符创建: var firstName = "John"
  • 但我们也可以使用 new 关键字将字符串定义为一个对象: var firstName = new String("John")
var x = "John";
var y = new String("John");
typeof x  // 返回 String
typeof y  // 返回 Object

注意:不要创建 String 对象。它会拖慢执行速度,并可能产生其他副作用。( === 为绝对相等,即数据类型与值都必须相等。 )

var x = "John";
var y = new String("John");
var bval = (x === y)   // 结果为 false,因为 x 是字符串,y 是对象
console.log(bval)      // 输出结果 false

字符串属性和方法

原始值字符串,如 "John", 没有属性和方法(因为他们不是对象)。

原始值可以使用 JavaScript 的属性和方法,因为 JavaScript 在执行方法和属性时可以把原始值当作对象。

字符串方法我们将在下一章节中介绍。

字符串属性

属性描述
constructor返回创建字符串属性的函数
length返回字符串的长度
prototype允许您向对象添加属性和方法

字符串方法

更多方法实例可以参见:JavaScript String 对象。

方法描述
charAt()返回指定索引位置的字符
charCodeAt()返回指定索引位置字符的 Unicode 值
concat()连接两个或多个字符串,返回连接后的字符串
fromCharCode()将 Unicode 转换为字符串
indexOf()返回字符串中检索指定字符第一次出现的位置
lastIndexOf()返回字符串中检索指定字符最后一次出现的位置
localeCompare()用本地特定的顺序来比较两个字符串
match()找到一个或多个正则表达式的匹配
replace()替换与正则表达式匹配的子串
search()检索与正则表达式相匹配的值
slice()提取字符串的片断,并在新的字符串中返回被提取的部分
split()把字符串分割为子字符串数组
substr()从起始索引号提取字符串中指定数目的字符
substring()提取字符串中两个指定的索引号之间的字符
toLocaleLowerCase()根据主机的语言环境把字符串转换为小写,只有几种语言(如土耳其语)具有地方特有的大小写映射
toLocaleUpperCase()根据主机的语言环境把字符串转换为大写,只有几种语言(如土耳其语)具有地方特有的大小写映射
toLowerCase()把字符串转换为小写
toString()返回字符串对象值
toUpperCase()把字符串转换为大写
trim()移除字符串首尾空白
valueOf()返回某个字符串对象的原始值

JavaScript 运算符

运算符优先级https://developer.mozilla/zh-CN/docs/Web/JavaScript/Reference/Operators/Operator_Precedence

运算符 = 用于赋值。运算符 = 用于给 JavaScript 变量赋值。

运算符 + 用于加值。算术运算符 + 用于把值加起来。

实例:指定变量值,并将值相加:

y = 5;
z = 2;
x = y + z; //执行后,x 的值是 7

JavaScript 算术运算符

运算符描述例子x 运算结果y 运算结果在线实例
+加法x=y+275实例 »
-减法x=y-235实例 »
*乘法x=y*2105实例 »
/除法x=y/22.55实例 »
%取模(余数)x=y%215实例 »
++自增x=++y66实例 »
x=y++56实例 »
--自减x=--y44实例 »
x=y--54实例 »

JavaScript 赋值运算符

赋值运算符用于给 JavaScript 变量赋值。赋值运算符:

运算符例子等同于运算结果在线实例
=x=y x=5实例 »
+=x+=yx=x+yx=15实例 »
-=x-=yx=x-yx=5实例 »
*=x*=yx=x*yx=50实例 »
/=x/=yx=x/yx=2实例 »
%=x%=yx=x%yx=0实例 »

用于字符串的 + 运算符

+ 运算符用于把文本值或字符串变量加起来(连接起来)。如需把两个或多个字符串变量连接起来,请使用 + 运算符。

实例:如需把两个或多个字符串变量连接起来,请使用 + 运算符:

txt1 = "What a very";
txt2 = "nice day";
txt3 = txt1 + txt2; // txt3 值为 What a verynice day

字符串数字进行加法运算

规则:如果把数字与字符串相加,结果将成为字符串!

两个数字相加,返回数字相加的和,如果数字与字符串相加,返回字符串,如下实例:

x = 5 + 5;        // x 值为 10
y = "5" + 5;      // y 值为 55
z = "Hello" + 5;  // z 值为 Hello5
a = 123 + 'hello' // a 值为 123hello

JavaScript 比较 和 逻辑 运算符

比较和逻辑运算符用于测试 true 或者 false

比较运算符

比较运算符在逻辑语句中使用,以测定变量或值是否相等。比较运算符:

运算符描述比较返回值实例
==等于x==8false实例 »
x==5true实例 »
===绝对等于(值和类型均相等)x==="5"false实例 »
x===5true实例 »
!= 不等于x!=8true实例 »
!== 不绝对等于(值和类型有一个不相等,或两个都不相等)x!=="5"true实例 »
x!==5false实例 »
> 大于x>8false实例 »
< 小于x<8true实例 »
>= 大于或等于x>=8false实例 »
<= 小于或等于x<=8true实例 »

逻辑运算符

逻辑运算符用于测定变量或值之间的逻辑。逻辑运算符:

运算符描述例子
&&and(x < 10 && y > 1) 为 true
||or(x==5 || y==5) 为 false
!not!(x==y) 为 true

注意:

  • &    按 位 与
  • |     按 位 或  
function a(){console.log('a');return true}
function b(){console.log('b');return false}
function c(){console.log('c');return undefined}
function d(){console.log('d');return null}

result = a() && b()      // a b false
result = a() & b()       // a b 0

result = a() ||  b()     // a true
result = a() |  b()      // a b 1

result = a() || b() && c() || d()       // 等价于 a() || ''  ,所以 a true
result = a() &&  b() || c()  && d()     // a b c undefined
result = (a() ||  b()) &&  c() || d()   // a c d null
result = (a() ||  b()) && (c() || d())  // a c d null
result = a() ||  b()  && (c() || d())   // a true
result = a() ||  (b() && c()) || d()    // a true

条件运算符

JavaScript 还包含了基于某些条件对变量进行赋值的条件运算符。

语法:variablename = (condition)?value1:value2;

//如果变量 age 中的值小于 18,则向变量 voteable 赋值 "年龄太小",否则赋值 "年龄已达到"。
voteable = (age < 18) ? "年龄太小" : "年龄已达到";

JavaScript 语句

if else 条件语句

条件语句用于基于不同的条件来执行不同的动作。

在写代码时,需要为不同的决定来执行不同的动作。可以在代码中使用条件语句来完成该任务。

在 JavaScript 中,我们可使用以下条件语句:

  • if 语句                              ----- 只有当指定条件为 true 时,使用该语句来执行代码
  • if...else 语句                    ----- 当条件为 true 时执行代码,当条件为 false 时执行其他代码
  • if...else if....else 语句     ----- 使用该语句来选择多个代码块之一来执行
  • switch 语句                     ----- 使用该语句来选择多个代码块之一来执行
// if 语句
if (condition)
{
    // 当条件为 true 时执行的代码
}

// if else 语句
if (condition)
{
    // 当条件为 true 时执行的代码
}
else
{
    // 当条件不为 true 时执行的代码
}

// if else if else 语句
if (condition1)
{
    // 当条件 1 为 true 时执行的代码
}
else if (condition2)
{
    // 当条件 2 为 true 时执行的代码
}
else
{
    // 当条件 1 和 条件 2 都不为 true 时执行的代码
}

switch 语句

switch 语句用于基于不同的条件来执行不同的动作。

switch(n)
{
    case 1:
        // 执行代码块 1
        break;
    case 2:
        // 执行代码块 2
        break;
    default:
        // 与 case 1 和 case 2 不同时执行的代码
}

工作原理:首先设置表达式 n(通常是一个变量)。随后表达式的值会与结构中的每个 case 的值做比较。如果存在匹配,则与该 case 关联的代码块会被执行。请使用 break 来阻止代码自动地向下一个 case 运行。

实例:显示今天的星期名称。请注意 Sunday=0, Monday=1, Tuesday=2, 等等:

var d = new Date().getDay();
switch (d) {
    case 0:
        x = "今天是星期日";
        break;
    case 1:
        x = "今天是星期一";
        break;
    case 2:
        x = "今天是星期二";
        break;
    case 3:
        x = "今天是星期三";
        break;
    case 4:
        x = "今天是星期四";
        break;
    case 5:
        x = "今天是星期五";
        break;
    case 6:
        x = "今天是星期六";
        break;
}
console.log(x)

default 关键词来规定匹配不存在时做的事情:

var d = new Date().getDay();
switch (d) {
    case 6:
        x = "今天是星期六";
        break;
    case 0:
        x = "今天是星期日";
        break;
    default:
        x = "期待周末";
}
console.log(x)

for 循环

JavaScript 支持不同类型的循环:

  • for - 循环代码块一定的次数
  • for/in - 循环遍历对象的属性
  • while - 当指定的条件为 true 时循环指定的代码块
  • do/while - 同样当指定的条件为 true 时循环指定的代码块

for 循环是您在希望创建循环时常会用到的工具。

for 循环的语法:
        for (语句1; 语句2; 语句3)
        {
            被执行的代码块
        }
说明:
        语句1 (代码块)开始前执行
        语句2 定义运行循环(代码块)的条件
        语句3 在循环(代码块)已被执行之后执行

使用 for 循环

for (var i = 0; i < cars.length; i++) {
    document.write(cars[i] + "<br>");
}
for (var i = 0; i < 5; i++) {
    x = x + "该数字为 " + i + "<br>";
}

语句1
        通常使用 语句1 初始化循环中所用的变量 (var i=0)。
        语句1 是可选的,也就是说不使用 语句1 也可以。
        可以在 语句1 中初始化任意(或者多个)值
语句2
        通常 语句2 用于评估初始变量的条件。
        如果 语句2 返回 true,则循环再次开始,如果返回 false,则循环将结束。
        语句2 同样是可选的。        
        如果省略了 语句2,那么必须在循环内提供 break。否则循环就无法停下来。这样有可能令浏览器崩溃。
语句3
        通常 语句3 会增加初始变量的值。
        语句3 也是可选的。
        语句3 有多种用法。增量可以是负数 (i--),或者更大 (i=i+15)。
        语句3 也可以省略(比如当循环内部有相应的代码时)

for (var i = 0, len = cars.length; i < len; i++) {
    document.write(cars[i] + "<br>");
}

// 省略 语句1
var i = 2, len = cars.length;
for (; i < len; i++) {
    document.write(cars[i] + "<br>");
}

// 省略 语句3
var i = 0, len = cars.length;
for (; i < len;) {
    document.write(cars[i] + "<br>");
    i++;
}

for / In 循环

JavaScript 的 for / in 语句循环遍历对象的属性。( 在有关 JavaScript 对象的章节学到更多有关 for / in 循环的知识。 )

var person = {fname: "Bill", lname: "Gates", age: 56};

var txt = '';
for (x in person)  // x 为属性名
{
    txt = txt + person[x];
}
console.log(txt)

while 循环

while 循环会在指定条件为真时循环执行代码块。

语法:
        while (条件)
        {
            // 需要执行的代码
        }

实例:本例中的循环将继续运行,只要变量 i 小于 5:

var i = 1, x = '';
while (i < 5) {
    x = x + "The number is " + i + "<br>" + '\n';
    i++;
}
console.log(x)

do/while 循环

do/while 循环是 while 循环的变体。该循环会在检查条件是否为真之前执行一次代码块,然后如果条件为真的话,就会重复这个循环。

语法:
        do
        {
            // 需要执行的代码
        }
        while (条件);

实例:下面的例子使用 do/while 循环。该循环至少会执行一次,即使条件为 false 它也会执行一次,因为代码块会在条件被测试前执行:

var x = '';
do {
    x = x + "The number is " + i + "<br>";
    i++;
}
while (i < 5);

比较 for 和 while

while 循环与 for 循环很像。

使用 for 循环来显示 cars 数组中的所有值:

cars = ["BMW", "Volvo", "Saab", "Ford"];
var i = 0;
for (; cars[i];) {
    document.write(cars[i] + "<br>");
    i++;
}

使用 while 循环来显示 cars 数组中的所有值:

cars = ["BMW", "Volvo", "Saab", "Ford"];
var i = 0;
while (cars[i]) {
    document.write(cars[i] + "<br>");
    i++;
}

break 和 continue 语句

  • break 语句用于跳出循环。
  • continue 用于跳过循环中的一个迭代。

typeof, null, 和 undefined

typeof

typeof 操作符:你可以使用 typeof 操作符来检测变量的数据类型。

在JavaScript中,数组是一种特殊的对象类型。 因此 typeof [1,2,3,4] 返回 object。 

console.log(typeof "John")                // 返回 string
console.log(typeof 3.14)                  // 返回 number
console.log(typeof false)                 // 返回 boolean
console.log(typeof [1,2,3,4])             // 返回 object
console.log(typeof {name:'John', age:34}) // 返回 object

null

在 JavaScript 中 null 表示 "什么都没有"。

null 是一个只有一个值的特殊类型。表示一个空对象引用。用 typeof 检测 null 返回是object

可以设置为 null 来清空对象:

var person = null;           // 值为 null(空), 但类型为对象

undefined

在 JavaScript 中, undefined 是一个没有设置值的变量。typeof 一个没有值的变量会返回 undefined

var person;    // 值为 undefined(空), 类型是undefined

任何变量都可以通过设置值为 undefined 来清空。 类型为 undefined.

person = undefined;    // 值为 undefined, 类型是undefined

undefined 和 null 的区别

null 和 undefined 的值相等,但类型不等

typeof undefined             // undefined
typeof null                  // object
null === undefined           // false  (=== 表示绝对相等:"值"和"类型"都相等)
null == undefined            // true   (== 表示值相等)

1、定义

  •  (1)undefined:是所有没有赋值变量的默认值,自动赋值。
  •  (2)null:主动释放一个变量引用的对象,表示一个变量不再指向任何对象地址。

2、何时使用null?

当使用完一个比较大的对象时,需要对其进行释放内存时,设置为 null。

3、null 与 undefined 的异同点是什么呢?

共同点:都是原始类型,保存在栈中变量本地。

不同点:

  • (1)undefined : 表示变量声明过但并未赋过值。它是所有未赋值变量默认值,例如:var a; // a 自动被赋值为 undefined
  • (2)null :表示一个变量将来可能指向一个对象。一般用于主动释放指向对象的引用,例如:var emps = ['ss','nn']; emps = null; // 释放指向数组的引用

4、延伸 ----- 垃圾回收站

它是专门释放对象内存的一个程序。

  •  (1)在底层,后台伴随当前程序同时运行;引擎会定时自动调用垃圾回收期;
  •  (2)总有一个对象不再被任何变量引用时,才释放。

JavaScript 正则表达式

JS正则表达式语法大全(非常详细)http://c.biancheng/view/5632.html

JS正则表达式完整版:https://blog.csdn/h610443955/article/details/81079439

JavaScript 正则表达式:https://www.runoob/js/js-regexp.html

语法(其中修饰符是可选的):/正则表达式主体/修饰符(可选)

实例:var patt = /runoob/i

实例解析:

  • /runoob/i  是一个正则表达式。
  • runoob  是一个正则表达式主体 (用于检索)。
  • i  是一个修饰符 (搜索不区分大小写)。

字符串方法 search 和 replace

在 JavaScript 中,正则表达式通常用于两个字符串方法 : search() 和 replace()。

  • search() 方法 用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串,并返回子串的起始位置。
  • replace() 方法 用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。

search() 方法replace() 方法 使用正则表达式:

// 使用正则表达式搜索 "Runoob" 字符串,且不区分大小写:
var str = "Visit Runoob!";
var str_index = str.search(/Runoob/i);
console.log(str_index)

// 使用正则表达式且不区分大小写将字符串中的 Microsoft 替换为 Runoob :
// var str = document.getElementById("demo").innerHTML;
var str = 'this is a [microsoft] test'
var txt = str.replace(/microsoft/i, "Runoob");
console.log(txt)

使用字符串作为参数时,字符串参数会转换为正则表达式:

// 检索字符串中 "Runoob" 的子串:
var str = "Visit Runoob!";
var n = str.search("Runoob");

var str = document.getElementById("demo").innerHTML;
var txt = str.replace("Microsoft","Runoob");

正则表达式修饰符

修饰符 可以在全局搜索中不区分大小写:

修饰符描述
i执行对大小写不敏感的匹配。
g执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。
m执行多行匹配。

正则表达式模式

方括号用于查找某个范围内的字符:

表达式描述
[abc]查找方括号之间的任何字符。
[0-9]查找任何从 0 至 9 的数字。
(x|y)查找任何以 | 分隔的选项。

元字符是拥有特殊含义的字符:

元字符描述
\d查找数字。
\s查找空白字符。
\b匹配单词边界。
\uxxxx查找以十六进制数 xxxx 规定的 Unicode 字符。

量词:

量词描述
n+匹配任何包含至少一个 n 的字符串。
n*匹配任何包含零个或多个 n 的字符串。
n?匹配任何包含零个或一个 n 的字符串。

使用 RegExp 对象

在 JavaScript 中,RegExp 对象是一个预定义了属性和方法的正则表达式对象。

使用 test():

test() 方法是一个正则表达式方法。

test() 方法用于检测一个字符串是否匹配某个模式,如果字符串中含有匹配的文本,则返回 true,否则返回 false。

以下实例用于搜索字符串中的字符 "e"。实例:

var patt = /e/;
patt.test("The best things in life are free!"); // 返回 true

也可以不用设置正则表达式的变量,以上两行代码可以合并为一行:/e/.test("The best things in life are free!")

使用 exec()

exec() 方法是一个正则表达式方法。

exec() 方法用于检索字符串中的正则表达式的匹配。

该函数返回一个数组,其中存放匹配的结果。如果未找到匹配,则返回值为 null。

以下实例用于搜索字符串中的字母 "e":/e/.exec("The best things in life are free!");

示例:

var testStr = "now test001 test002";
var re = /test(\d+)/ig;
var r = "";
while(r = re.exec(testStr)) {
    console.log(r[0] + "  " + r[1]);
}

var tempArray = /<([^>]*?)>/.exec("<div>>测试div<span>>");
console.log("长度 : " + tempArray.length);
for (var index = 0; index < tempArray.length; index++) {
    console.log(index + " = " + tempArray[index]);
}

re = "<div>>测试div<span>>".match(/<([^>]*?)>/g)
console.log("长度 : " + tempArray.length);
for (var index = 0; index < tempArray.length; index++) {
    console.log(re[index])
}

更多实例

  • JS 判断输入字符串是否为数字、字母、下划线组成

  • JS 判断输入字符串是否全部为字母

  • JS 判断输入字符串是否全部为数字

完整的 RegExp 参考手册

完整的 RegExp 对象参考手册,请参考我们的 JavaScript RegExp 参考手册。

该参考手册包含了所有 RegExp 对象的方法和属性。

JavaScript 的错误捕获 --- throw、try 和 catch

  • try 语句测试代码块的错误。
  • catch 语句处理错误。
  • throw 语句创建自定义错误。
  • finally 语句在 try 和 catch 语句之后,无论是否有触发异常,该语句都会执行。

try 和 catch

try 语句允许我们定义在执行时进行错误测试的代码块。

catch 语句允许我们定义当 try 代码块发生错误时,所执行的代码块。

JavaScript 语句 try 和 catch 是成对出现的。

语法:
        try {
            //异常的抛出
        } catch(e) {
            //异常的捕获与处理
        } finally {
            //结束处理
        }

实例:在下面的例子中,我们故意在 try 块的代码中写了一个错字。catch 块会捕捉到 try 块中的错误,并执行代码来处理它。

var txt = "";

function message() {
    try {
        adddlert("Welcome guest!");
    } catch (err) {
        txt = "本页有一个错误。\n\n";
        txt += "错误描述:" + err.message + "\n\n";
        txt += "点击确定继续。\n\n";
        console.log(txt);
    }
}
message()

finally 语句

finally 语句不论之前的 try 和 catch 中是否产生异常都会执行该代码块。

function myFunction() {
    var message, x;
    message = document.getElementById("p01");
    message.innerHTML = "";
    x = document.getElementById("demo").value;
    try {
        if(x == "") throw "值是空的";
        if(isNaN(x)) throw "值不是一个数字";
        x = Number(x);
        if(x > 10) throw "太大";
        if(x < 5) throw "太小";
    }
    catch(err) {
        message.innerHTML = "错误: " + err + ".";
    }
    finally {
        document.getElementById("demo").value = "";
    }
}

Throw 语句

throw 语句允许我们创建自定义错误。

正确的技术术语是:创建或抛出异常(exception)。

如果把 throw 与 try 和 catch 一起使用,那么您能够控制程序流,并生成自定义的错误消息。

语法:throw exception

异常可以是 JavaScript 字符串、数字、逻辑值或对象。

实例:本例检测输入变量的值。如果值是错误的,会抛出一个异常(错误)。catch 会捕捉到这个错误,并显示一段自定义的错误消息:

function myFunction() {
    var message, x;
    message = document.getElementById("message");
    message.innerHTML = "";
    x = document.getElementById("demo").value;
    try {
        if(x == "")  throw "值为空";
        if(isNaN(x)) throw "不是数字";
        x = Number(x);
        if(x < 5)    throw "太小";
        if(x > 10)   throw "太大";
    }
    catch(err) {
        message.innerHTML = "错误: " + err;
    }
}

JavaScript 使用误区

赋值运算符应用错误

在 JavaScript 程序中如果你在 if 条件语句中使用赋值运算符的等号 (=) 将会产生一个错误结果, 正确的方法是使用比较运算符的两个等号 (==)。

if 条件语句返回 false (是我们预期的) 因为 x 不等于 10:

var x = 0;
if (x == 10){
    console.log("相等");
}else {
    console.log("不相等");
}

比较运算符常见错误

在常规的比较中,数据类型被忽略的,以下 if 条件语句返回 true:

var x = 10;
var y = "10";
if (x == y){
    console.log("相等");
}else {
    console.log("不相等");
}

在严格的比较运算中,=== 为恒等计算符,同时检查表达式的 类型,以下 if 条件语句返回 false:

var x = 10;
var y = "10";
if (y === x){
    console.log("相等");
}else {
    console.log("不相等");
}

这种错误经常会在 switch 语句中出现,switch 语句会使用恒等计算符(===)进行比较:

加法与连接注意事项

加法 是两个数字相加。

连接 是两个字符串 连接。

JavaScript 的加法和连接都使用 + 运算符。

接下来我们可以通过实例查看 两个数字相加 及 数字与字符串连接 的区别:

var x = 10 + 5;          // x 的结果为 15
var x = 10 + "5";        // x 的结果为 "105"

使用变量相加结果也不一致:

var x = 10;
var y = 5;
var z = x + y;           // z 的结果为 15

var x = 10;
var y = "5";
var z = x + y;           // z 的结果为 "105"

浮点型数据使用注意事项

JavaScript 中的所有数据都是以 64 位浮点型数据(float) 来存储。

所有的编程语言,包括 JavaScript,对浮点型数据的精确度都很难确定:

var x = 0.1;
var y = 0.2;
var z = x + y     // z 的结果为 0.30000000000000004
if (z == 0.3)     // 返回 false

为解决以上问题,可以用整数的乘除法来解决:

var z = (x * 10 + y * 10) / 10;     // z 的结果为 0.3

更多内容可以参考:JavaScript 中精度问题以及解决方案

JavaScript 字符串分行

JavaScript 允许我们在字符串中使用断行语句:

正确示例:

var x =
"Hello World!";

错误示例:在字符串中直接使用回车换行是会报错的:

var x = "Hello
World!";

正确示例:字符串断行需要使用反斜杠(\),如下所示:

var x = "Hello \
World!";

错误的使用分号

在 JavaScript 中,分号是可选的

由于 return 是一个完整的语句,所以 JavaScript 将关闭 return 语句。

以下实例中,if 语句失去方法体,原 if 语句的方法体作为独立的代码块被执行,导致错误的输出结果。

由于分号使用错误,if 语句中的代码块就一定会执行:

if (x == 19);
{
    // code block 
}

return 语句使用注意事项

JavaScript 默认是在代码的最后一行自动结束。

以下两个实例返回结果是一样的 ( 一个有分号,一个没有 ):

实例 1 :( 没有分号 )

function myFunction(a) {
    var power = 10 
    return a * power
}

实例 2 ( 有分号 )

function myFunction(a) {
    var power = 10;
    return a * power;
}

JavaScript 也可以使用多行来结束一个语句。以下实例返回相同的结果。示例 3

function myFunction(a) {
    var
    power = 10; 
    return a * power;
}

数组中使用名字来索引 ( 错误用法 )

  • 许多程序语言都允许使用名字来作为数组的索引。使用名字来作为索引的数组称为 关联数组 (或 哈希 )。
  • JavaScript 不支持使用名字来索引数组,只允许使用数字索引。

实例

var person = [];
person[0] = "John";
person[1] = "Doe";
person[2] = 46;
var x = person.length;         // person.length 返回 3
var y = person[0];             // person[0] 返回 "John"

在 JavaScript 中, 对象 使用 名字作为索引

当访问数组时,如果使用名字作为索引,JavaScript 会把数组重新定义为标准对象。执行这样操作后,数组的方法及属性将不能再使用,否则会产生错误:

var person = [];
person["firstName"] = "John";
person["lastName"] = "Doe";
person["age"] = 46;
var x = person.length;         // person.length 返回 0
var y = person[0];             // person[0] 返回 undefined

定义数组元素,最后不能添加逗号

数组最后一个值的后面添加逗号虽然语法没有问题,但是在不同的浏览器可能得到不同的结果。

  • 错误的方式:var colors = [5, 6, 7,];    // 这样数组的长度可能为3 也可能为4。
  • 正确的定义方式:points = [40, 100, 1, 5, 25, 10];

定义对象,最后不能添加逗号

  • 错误的定义方式:websites = {site:"菜鸟教程", url:"www.runoob", like:460,}
  • 正确的定义方式:websites = {site:"菜鸟教程", url:"www.runoob", like:460}

Undefined 不是 Null

在 JavaScript 中, 

  • null 用于 对象
  • undefined 用于变量,属性和方法。

对象只有被定义才有可能为 null,否则为 undefined。

如果我们想测试对象是否存在,在对象还没定义时将会抛出一个错误。

  • 错误的使用方式:if (myObj !== null && typeof myObj !== "undefined")
  • 正确的方式是先使用 typeof 来检测对象是否已定义:if (typeof myObj !== "undefined" && myObj !== null)

程序块作用域

在每个代码块中 JavaScript 不会创建一个新的作用域,一般各个代码块的作用域都是全局的。

以下代码的的变量 i 返回 10,而不是 undefined:

for (var i = 0; i < 10; i++) {
    // some code
}
return i;

JavaScript 表单

JavaScript 表单验证

HTML 表单验证可以通过 JavaScript 来完成。

以下实例代码用于判断表单字段(fname)值是否存在, 如果不存在,就弹出信息,阻止表单提交:

JavaScript 实例

function validateForm() {
    var x = document.forms["myForm"]["fname"].value;
    if (x == null || x == "") {
        alert("需要输入名字。");
        return false;
    }
}

以上 JavaScript 代码可以通过 HTML 代码来调用。

HTML 表单实例。示例:尝试一下 

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script>
function validateForm() {
    var x = document.forms["myForm"]["fname"].value;
    if (x == null || x == "") {
        alert("需要输入名字。");
        return false;
    }
}
</script>
</head>
<body>

<form name="myForm" action="demo_form.php"
onsubmit="return validateForm()" method="post">
名字: <input type="text" name="fname">
<input type="submit" value="提交">
</form>

</body>
</html>

HTML 表单自动验证

HTML 表单验证也可以通过浏览器来自动完成。

如果表单字段 (fname) 的值为空, required 属性会阻止表单提交:

实例

<form action="demo_form.php" method="post">
  <input type="text" name="fname" required="required">
  <input type="submit" value="提交">
</form>

示例:尝试一下

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>

<form action="demo_form.php" method="post">
  <input type="text" name="fname" required="required">
  <input type="submit" value="提交">
</form>

<p>点击提交按钮,如果输入框是空的,浏览器会提示错误信息。</p>

</body>
</html>

数据验证

数据验证用于确保用户输入的数据是有效的。

典型的数据验证有:

  • 必需字段是否有输入?
  • 用户是否输入了合法的数据?
  • 在数字字段是否输入了文本?

大多数情况下,数据验证用于确保用户正确输入数据。

数据验证可以使用不同方法来定义,并通过多种方式来调用。

服务端数据验证是在数据提交到服务器上后再验证。

客户端数据验证是在数据发送到服务器前,在浏览器上完成验证。

HTML 约束验证

HTML5 新增了 HTML 表单的验证方式:约束验证(constraint validation)。

约束验证是表单被提交时浏览器用来实现验证的一种算法。

HTML 约束验证基于:

  • HTML 输入属性
  • CSS 伪类选择器
  • DOM 属性和方法

约束验证 HTML 输入属性

属性描述
disabled规定输入的元素不可用
max规定输入元素的最大值
min规定输入元素的最小值
pattern规定输入元素值的模式
required规定输入元素字段是必需的
type 规定输入元素的类型

完整列表,请查看 HTML 输入属性。

约束验证 CSS 伪类选择器

选择器描述
:disabled选取属性为 "disabled" 属性的 input 元素
:invalid选取无效的 input 元素
:optional选择没有"required"属性的 input 元素
:required选择有"required"属性的 input 元素
:valid选取有效值的 input 元素

完整列表,请查看 CSS 伪类。

JavaScript 表单验证

JavaScript 表单验证

JavaScript 可用来在数据被送往服务器前对 HTML 表单中的这些输入数据进行验证。

表单数据经常需要使用 JavaScript 来验证其正确性:

  • 验证表单数据是否为空?
  • 验证输入是否是一个正确的email地址?
  • 验证日期是否输入正确?
  • 验证表单输入内容是否为数字型?

必填(或必选)项目

下面的函数用来检查用户是否已填写表单中的必填(或必选)项目。假如必填或必选项为空,那么警告框会弹出,并且函数的返回值为 false,否则函数的返回值则为 true(意味着数据没有问题):

function validateForm()
{
    var x=document.forms["myForm"]["fname"].value;
    if (x==null || x=="")
    {
        alert("姓必须填写");
        return false;
    }
}

以上函数在 form 表单提交时被调用:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob)</title>
</head>
<head>
<script>
function validateForm(){
var x=document.forms["myForm"]["fname"].value;
if (x==null || x==""){
  alert("姓必须填写");
  return false;
  }
}
</script>
</head>
<body>
	
<form name="myForm" action="demo-form.php" onsubmit="return validateForm()" method="post">
姓: <input type="text" name="fname">
<input type="submit" value="提交">
</form>
	
</body>
</html>

实例:尝试一下 »

E-mail 验证

下面的函数检查输入的数据是否符合电子邮件地址的基本语法。

意思就是说,输入的数据必须包含 @ 符号和点号(.)。同时,@ 不可以是邮件地址的首字符,并且 @ 之后需有至少一个点号:

function validateForm(){
    var x=document.forms["myForm"]["email"].value;
    var atpos=x.indexOf("@");
    var dotpos=x.lastIndexOf(".");
    if (atpos<1 || dotpos<atpos+2 || dotpos+2>=x.length){
        alert("不是一个有效的 e-mail 地址");
        return false;
    }
}

下面是连同 HTML 表单的完整代码:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob)</title>
</head>
<head>
<script>
function validateForm(){
	var x=document.forms["myForm"]["email"].value;
	var atpos=x.indexOf("@");
	var dotpos=x.lastIndexOf(".");
	if (atpos<1 || dotpos<atpos+2 || dotpos+2>=x.length){
		alert("不是一个有效的 e-mail 地址");
  		return false;
	}
}
</script>
</head>
<body>
	
<form name="myForm" action="demo-form.php" onsubmit="return validateForm();" method="post">
Email: <input type="text" name="email">
<input type="submit" value="提交">
</form>
	
</body>
</html>

示例::尝试一下

JavaScript 验证 API

  • 约束验证 DOM 方法
  • 约束验证 DOM 属性
  • Validity 属性

:https://www.runoob/js/js-validation-api.html

JavaScript 保留关键字

在 JavaScript 中,一些标识符是保留关键字,不能用作变量名或函数名。

:https://www.runoob/js/js-reserved.html

this 关键字

面向对象语言中 this 表示当前对象的一个引用。

但在 JavaScript 中 this 不是固定不变的,它会随着执行环境的改变而改变。

  • 在方法中,this 表示该方法所属的对象。
  • 如果单独使用,this 表示全局对象。
  • 在函数中,this 表示全局对象。
  • 在函数中,在严格模式下,this 是未定义的(undefined)。
  • 在事件中,this 表示接收事件的元素。
  • 类似 call() 和 apply() 方法可以将 this 引用到任何对象。

示例:

var person = {
    firstName: "John",
    lastName : "Doe",
    id       : 5566,
    fullName : function() {
        return this.firstName + " " + this.lastName;
    }
};

console.log(person.fullName());

方法中的 this

在对象方法中, this 指向调用它所在方法的对象。

在上面一个实例中,this 表示 person 对象。fullName 方法所属的对象就是 person。

单独使用 this

单独使用 this,则它指向 全局(Global)对象

在浏览器中,window 就是该全局对象为 [object Window]:

示例:

var x = this;

严格模式下,如果单独使用,this 也是指向全局(Global)对象。示例:

"use strict";
var x = this;

事件中的 this

在 HTML 事件句柄中,this 指向了接收事件的 HTML 元素:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob)</title>
</head>
<body>

<h2>JavaScript <b>this</b> 关键字</h2>

<button onclick="this.style.display='none'">点我后我就消失了</button>

</body>
</html>

对象方法中绑定

下面实例中,this 是 person 对象,person 对象是函数的所有者。示例:

var person = {
    firstName  : "John",
    lastName   : "Doe",
    id         : 5566,
    myFunction : function() {
        return this;
    }
};

示例:( 说明: this.firstName 表示 this (person) 对象的 firstName 属性。 )

var person = {
  firstName: "John",
  lastName : "Doe",
  id       : 5566,
  fullName : function() {
    return this.firstName + " " + this.lastName;
  }
};

显式函数绑定

在 JavaScript 中函数也是对象,对象则有方法,apply 和 call 就是函数对象的方法。这两个方法异常强大,他们允许切换函数执行的上下文环境(context),即 this 绑定的对象。

在下面实例中,当我们使用 person2 作为参数来调用 person1.fullName 方法时, this 将指向 person2, 即便它是 person1 的方法:

var person1 = {
    fullName: function() {
        return this.firstName + " " + this.lastName;
    }
}
var person2 = {
    firstName:"John",
    lastName: "Doe",
}
person1.fullName.call(person2);  // 返回 "John Doe"

JSON

JSON 是用于存储和传输数据的格式。JSON 通常用于服务端向网页传递数据 。

什么是 JSON?

  • JSON 英文全称 JavaScript Object Notation
  • JSON 是一种轻量级的数据交换格式。
  • JSON是独立的语言 *
  • JSON 易于理解。

JSON 使用 JavaScript 语法,但是 JSON 格式仅仅是一个文本。文本可以被任何编程语言读取及作为数据格式传递。

JSON 实例

以下 JSON 语法定义了 sites 对象: 3 条网站信息(对象)的数组:

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

JSON 格式化后为 JavaScript 对象

JSON 格式在语法上与创建 JavaScript 对象代码是相同的。

由于它们很相似,所以 JavaScript 程序可以很容易的将 JSON 数据转换为 JavaScript 对象。

JSON 语法规则

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

JSON 数据 - 一个名称对应一个值

JSON 数据格式为 键/值 对,就像 JavaScript 对象属性。

键/值对包括字段名称(在双引号中),后面一个冒号,然后是值:"name":"Runoob"

JSON 对象

JSON 对象保存在大括号内。

就像在 JavaScript 中, 对象可以保存多个 键/值 对:{"name":"Runoob", "url":"www.runoob"}

JSON 数组

JSON 数组保存在中括号内。

就像在 JavaScript 中, 数组可以包含对象:

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

在以上实例中,对象 "sites" 是一个数组,包含了三个对象。每个对象为站点的信息(网站名和网站地址)。

JSON 字符串转换为 JavaScript 对象

通常我们从服务器中读取 JSON 数据,并在网页中显示数据。

简单起见,我们网页中直接设置 JSON 字符串 (你还可以阅读我们的 JSON 教程):

首先,创建 JavaScript 字符串,字符串为 JSON 格式的数据:

var text = '{ "sites" : [' +
'{ "name":"Runoob" , "url":"www.runoob" },' +
'{ "name":"Google" , "url":"www.google" },' +
'{ "name":"Taobao" , "url":"www.taobao" } ]}';

然后,使用 JavaScript 内置函数 JSON.parse() 将字符串转换为 JavaScript 对象:var obj = JSON.parse(text);

最后,在你的页面中使用新的 JavaScript 对象:

var text = '{ "sites" : [' +
    '{ "name":"Runoob" , "url":"www.runoob" },' +
    '{ "name":"Google" , "url":"www.google" },' +
    '{ "name":"Taobao" , "url":"www.taobao" } ]}';
    
obj = JSON.parse(text);
document.getElementById("demo").innerHTML = obj.sites[1].name + " " + obj.sites[1].url;

相关函数

函数描述
JSON.parse()用于将一个 JSON 字符串转换为 JavaScript 对象。
JSON.stringify()用于将 JavaScript 值转换为 JSON 字符串。

更多 JSON 信息,你可以阅读我们的 JSON 教程。

javascript:void(0) 含义

void() 仅仅是代表不返回任何值但是括号内的表达式还是要运行,如:void(alert("Warnning!"))

我们经常会使用到 javascript:void(0) 这样的代码,那么在 JavaScript 中 javascript:void(0) 代表的是什么意思呢?

javascript:void(0) 中最关键的是 void 关键字, void 是 JavaScript 中非常重要的关键字,该操作符指定要计算一个表达式但是不返回值。

语法格式如下:

void func()
javascript:void func()

或者

void(func())
javascript:void(func())

示例:(  尝试一下 )下面的代码创建了一个超级链接,当用户点击以后不会发生任何事。

<!DOCTYPE html> 
<html>
<head> 
<meta charset="utf-8"> 
<title>菜鸟教程(runoob)</title> 
</head> 
<body>
	
    <a href="javascript:void(0)">单此处什么也不会发生</a>
	
</body>
</html>

当用户链接时,void(0) 计算为 0,但 Javascript 上没有任何效果。以下实例中,在用户点击链接后显示警告信息:( 尝试一下 )

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob)</title>
</head>
<body>
	
<p>点击以下链接查看结果:</p>
<a href="javascript:void(alert('Warning!!!'))">点我!</a>
	
</body>
</html>

以下实例中参数 a 将返回 undefined :

function getValue(){
   var a,b,c;
   a = void ( b = 5, c = 7 );
   document.write('a = ' + a + ' b = ' + b +' c = ' + c );
}

href="#"与href="javascript:void(0)"的区别

  • # 包含了一个位置信息,默认的锚是#top 也就是网页的上端。在页面很长的时候会使用 # 来定位页面的具体位置,格式为:# + id
  • 而 javascript:void(0), 仅仅表示一个死链接。如果你要定义一个死链接请使用 javascript:void(0) 。

示例:( 尝试一下 )

<a href="javascript:void(0);">点我没有反应的!</a>
<a href="#pos">点我定位到指定位置!</a>
<br>
...
<br>
<p id="pos">尾部定位点</p>

JavaScript 异步编程

异步的概念

异步(Asynchronous, async)是与同步(Synchronous, sync)相对的概念。

  • 传统单线程编程中,程序的运行是同步的(同步不意味着所有步骤同时运行,而是指步骤在一个控制流序列中按顺序执行)。
  • 而异步的概念则是不保证同步的概念,也就是说,一个异步过程的执行将不再与原有的序列有顺序关系。

简单来理解就是:同步按你的代码顺序执行,异步不按照代码顺序执行,异步的执行效率更高。

以上是关于异步的概念的解释,接下来我们通俗地解释一下异步:异步就是从主线程发射一个子线程来完成任务。

什么时候用异步编程

在前端编程中(甚至后端有时也是这样),我们在处理一些简短、快速的操作时,例如计算 1 + 1 的结果,往往在主线程中就可以完成。主线程作为一个线程,不能够同时接受多方面的请求。所以,当一个事件没有结束时,界面将无法处理其他请求。

现在有一个按钮,如果我们设置它的 onclick 事件为一个死循环,那么当这个按钮按下,整个网页将失去响应。

为了避免这种情况的发生,我们常常用子线程来完成一些可能消耗时间足够长以至于被用户察觉的事情,比如读取一个大文件或者发出一个网络请求。因为子线程独立于主线程,所以即使出现阻塞也不会影响主线程的运行。但是子线程有一个局限:一旦发射了以后就会与主线程失去同步,我们无法确定它的结束,如果结束之后需要处理一些事情,比如处理来自服务器的信息,我们是无法将它合并到主线程中去的。

为了解决这个问题,JavaScript 中的异步操作函数往往通过回调函数来实现异步任务的结果处理。

回调函数

回调函数就是一个函数,它是在我们启动一个异步任务的时候就告诉它:等你完成了这个任务之后要干什么。这样一来主线程几乎不用关心异步任务的状态了,他自己会善始善终。

示例:

function print() {
    console.log('test callback')
}
setTimeout(print, 3000);
console.log('main thread continue')

这段程序中的 setTimeout 就是一个消耗时间较长(3 秒)的过程,它的第一个参数是个回调函数,第二个参数是毫秒数,这个函数执行之后会产生一个子线程,子线程会等待 3 秒,然后执行回调函数 "print",在命令行输出 "Time out"。当然,JavaScript 语法十分友好,我们不必单独定义一个函数 print ,我们常常将上面的程序写成:

注意: setTimeout 会在子线程中等待 3 秒,在 setTimeout 函数执行之后主线程并没有停止。。。

function print() {
    
}
setTimeout(print, 3000);
console.log('main thread continue')

setTimeout(function () {
    console.log('test callback');
}, 3000);

异步 AJAX

除了 setTimeout 函数以外,异步回调广泛应用于 AJAX 编程。有关于 AJAX 详细请参见:https://www.runoob/ajax/ajax-tutorial.html

XMLHttpRequest 常常用于请求来自远程服务器上的 XML 或 JSON 数据。一个标准的 XMLHttpRequest 对象往往包含多个回调:

示例:( 尝试一下 )

<!DOCTYPE html>
<html>
<head> 
<meta charset="utf-8"> 
<title>菜鸟教程(runoob)</title> 
</head>
<body>

<p><strong>以下内容是通过异步请求获取的:</strong></p>
<p id="demo"></p>
<script>
var xhr = new XMLHttpRequest();
 
xhr.onload = function () {
    // 输出接收到的文字数据
    document.getElementById("demo").innerHTML=xhr.responseText;
}
 
xhr.onerror = function () {
    document.getElementById("demo").innerHTML="请求出错";
}
 
// 发送异步 GET 请求
xhr.open("GET", "https://www.runoob/try/ajax/ajax_info.txt", true);
xhr.send();
</script>

</body>
</html>

XMLHttpRequest 的 onload 和 onerror 属性都是函数,分别在它请求成功和请求失败时被调用。

如果你使用完整的 jQuery 库,也可以更加优雅的使用异步 AJAX:

示例:( 尝试一下 )

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob)</title>
<script src="https://cdn.staticfile/jquery/1.10.2/jquery.min.js">
</script>
<script>
$(document).ready(function(){
	$("button").click(function(){
		$.get("/try/ajax/demo_test.php",function(data,status){
			alert("数据: " + data + "\n状态: " + status);
		});
	});
});
</script>
</head>
<body>

<button>发送一个 HTTP GET 请求并获取返回结果</button>

</body>
</html>

JavaScript 的 Promise

在学习本章节内容前,你需要先了解什么是异步编程,可以参考:JavaScript 异步编程

Promise 是一个 ECMAScript 6 提供的类,目的是更加优雅地书写复杂的异步任务。

由于 Promise 是 ES6 新增加的,所以一些旧的浏览器并不支持,苹果的 Safari 10 和 Windows 的 Edge 14 版本以上浏览器才开始支持 ES6 特性。

构造 Promise

现在我们新建一个 Promise 对象:

new Promise(function (resolve, reject) {
    // 要做的事情...
});

通过新建一个 Promise 对象好像并没有看出它怎样 "更加优雅地书写复杂的异步任务"。我们之前遇到的异步任务都是一次异步,如果需要多次调用异步函数呢?例如,如果我想分三次输出字符串,第一次间隔 1 秒,第二次间隔 4 秒,第三次间隔 3 秒。示例:

setTimeout(function () {
    console.log("First");
    setTimeout(function () {
        console.log("Second");
        setTimeout(function () {
            console.log("Third");
        }, 3000);
    }, 4000);
}, 1000);

这段程序实现了这个功能,但是它是用 "函数瀑布" 来实现的。可想而知,在一个复杂的程序当中,用 "函数瀑布" 实现的程序无论是维护还是异常处理都是一件特别繁琐的事情,而且会让缩进格式变得非常冗赘。

现在我们用 Promise 来实现同样的功能。示例:

new Promise(function (resolve, reject) {
    setTimeout(function () {
        console.log("First");
        resolve();
    }, 1000);
}).then(function () {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            console.log("Second");
            resolve();
        }, 4000);
    });
}).then(function () {
    setTimeout(function () {
        console.log("Third");
    }, 3000);
});

这段代码较长,所以还不需要完全理解它,我想引起注意的是 Promise 将嵌套格式的代码变成了顺序格式的代码。

Promise 的使用

下面我们通过剖析这段 Promise "计时器" 代码来讲述 Promise 的使用:

Promise 构造函数只有一个参数,是一个函数,这个函数在构造之后会直接被异步运行,所以我们称之为起始函数。起始函数包含两个参数 resolve 和 reject。

当 Promise 被构造时,起始函数会被异步执行。示例:

new Promise(function (resolve, reject) {
    console.log("Run");
});

这段程序会直接输出 Run

resolve 和 reject 都是函数,其中调用 resolve 代表一切正常,reject 是出现异常时所调用的。示例:

new Promise(function (resolve, reject) {
    var a = 0;
    var b = 1;
    if (b == 0) reject("Diveide zero");
    else resolve(a / b);
}).then(function (value) {
    console.log("a / b = " + value);
}).catch(function (err) {
    console.log(err);
}).finally(function () {
    console.log("End");
});

这段程序执行结果是:

a / b = 0
End

Promise 类有 .then() .catch() 和 .finally() 三个方法,这三个方法的参数都是一个函数,.then() 可以将参数中的函数添加到当前 Promise 的正常执行序列,.catch() 则是设定 Promise 的异常处理序列,.finally() 是在 Promise 执行的最后一定会执行的序列。 .then() 传入的函数会按顺序依次执行,有任何异常都会直接跳到 catch 序列:

示例:

new Promise(function (resolve, reject) {
    console.log(1111);
    resolve(2222);
}).then(function (value) {
    console.log(value);
    return 3333;
}).then(function (value) {
    console.log(value);
    throw "An error";
}).catch(function (err) {
    console.log(err);
});

执行结果:

1111
2222
3333
An error

resolve() 中可以放置一个参数用于向下一个 then 传递一个值,then 中的函数也可以返回一个值传递给 then。但是,如果 then 中返回的是一个 Promise 对象,那么下一个 then 将相当于对这个返回的 Promise 进行操作,这一点从刚才的计时器的例子中可以看出来。

reject() 参数中一般会传递一个异常给之后的 catch 函数用于处理异常。

但是请注意以下两点:

  • resolve 和 reject 的作用域只有起始函数,不包括 then 以及其他序列;
  • resolve 和 reject 并不能够使起始函数停止运行,别忘了 return。

Promise 函数

上述的 "计时器" 程序看上去比函数瀑布还要长,所以我们可以将它的核心部分写成一个 Promise 函数:

function print(delay, message) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            console.log(message);
            resolve();
        }, delay);
    });
}

然后我们就可以放心大胆的实现程序功能了:

print(1000, "First").then(function () {
    return print(4000, "Second");
}).then(function () {
    print(3000, "Third");
});

这种返回值为一个 Promise 对象的函数称作 Promise 函数,它常常用于开发基于异步操作的库。

回答常见的问题(FAQ)

Q: then、catch 和 finally 序列能否顺序颠倒?

A: 可以,效果完全一样。但不建议这样做,最好按 then-catch-finally 的顺序编写程序。

Q: 除了 then 块以外,其它两种块能否多次使用?

A: 可以,finally 与 then 一样会按顺序执行,但是 catch 块只会执行第一个,除非 catch 块里有异常。所以最好只安排一个 catch 和 finally 块。

Q: then 块如何中断?

A: then 块默认会向下顺序执行,return 是不能中断的,可以通过 throw 来跳转至 catch 实现中断。

Q: 什么时候适合用 Promise 而不是传统回调函数?

A: 当需要多次顺序执行异步操作的时候,例如,如果想通过异步方法先后检测用户名和密码,需要先异步检测用户名,然后再异步检测密码的情况下就很适合 Promise。

Q: Promise 是一种将异步转换为同步的方法吗?

A: 完全不是。Promise 只不过是一种更良好的编程风格。

Q: 什么时候我们需要再写一个 then 而不是在当前的 then 接着编程?

A: 当你又需要调用一个异步任务的时候。

异步函数

异步函数(async function)是 ECMAScript 2017 (ECMA-262) 标准的规范,几乎被所有浏览器所支持,除了 Internet Explorer。

在 Promise 中我们编写过一个 Promise 函数:

function print(delay, message) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            console.log(message);
            resolve();
        }, delay);
    });
}

然后用不同的时间间隔输出了三行文本:

print(1000, "First").then(function () {
    return print(4000, "Second");
}).then(function () {
    print(3000, "Third");
});

我们可以将这段代码变得更好看:

async function asyncFunc() {
    await print(1000, "First");
    await print(4000, "Second");
    await print(3000, "Third");
}
asyncFunc();

哈!这岂不是将异步操作变得像同步操作一样容易了吗!

这次的回答是肯定的,异步函数 async function 中可以使用 await 指令,await 指令后必须跟着一个 Promise,异步函数会在这个 Promise 运行中暂停,直到其运行结束再继续运行。

异步函数实际上原理与 Promise 原生 API 的机制是一模一样的,只不过更便于程序员阅读。

处理异常的机制将用 try-catch 块实现:

async function asyncFunc() {
    try {
        await new Promise(function (resolve, reject) {
            throw "Some error"; // 或者 reject("Some error")
        });
    } catch (err) {
        console.log(err);
        // 会输出 Some error
    }
}
asyncFunc();

如果 Promise 有一个正常的返回值,await 语句也会返回它:

async function asyncFunc() {
    let value = await new Promise(
        function (resolve, reject) {
            resolve("Return value");
        }
    );
    console.log(value);
}
asyncFunc();

程序会输出:Return value

更多内容:JavaScript Promise 对象

更多推荐

菜鸟教程 之 JavaScript 教程