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代码,如下图
上图中我们在 > 符号后输入我们要执行的代码 console.log("runoob"),按回车后执行。
我们也可以在其他地方复制一段代码过来执行,比如复制以下代码到 Console 窗口,按回车执行
4.2清空 Console 窗口到内容可以按以下按钮
4.3Chrome snippets 小脚本
我们也可以在 Chrome 浏览器中创建一个脚本来执行,在开发者工具中点击 Sources 面板,选择 Snippets 选项卡,在导航器中右击鼠标,然后选择 Create new snippet 来新建一个脚本文件
如果你没看到 Snippets ,可以点下面板上到 >> 就能看到了
点击 Create new snippet 后,会自动创建一个文件,你只需在右侧窗口输入以下代码,然后按 Command+S(Mac)或 Ctrl+S(Windows 和 Linux)保存更改即可
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 语句标识符
空格
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>
其它事件
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
比较运算符
逻辑运算符
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; // 声明 yelem = document.getElementById("demo"); // 查找元素
elem.innerHTML = x + " " + y; // 显示 x 和 yy = 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)相对的概念
程序的运行是同步的(同步不意味着所有步骤同时运行,而是指步骤在一个控制流序列中按顺序执行)。而异步的概念则是不保证同步的概念,也就是说,一个异步过程的执行将不再与原有的序列有顺序关系
回调函数
回调函数就是一个函数,它是在我们启动一个异步任务的时候就告诉它:等你完成了这个任务之后要干什么。这样一来主线程几乎不用关心异步任务的状态了,他自己会善始善终
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; // 1let 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 = countconsole.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 }