JavaScript基础知识总结

1.前提

Html是一种标记语言,用来结构化我们的网页内容并赋予内容含义,例如定义段落、标题和数据表,或在页面中嵌入图片和视频

Css是一种样式规则语言,可将样式应用于 HTML 内容,例如设置背景颜色和字体,在多个列中布局内容

JavaScript 是一种脚本语言,可以用来创建动态更新的内容,控制多媒体,制作图像动画,还有很多

2.简介

JavaScript 是一种轻量级的编程语言。

JavaScript 是可插入 HTML 页面的编程代码

JavaScript 插入 HTML 页面后,可由所有的现代浏览器执行

3.JavaScript 用法

HTML 中的 Javascript 脚本代码必须位于 <script> 与 </script> 标签之间

Javascript 脚本代码可被放置在 HTML 页面的 <body> 和 <head> 部分中

3.1<script><script/> 标签

如需在 HTML 页面中插入 JavaScript,请使用 <script> 标签

<script> 和 </script> 会告诉 JavaScript 在何处开始和结束

<script> 和 </script> 之间的代码行包含了 JavaScript

<script>
        alert("我的第一个 JavaScript");
</script>

您无需理解上面的代码。只需明白,浏览器会解释并执行位于 <script> 和 </script>之间的 JavaScript 代码

那些老旧的实例可能会在 <script> 标签中使用 type="text/javascript"。现在已经不必这样做了。JavaScript 是所有现代浏览器以及 HTML5 中的默认脚本语言

3.2 <body> 中的 JavaScript

JavaScript 会在页面加载时向 HTML 的 <body> 写文本

<!DOCTYPE html>
<html>
<body>
.
.
<script>
document.write("<h1>这是一个标题</h1>");
document.write("<p>这是一个段落</p>");
</script>
.
.
</body>
</html>

3.3<head> 中的 JavaScript 函数

该函数会在点击按钮时被调用

<!DOCTYPE html>
<html>
<head>
<script>
   function myFunction()
     {
              document.getElementById("demo").innerHTML="我的第一个 JavaScript 函数";
          }
</script>
</head>
<body>
       <h1>我的 Web 页面</h1>
       <p id="demo">一个段落</p>
       <button type="button" οnclick="myFunction()">尝试一下</button>
</body>
</html>

3.4外部的 JavaScript

可以把脚本保存到外部文件中。外部文件通常包含被多个网页使用的代码

如需使用外部文件,请在 <script> 标签的 "src" 属性中设置该 .js 文件:

<!DOCTYPE html>
<html>
<body>
        <script src="myScript.js"></script>
</body>
</html>

myScript.js 文件代码如下

function myFunction()
{
    document.getElementById("demo").innerHTML="我的第一个 JavaScript 函数";
}

 

4.Chrome 浏览器中执行 JavaScript

如何在 Chrome 浏览器上进行 JavaScript 代码的运行与调试

我们在 Chrome 浏览器中可以通过按下 F12 按钮或者右击页面,选择"检查"来开启开发者工具

也可以在右上角菜单栏选择 "更多工具"=》"开发者工具" 来开启

4.1Console 窗口调试 JavaScript 代码

打开开发者工具后,我们可以在 Console 窗口调试 JavaScript代码,如下图

038a2bb992d544f7940eadb36f391866.png

上图中我们在 > 符号后输入我们要执行的代码 console.log("runoob"),按回车后执行。

我们也可以在其他地方复制一段代码过来执行,比如复制以下代码到 Console 窗口,按回车执行

4.2清空 Console 窗口到内容可以按以下按钮

09766048d1d54a588ff76f9816fa14cc.png

4.3Chrome snippets 小脚本

我们也可以在 Chrome 浏览器中创建一个脚本来执行,在开发者工具中点击 Sources 面板,选择 Snippets 选项卡,在导航器中右击鼠标,然后选择 Create new snippet 来新建一个脚本文件

bab35e9169994fa193eb45f7a1fa1aa0.png

如果你没看到 Snippets ,可以点下面板上到 >> 就能看到了

点击 Create new snippet 后,会自动创建一个文件,你只需在右侧窗口输入以下代码,然后按 Command+S(Mac)或 Ctrl+S(Windows 和 Linux)保存更改即可

713a5cbdb8b945349f16329d459f55ab.png

5.JavaScript 输出

JavaScript 可以通过不同的方式来输出数据:

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

<p id="demo">我的第一个段落</p>

<script>
document.getElementById("demo").innerHTML = "段落已修改。";
</script>

<script> document.write(Date());</script>

<button οnclick="myFunction()">点我</button>

<script>
function myFunction() {
    document.write(Date());
}
</script>

<script>
a = 5;
b = 6;
c = a + b;
console.log(c);
</script>

6.JavaScript 语法

JavaScript 代码

document.getElementById("demo").innerHTML="你好 Dolly";

JavaScript 代码块

function myFunction()
{
    document.getElementById("demo").innerHTML="你好Dolly";
}

JavaScript 语句标识符

9e04a07b512849a1b943fc82d96b5241.png

空格

JavaScript 会忽略多余的空格 您可以向脚本添加空格,来提高其可读性

7.JavaScript 注释

单行注释以 // 开头

多行注释以 /* 开始,以 */ 结尾

8.JavaScript 变量

  • 变量必须以字母开头
  • 变量也能以 $ 和 _ 符号开头(不过我们不推荐这么做)
  • 变量名称对大小写敏感(y 和 Y 是不同的变量)

声明(创建) 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

9.JavaScript 数据类型

值类型(基本类型)

字符串(String)、数字(Number)、布尔(Boolean)、空(Null)、未定义(Undefined)、Symbol

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

引用数据类型(对象类型)

对象(Object)、数组(Array)、函数(Function),两个特殊的对象:正则(RegExp)和日期(Date)

JavaScript 拥有动态类型

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

var x;               // x 为 undefinedvar

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

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

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

JavaScript 数字

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

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

JavaScript 布尔

布尔(逻辑)只能有两个值:true 或 false

var x=true;
var y=false;

JavaScript 数组

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

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

var cars=new Array("Saab","Volvo","BMW");

 

var cars=["Saab","Volvo","BMW"];

JavaScript 对象

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

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

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

undefined 和 Null

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

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

cars=null;
person=null;

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

// name=person.lastname;

// name=person["lastname"];

 

var cars=["Saab","Volvo","BMW"];

cars=null;

person=null;

console.info(cars)

声明变量类型

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

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

对象方法

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

JavaScript 函数

函数是由事件驱动的或者当它被调用时执行的可重复使用的代码块

<script> function myFunction() {

        alert("Hello World!");

} </script>  

 

<button οnclick="myFunction()">点我</button>

JavaScript 函数语法

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

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

调用带参数的函数

在调用函数时,您可以向其传递值,这些值被称为参数。

这些参数可以在函数中使用。

您可以发送任意多的参数,由逗号 (,) 分隔:

<p>点击这个按钮,来调用带参数的函数。</p>

<button οnclick="myFunction('Harry Potter','Wizard')">点击这里</button>

<script>

function myFunction(name,job){

        alert("Welcome " + name + ", the " + job);

}

</script>

带有返回值的函数

有时,我们会希望函数将值返回调用它的地方。

通过使用 return 语句就可以实现。

在使用 return 语句时,函数会停止执行,并返回指定的值

function myFunction()
{
    var x=5;
    return x;
}

var myVar=myFunction();

function myFunction(a,b) { return a*b; } document.getElementById("demo").innerHTML=myFunction(4,3);

10.JavaScript 作用域

11.JavaScript 事件

HTML 事件是发生在 HTML 元素上的事情

当在 HTML 页面中使用 JavaScript 时, JavaScript 可以触发这些事件

HTML 事件可以是浏览器行为,也可以是用户行为。

以下是 HTML 事件的实例:

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

通常,当事件发生时,你可以做些事情。

在事件触发时 JavaScript 可以执行一些代码。

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

 

在以下实例中,按钮元素中添加了 onclick 属性 (并加上代码)

<button οnclick="getElementById('demo').innerHTML=Date()">现在的时间是?</button>

或者

<p>点击按钮执行 <em>displayDate()</em> 函数.</p>
<button οnclick="displayDate()">点这里</button>
<script>
        function displayDate(){
               document.getElementById("demo").innerHTML=Date();
        }
</script>
        <p id="demo"></p>

</body>

其它事件

bc43403aa96f4d02849c1fdfbe7e7366.png

12.JavaScript 字符串

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

var carname = "Volvo XC60";

var character = carname[7];

const name = "RUNOOB";
let letter = name[2];

document.getElementById("demo").innerHTML = letter;

字符串长度

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

var txt = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

var sln = txt.length;

13.JavaScript 运算符

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

比较运算符

8b083f780d9741c78c260495d839bf52.png

逻辑运算符

5d0c417af15c415496b497d4c31e02ae.png

 

14.JavaScript if...Else 语句

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

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

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

15.JavaScript switch 语句

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

16.JavaScript for 循环

for 循环

for (var i=0; i<5; i++)
{
      x=x + "该数字为 " 
}

For/In 循环

JavaScript for/in 语句循环遍历对象的属性

var person={fname:"Bill",lname:"Gates",age:56}; 
 
for (x in person)  // x 为属性名
{
    txt=txt + person[x];
}

while 循环

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

17.JavaScript break 和 continue 语句

break 语句用于跳出循环

continue 用于跳过循环中的一个迭代

for (i=0;i<10;i++){
    if (i==3){
        break;
    }
    x=x + "The number is " + i + "<br>";
}

for (i=0;i<=10;i++){

        if (i==3) continue;

               console.info(i)

}

18.JavaScript typeof, null, 和 undefined

typeof 操作符来检测变量的数据类型

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

null

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

null是一个只有一个值的特殊类型。表示一个空对象引用

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

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

undefined

在 JavaScript 中, undefined 是一个没有设置值的变量

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

19.JavaScript 正则表达式

20.JavaScript 表单验证

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

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

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

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

HTML 表单自动验证

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

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

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

21.JavaScript this 关键字

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

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

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

方法中的 this

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

在上面一个实例中,this 表示 person 对象

fullName 方法所属的对象就是 person

<script>
// 创建一个对象
var person = {
  firstName: "John",
  lastName : "Doe",
  id     : 5566,
  fullName : function() {
    return this.firstName + " " + this.lastName;
  }
};

// 显示对象的数据
document.getElementById("demo").innerHTML = person.fullName();
</script>

事件中的 this

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

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

对象方法中绑定

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

<script>
// 创建一个对象
var person = {
  firstName  : "John",
  lastName   : "Doe",
  id     : 5566,
  myFunction : function() {
    return this;
  }
};

// 显示表单数据
document.getElementById("demo").innerHTML = person.myFunction();
</script>

显式函数绑定

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

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

<script>
var person1 = {
  fullName: function() {
    return this.firstName + " " + this.lastName;
  }
}
var person2 = {
  firstName:"John",
  lastName: "Doe",
}
var x = person1.fullName.call(person2); 
document.getElementById("demo").innerHTML = x; 
</script>

22. let 和 const区别

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

let 声明的变量只在 let 命令所在的代码块内有效。

const 声明一个只读的常量,一旦声明,常量的值就不能改变。

在 ES6 之前,JavaScript 只有两种作用域: 全局变量 与 函数内的局部变量

全局变量

在函数外声明的变量作用域是全局的:

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

全局变量在 JavaScript 程序的任何地方都可以访问。

局部变量

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

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

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

块级作用域(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 关键字:

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

在第一个实例中,使用了 var 关键字,它声明的变量是全局的,包括循环体内与循环体外。

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

变量提升

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

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

x = 5; // 变量 x 设置为 5

elem = document.getElementById("demo"); // 查找元素 
elem.innerHTML = x;                     
// 在元素中显示 xvar x; // 声明 x

var x = 5; // 初始化 x
var y;     // 声明 y

elem = document.getElementById("demo"); // 查找元素
elem.innerHTML = x + " " + y;           // 显示 x 和 y

y = 7;    // 设置 y 为 7

const 关键字

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

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

  • const声明的常量必须初始化,而let声明的变量不用
  • const 定义常量的值不能通过再赋值修改,也不能再次声明。而 let 定义的变量值可以修改

const 的本质: const 定义的变量并非常量,并非不可变,它定义了一个常量引用一个值。

使用 const 定义的对象或者数组,其实是可变的。下面的代码并不会报错:

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

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

23.JavaScript JSON

JSON 是用于存储和传输数据的格式。

JSON 通常用于服务端向网页传递数据 。

什么是 JSON

JSON 是一种轻量级的数据交换格式

JSON 语法规则

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

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

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

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

"name":"Runoob"

JSON 对象

JSON 对象保存在大括号内。

就像在 JavaScript 中, 对象可以保存多个 键/值 对:

{"name":"Runoob", "url":"www.runoob.com"}

JSON 数组

JSON 数组保存在中括号内。

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

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

JSON 字符串转换为 JavaScript 对象

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

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

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

var obj = JSON.parse(text);

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

var text = '{ "sites" : [' +
    '{ "name":"Runoob" , "url":"www.runoob.com" },' +
    '{ "name":"Google" , "url":"www.google.com" },' +
    '{ "name":"Taobao" , "url":"www.taobao.com" } ]}';
    
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字符串转换为JS对象,使用 JSON.parse() 方法

 //结果是 {a: 'Hello', b: 'World'} 一个对象
var obj = JSON.parse('{"a": "Hello", "b": "World"}');

要实现从JS对象转换为JSON字符串,使用 JSON.stringify() 方法

 //结果是 '{"a": "Hello", "b": "World"}' 一个JSON格式的字符串

var json = JSON.stringify({a: 'Hello', b: 'World'}); 

javascript:void(0)

创建了一个超级链接,当用户点击以后不会发生任何事

<a href="javascript:void(0)">单击此处什么也不会发生</a>

24.异步编程

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

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

026a4a7f724d478482a51633af758318.png

回调函数

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

setTimeout(function () {
    document.getElementById("demo").innerHTML="RUNOOB!";
}, 3000);

25.JavaScript Promise

Promise 是一个 ECMAScript 6 提供的类,目的是更加优雅地书写复杂的异步任务

由于 Promise 是 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 构造函数是 JavaScript 中用于创建 Promise 对象的内置构造函数。

Promise 构造函数接受一个函数作为参数,该函数是同步的并且会被立即执行,所以我们称之为起始函数。起始函数包含两个参数 resolve 和 reject,分别表示 Promise 成功和失败的状态。

起始函数执行成功时,它应该调用 resolve 函数并传递成功的结果。当起始函数执行失败时,它应该调用 reject 函数并传递失败的原因。

Promise 构造函数返回一个 Promise 对象,该对象具有以下几个方法:

  • then:用于处理 Promise 成功状态的回调函数。
  • catch:用于处理 Promise 失败状态的回调函数。
  • finally:无论 Promise 是成功还是失败,都会执行的回调函数。

下面是一个使用 Promise 构造函数创建 Promise 对象的例子

const promise = new Promise((resolve, reject) => {
  // 异步操作
  setTimeout(() => {
    if (Math.random() < 0.5) {
      resolve('success');
    } else {
      reject('error');
    }
  }, 1000);
});
 
promise.then(result => {
  console.log(result);
}).catch(error => {
  console.log(error);
});

在上面的例子中,我们使用 Promise 构造函数创建了一个 Promise 对象,并使用 setTimeout 模拟了一个异步操作。如果异步操作成功,则调用 resolve 函数并传递成功的结果;如果异步操作失败,则调用 reject 函数并传递失败的原因。然后我们使用 then 方法处理 Promise 成功状态的回调函数,使用 catch 方法处理 Promise 失败状态的回调函数。

这段程序会直接输出 error 或 success

resolve 和 reject 都是函数,其中调用 resolve 代表一切正常,reject 是出现异常时所调用的

 

new Promise(function (resolve, reject) {
    var a = 0;
    var b = 1;
    if (b == 0) reject("Divide zero");
    else resolve(a / b);
}).then(function (value) {
    console.log("a / b = " + value);
}).catch(function (err) {
    console.log(err);
}).finally(function () {
    console.log("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);
});

但是请注意以下两点:

  • 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");
});

异步函数

异步函数(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 运行中暂停,直到其运行结束再继续运行

 

处理异常的机制将用 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();

总结

  •  1、Promise 对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)
  •  2、Promise构造函数接收一个函数作为参数,该函数的两个参数分别是 resolve 和 reject
  •  3、一个promise对象只能改变一次状态,成功或者失败后都会返回结果数据。
  •  4、then 方法可以接收两个回调函数作为参数,第一个回调函数是Promise对象的状态改变为 resoved 是调用,第二个回调函数是 Promise 对象的状态变为 rejected 时调用。其中第二个参数可以省略。
  •  5、catch 方法,该方法相当于最近的 then 方法的第二个参数,指向 reject 的回调函数,另一个作用是,在执行resolve回调函数时,如果出错,抛出异常,不会停止运行,而是进入catch 方法中。

注意:catch 只捕获最近的 then 的回调函数,前面的then的执行不成功的结果,有后面 then 的 reject 回调函数执行,如果没有后续 then 回调函数执行,则会被 catch 捕获执行

var promise =new Promise(function(resolve,reject){
    //To Do 要异步执行的事情,这个异步执行的事情有可能成功执行完毕,那么Promise将是        fulfilled状态,如果执行失败则是rejected;
    //下面测试代码,人为设置为rejected状态;
    reject("将当前构建的Promise对象的状态由pending(进行中)设置为rejected(已拒绝)"); //当然此处也可以设置为fulfilled(已完成)状态
})

promise.then(//调用第一个then()
    success=>{
        console.log("异步执行成功,状态为:fulfilled,成功后返回的结果是:"+success);
        return(" 当前 success ");
    },
    error=>{
        console.log("异步执行失败,状态为rejected,失败后返回的结果是:"+error);
        return(" 当前 error ");
    }
).then(
    //调用第二个then() 因为调用第一个then()方法返回的是一个新的promise对象,此对象的状态由上面的success或者error两个回调函数的执行情况决定的:
    //如果回调函数能正常执行完毕,则新的promise对象的状态为fulfilled,下面执行success2,如果回调函数无法正常执行,则promise状态为rejected;下面执行error2
    success2=>{
        console.log("第一个then的回调函数执行成功 成功返回结果:"+success2);
        throw(" 当前 success2 ");//自定义异常抛出
    },
    error2=>{
        console.log("第一个then的回调函数执行失败 失败返回结果:"+error2);
        return(" 当前 error2 ");
    }
).catch(err=>{
    //当success2或者error2执行报错时,catch会捕获异常;
    console.log("捕获异常:"+err);
});

 

26.JavaScript 函数

JavaScript 函数定义

JavaScript 使用关键字 function 定义函数。

函数可以通过声明定义,也可以是一个表达式

函数声明

函数声明后不会立即执行,会在我们需要的时候调用到

function myFunction(a, b) {  return a * b;}

函数表达式

var x = function (a, b) {return a * b};
var z = x(4, 3);

Function() 构造函数

在以上实例中,我们了解到函数通过关键字 function 定义。

函数同样可以通过内置的 JavaScript 函数构造器(Function())定义

var myFunction = new Function("a", "b", "return a * b");
var x = myFunction(4, 3);

实际上,你不必使用构造函数。上面实例可以写成

var myFunction = function (a, b) {return a * b};
var x = myFunction(4, 3);

箭头函数

ES6 新增了箭头函数。

箭头函数表达式的语法比普通函数表达式更简洁

(参数1, 参数2, …, 参数N) => { 函数声明 }

(参数1, 参数2, …, 参数N) => 表达式(单一)
// 相当于:(参数1, 参数2, …, 参数N) =>{ return 表达式; }

当只有一个参数时,圆括号是可选的

(单一参数) => {函数声明}
单一参数 => {函数声明}

没有参数的函数应该写成一对圆括号

() => {函数声明}

// ES5
var x = function(x, y) {
     return x * y;
}
 
// ES6
const x = (x, y) => x * y;

有的箭头函数都没有自己的 this。 不适合定义一个 对象的方法

当我们使用箭头函数的时候,箭头函数会默认帮我们绑定外层 this 的值,所以在箭头函数中 this 的值和外层的 this 是一样的。

箭头函数是不能提升的,所以需要在使用之前定义。

使用 const 比使用 var 更安全,因为函数表达式始终是一个常量。

如果函数部分只是一个语句,则可以省略 return 关键字和大括号 {},这样做是一个比较好的习惯

const x = (x, y) => { return x * y };

总结

(参数) => { 函数体 }

// 普通函数
let sum = function(a, b) {
    return a + b;
}

// 箭头函数
let sum1 = (a, b) => {
    return a + b;
}

省略包含参数的小括号

// 有效
let sum = (x) => {
    return x;
};
// 有效
let sum1 = x => {
    return x;
};
// 没有参数需要括号
let sum2 = () => {
    return 1;
};
// 有多个参数需要括号
let sum3 = (a, b) => {
    return a + b;
};

 省略包含函数体的大括号

// 有效
let sum = (a, b) => {
    return a + b;
};
// 有效
let sum1 = (a, b) => a + b; // 相当于 return a + b;
// 无效的写法
let sum2 = (a, b) => return a + b;

 嵌入函数

let arr = [1, 2, 3, 4, 5];
arr.map(val => val * 2); // [2, 4, 6, 8, 10]

箭头函数不能使用arguments

// 普通函数
let sum = function() {
    return arguments.length;
}
sum(1, 2, 3); // 3

// 箭头函数
let sum1 = () => {
    return arguments.length;
}

sum1(1, 2); // Uncaught ReferenceError: arguments is not defined

虽然箭头函数中没有 arguments 对象,但可以在包装函数中把它提供给箭头函数:

function foo() {
    let bar = () => {
        console.log(arguments.length);
    }
    bar(); 
}
foo(5, 5, 5);  // 3

箭头函数中this 指向

let num = 11;
const obj1 = {
    num: 22,
    fn1: function() {
        let num = 33;
        const obj2 = {
            num: 44,
            fn2: () => {
                console.log(this.num);
            }
        }
        obj2.fn2();
    }
}
obj1.fn1(); // 22
 

fn2中得到的结果为:22

原因箭头函数没有this,箭头函数的this是继承父执行上下文里面的this ,这里箭头函数的执行上下文是函数fn1(),所以它就继承了fn1()的this,obj1调用的fn1,所以fn1的this指向obj1, 所以obj1.num 为 22。
注意:简单对象(非函数)是没有执行上下文的!
 

如果fn1也是个箭头函数呢?

let num = 11; 
const obj1 = {
    num: 22,
    fn1: () => {
        let num = 33;
        const obj2 = {
            num: 44,
            fn2: () => {
                console.log(this.num);
            }
        }
        obj2.fn2();
    }
}
obj1.fn1();

上述结果为undefined,因为fn1也是一个箭头函数,所以它就只能继续向上找也就是window了。

那为什么是undefined而不是11呢?

这里涉及到var和let声明变量的一个区别:使用 let 在全局作用域中声明的变量不会成为 window 对象的属性,var 声明的变量则会(不过,let 声明仍然是在全局作用域中发生的,相应变量会在页面的生命周期内存续,所以使用window访问会为undefined):
 

var a = 1;
window.a; // 1

let b = 1;
window.b; // undefined

将let改成var后:

var num = 11; // 
const obj1 = {
    num: 22,
    fn1: () => {
        let num = 33;
        const obj2 = {
            num: 44,
            fn2: () => {
                console.log(this.num);
            }
        }
        obj2.fn2();
    }
}
obj1.fn1();

此时结果为window.num => 11

 

JavaScript 函数参数

参数传递

function foo(a, b) {
    console.log([a, b]);
}

foo(1, 2); // 输出 [1, 2]

这个例子中,a 和 b 属于函数中的局部变量,只能在函数中访问。调用函数时,传递的数据会根据位置来匹配对应,分别赋值给 a 和 b

创建函数时,function 函数名 后面括号中设定的参数被称为形参;调用函数时,函数名后面括号中传入的参数被称为实参

 

因为形参是已声明的变量,所以不能再用 let 和 const 重复声明

function foo(a, b) {
    let a = 1; // 报错,a 已声明
    const b = 1; // 报错,b 已声明
}

从一个变量赋值到另一个变量

function add(num) {
    return num + 1;
}

let count = 5;
let result = add(count); // 此处参数传递的过程可以看作是 num = count

console.log(count); // 5
console.log(result); // 6

引用值: 

function setName(obj) {
    obj.name = "小明";
}

let person = {};

setName(person); // 此处参数传递的过程可以看作是 obj = person;
console.log(person); // {name: "小明"}

理解参数

JavaScript 中的函数既不会检测参数的类型,也不会检测传入参数的个数。定义函数时设置两个形参,不意味着调用时必须传入两个参数。实际调用时不管是传了一个还是三个,甚至不传参数也不会报错。

所有函数(非箭头)中都有一个名为 arguments 的特殊的类数组对象(不是 Array 的实例),它保存着所有实参的副本,我们可以通过它按照数组的索引访问方式获取所有实参的值,也可以访问它的 arguments.length 属性来确定函数实际调用时传入的参数个数。

function foo(a, b) {
    console.log(arguments[0]);
    console.log(arguments[1]);
    console.log(arguments.length);
}

foo(10, 20); // 依次输出 10、20、2 

将对象属性用作实参

当一个函数包含的形参有多个时,调用函数就成了一种麻烦,因为你总是要保证传入的参数放在正确的位置上,有没有办法解决传参顺序的限制呢?

由于对象属性是无序的,通过属性名来确定对应的值。因此可以通过传入对象的方式,以对象中的属性作为真正的实参,这样参数的顺序就无关紧要了。

function foo(obj) {
    console.log(obj.name, obj.sex, obj.age);
}

foo({ sex: '男', age: 18, name: '小明' }); // 小明 男 18

参数默认值

如果调用函数时缺少提供实参,那么形参默认值为 undefined

有时候我们想要设置特定的默认值,在 ES6 之前还不支持显式地设置默认值的时候,只能采用变通的方式

function sayHi(name) {
    name = name || 'everyone';
    
    console.log( 'Hello ' + name + '!');
}

sayHi(); // 输出 'Hello everyone!' 

// if 语句判断
function sayHi(name) {
    if (name === undefined) {
        name = 'everyone';
    }
    
    console.log( 'Hello ' + name + '!');
}

// 三元表达式判断
function sayHi(name) {
    name =  (name !== undefined) ? name : 'everyone';
    
    console.log( 'Hello ' + name + '!');
}

ES6 就方便了许多,因为它支持了显式的设置默认值的方式

function sayHi(name = 'everyone') { // 定义函数时,直接给形参赋值
    console.log( 'Hello ' + name + '!');
}

sayHi(); // 输出 'Hello everyone!' 
sayHi('Tony'); // 输出 'Hello Tony!' 
sayHi(undefined); // 输出 'Hello everyone!'

参数默认值的位置

function fn(x, y = 2) {
    console.log([x, y]);
}

fn(); // 输出 [undefined, 2]
fn(1); // 输出 [1, 2]
fn(1, 1) // 输出 [1, 1]

参数的收集与展开

ES6 提供了**剩余参数(rest)**的语法(...变量名),它可以收集函数多余的实参(即没有对应形参的实参),这样就不再需要使用 arguments 对象来获取了。形参使用了 ... 操作符会变成一个数组,多余的实参都会被放进这个数组中。

剩余参数基本用法:

function sum(a, ...values) {
 
    for (let val of values) {
        a += val;
    }
    
    return a;
}

sum(0, 1, 2, 3); // 6

参数位置 

// 报错
function fn1(a, ...rest, b) {
    console.log([a, b, rest]);

// 正确写法
function fn2(a, b, ...rest) {
    console.log([a, b, rest]);
}

fn2(1, 2, 3, 4) // 输出 [1, 2, [3, 4]]

 

function sum(...values) {
    let sum = 0;
    
    for (let val of values) {
        sum += val;
    }
    
    return sum;
}

let arr = [1, 2, 3, 4];

sum(arr); // "01,2,3,4"

把所有传进来的数值累加,如果直接传入一个数组,就得到我们想要的结果

sum.apply(null, arr); // 10

sum(...arr); // 10  // 相当于 sum(1,2,3,4);

sum(-1, ...arr); // 9
sum(...arr, 5); // 15
sum(-1, ...arr, 5); // 14
sum(-1, ...arr, ...[5, 6, 7]); // 27

JavaScript 对象

对象只是一种特殊的数据。对象拥有属性方法

创建了对象的一个新实例,并向其添加了四个属性

JavaScript 对象就是一个 name:value 集合

person=new Object();
person.firstname="John";
person.lastname="Doe";
person.age=50;
person.eyecolor="blue";

{ name1 : value1, name2 : value2,...nameN : valueN }

 

 

  • 20
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值