JavaScript进阶(四)

2019版黑马程序员javaScript进阶面向对象ES6 122集教程,哔哩哔哩链接:https://www.bilibili.com/video/BV1Kt411w7MP?from=search&seid=936655930091245798&spm_id_from=333.337.0.0

2020版尚硅谷Web前端ES6教程,涵盖ES6-ES11 68集教程,哔哩哔哩链接:https://www.bilibili.com/video/BV1uK411H7on?spm_id_from=333.999.0.0

ECMAScript 6-11

ES5和ES6的作用域详解

在ES5中,只有全局作用域和函数作用域,无块级作用域。这会导致函数作用域覆盖了全局作用域;亦或者循环中的变量泄露为全局变量。

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><script>/*1.函数作用域覆盖了全局作用域,发生了变量提升,函数声明大于var声明的变量,因此函数里面的a提到了前面,在打印a,初始化一个undefined给a,所以打印出了undefined。*/var a = '1';function fn() {console.log(a);if (3 < 2) {var a = 3;}}fn(); // undefined// 2.循环中的变量泄露为全局变量for (var i = 0; i < 5; i++) {console.log(i); //依次输出为0、1、2、3、4}console.log(i); // 输出为5;</script>
</body>
</html>

ES6的块级作用域

用let命令新增了块级作用域,外层作用域无法获取到内层作用域,非常安全明了。即使外层和内层都使用相同变量名,也都互不干扰。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><script>// es6中的块级作用域// 1.外层作用域无法获取到内层作用域function fn1() {let a = 41;if (1 == 1) {let a = 3;console.log(2, a); // 2 3}console.log(1, a); // 1 41}fn1();// 2. 外层和内层都使用相同变量名,也都互不干扰{{let food = 'apple';console.log(food); // apple}let food = 'orange';console.log(food); // orange}</script>
</body></html>

块级作用域和和函数声明

在ES5中,函数只能在顶级作用域和函数作用域中声明,不能在块级作用域中声明。但是在ES6中,函数可以在块级作用域中声明。

但是会有一定的问题,因为函数声明会被提到代码的最前面。所以会报错,最好在ES6中用函数表达式来表示一个函数。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><script>//1.函数声明报错// {//     if (4 < 2) {//         function fn() {//             console.log('我在函数里面!');//         }//     }// }// fn(); // Uncaught TypeError: fn is not a function//2.函数表达式没错{let fa = '111';let fn = function () {console.log('我在函数里面!');}console.log(fa, fn); // 111 ƒ () { console.log('我在函数里面!');}}// 注意:ES6中允许函数在块级作用域中可以声明的条件是必须在大括号里面,否则就会报错?。// 1.报错的情况// if (4 > 2) {//     let fn2 = function () {//         console.log("nih");//     };// }// console.log(fn2);</script>
</body></html>

eval全局作用域

问题描述:
在使用eval()执行代码的时候,遇到作用域的问题。eval()语句写于函数内,因此eval()内执行的变量也只是局部变量。函数外部无法访问。

问题分析:

var x = 10;
function testEval() {eval("var x = 20");//局部变量
}
testEval();
console.log(x); //10

使用window.eval(),谷歌环境测试:

var x = 10;
function testEval() {window.eval("var x = 20");//全局变量
}
testEval();
console.log(x); //20

注意:
window.eval()需要考虑浏览器兼容性:

​ IE6/7/8中,eval和window.eval一样,都是局部作用域

​ Firefox/Safari/Chrome/Opera/IE9中,window.eval即使写在自定义函数内使用的也是全局作用域。

使用全局eval()

将eval赋值给变量,使其作为一个全局函数调用:

var x = 10;
var geval = eval;
function testEval() {geval("var x = 20");//全局变量
}
testEval();
console.log(x); //20

解决方法:
在自定义函数内要使eval内的变量具有全局作用域,可以使用以下两种方法:

  1. 使用window.eval(),需要考虑浏览器兼容性
  2. eval赋值给变量,使之具有全局作用域

js中的全局变量与局部变量

1.全局变量:声明在函数外部的变量(所有没有var直接赋值的变量都属于全局变量)

2.局部变量:声明在函数内部的变量(所有没有var直接赋值的变量都属于全局变量)

JS中变量申明分显式申明和隐式申明。

​ var i=100;//显式申明

​ i=100;//隐式申明

在函数中使用var关键字进行显式申明的变量是做为局部变量,而没有用var关键字,使用直接赋值方式声明的是全局变量。

当我们使用访问一个没有声明的变量时,JS会报错。而当我们给一个没有声明的变量赋值时,JS不会报错,相反它会认为我们是要隐式申明一个全局变量,这一点一定要注意。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><script>//输出undefind,全局变量在整个上下文都有效只是在没有赋值之前调用,会输出undefindalert(c);//alert(d);报错,Uncaught ReferenceError: d is not definedvar c = 3;function test() {var a = 1;b = 2;alert(c)//下面调用后输出3  }alert(c);//输出3  test();//输出3  </script>
</body></html>

函数作用域是针对局部变量来说的,在函数中定义的变量在函数外不能获取

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><script>function test() {alert(a);//声明未赋值输出undefine  var a = 1;alert(a);//1  }test();//alert(a); //报错,外部获取不到  </script>
</body></html>

对回调函数的理解

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。

在JavaScript中,回调函数具体的定义为:函数A作为参数(函数引用)传递到另一个函数B中,并且这个函数B执行函数A。我们就说函数A叫做回调函数。如果没有名称(函数表达式),就叫做匿名回调函数。

实例1

  //定义主函数,回调函数作为参数
function A(callback) {callback();  console.log('我是主函数');
}//定义回调函数
function B(){setTimeout("console.log('我是回调函数')", 3000);//模仿耗时操作
}//调用主函数,将函数B传进去
A(B);
//输出结果
我是主函数
我是回调函数

实例2

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><script>function a() {console.log("执行a函数");setTimeout(function () {console.log("执行a函数的延迟函数");}, 1000);}function b() {console.log("执行函数b");}a();b();</script>
</body>
</html>

以上代码会先执行函数a,而且不会等到a中的延迟函数执行完才执行函数b, 在延迟函数被触发的过程中就执行了函数b,当js引擎的event 队列空闲时才会去执行队列里等待的setTimeout的回调函数,这就是一个异步的例子

调用 setTimeout 函数会在一个时间段过去后在队列中添加一个消息。这个时间段作为函数的第二个参数被传入。如果队列中没有其它消息,消息会被马上处理。但是,如果有其它消息,setTimeout 消息必须等待其它消息处理完。因此第二个参数仅仅表示最少的时间 而非确切的时间

所以即使,时间设置为0,也是会照样先执行函数b

回调函数广泛运用在同步(sync)和异步(async)

一、 ECMASript 相关介绍

1.1.什么是 ECMA

ECMA(European Computer Manufacturers Association)中文名称为欧洲计算机制 造商协会,这个组织的目标是评估、开发和认可电信和计算机标准。1994 年后该 组织改名为 Ecma 国际。

1.2.什么是 ECMAScript

ECMAScript 是由 Ecma 国际通过 ECMA-262 标准化的脚本程序设计语言。

1.3.什么是 ECMA-262

Ecma 国际制定了许多标准,而 ECMA-262 只是其中的一个,所有标准列表查看 http://www.ecma-international.org/publications/standards/Standard.htm

1.4.ECMA-262 历史

ECMA-262(ECMAScript)历史版本查看网址 http://www.ecma-international.org/publications/standards/Ecma-262-arch.htm

1.5.谁在维护 ECMA-262

TC39(Technical Committee 39)是推进 ECMAScript 发展的委员会。其会员都是 公司(其中主要是浏览器厂商,有苹果、谷歌、微软、因特尔等)。TC39 定期 召开会议,会议由会员公司的代表与特邀专家出席

1.6.为什么要学习 ES6

ES6 的版本变动内容最多,具有里程碑意义

ES6 加入许多新的语法特性,编程实现更简单、高效

ES6 是前端发展趋势,就业必备技能

1.7.ES6 兼容性

http://kangax.github.io/compat-table/es6/ 可查看兼容性

二、 ECMASript 6 新特性

2.1.let 关键字

let 关键字用来声明变量,使用 let 声明的变量有几个特点:

​ 1) 不允许重复声明

​ 2) 块级作用域

​ 3) 不存在变量提升

​ 4) 不影响作用域链

应用场景:以后声明变量使用 let 就对了

代码演示:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>let</title>
</head>
<body><script>//声明变量let a;let b,c,d;let e = 100;let f = 521, g = 'iloveyou', h = [];//1. 变量不能重复声明,但是如果使用var的话可以,不会报错// let star = '罗志祥';// let star = '小猪';  //2. 块级作用域     es5:全局作用域, 函数作用域, eval作用域(严格模式中才出现)// 像if else while for 这些循环里也是块级作用域// {//如果用var声明的话就可以读取到,因为var在代码里没有块级作用域,//会往全局的window里面添加这个属性,所以可以读取到//     let girl = '周扬青'; // }// console.log(girl);//3. 不存在变量提升// console.log(song);  //会报错,用let声明的不允许在变量声明之前使用// let song = '恋爱达人';//输出为undefined,它在执行之前会收集这个变量//(注意要有这个变量,前面和后面声明都可以,不然会报错,说未定义),//收集成功后相当于在他前面添加了一个 var song;// console.log(song);  // var song = '恋爱达人';//4. 不影响作用域链{let school = '尚硅谷';function fn(){//函数作用域里没有school这个变量,会向上一级作用域里去找这个变量console.log(school);}fn(); //输出为 尚硅谷,}</script>
</body>
</html>

let实践案例

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>点击 DIV 换色</title><link crossorigin="anonymous" href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.min.css"rel="stylesheet"><style>.item {width: 100px;height: 50px;border: solid 1px rgb(42, 156, 156);float: left;margin-right: 10px;}</style>
</head><body><div class="container"><h2 class="page-header">点击切换颜色</h2><div class="item"></div><div class="item"></div><div class="item"></div></div><script>//获取div元素对象let items = document.getElementsByClassName('item');//遍历并绑定事件//注意这里不能用var声明,因为执行完这个循环,var已经变为3for(let i = 0;i<items.length;i++){ items[i].onclick = function(){//修改当前元素的背景颜色// this.style.background = 'pink';items[i].style.background = 'pink';}}      </script>
</body></html>

2.2. const 关键字

const 关键字用来声明常量,const 声明有以下特点 :

​ 1) 声明必须赋初始值

​ 2) 标识符一般为大写

​ 3) 不允许重复声明

​ 4) 值不允许修改

​ 5) 块级作用域

注意: 对象属性修改和数组元素变化不会出发 const 错误

应用场景:声明对象类型使用 const,非对象类型声明选择 let

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>const 定义常量</title>
</head>
<body><script>//声明常量const SCHOOL = '尚硅谷';//1. 一定要赋初始值// const A;  //报错//2. 一般常量使用大写(潜规则)// const a = 100;  //不会报错//3. 常量的值不能修改// SCHOOL = 'ATGUIGU';  //报错//4. 块级作用域// {//     const PLAYER = 'UZI';// }// console.log(PLAYER); //报错//5. 对于数组和对象的元素修改, 不算做对常量的修改, 不会报错const TEAM = ['UZI','MXLG','Ming','Letme'];TEAM.push('Meiko');// TEAM = 100; //报错console.log(TEAM);  //["UZI", "MXLG", "Ming", "Letme", "Meiko"]</script>
</body>
</html>

2.3.变量的解构赋值

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称 为解构赋值。

//数组的解构赋值
const arr = ['张学友', '刘德华', '黎明', '郭富城'];
let [zhang, liu, li, guo] = arr;//对象的解构赋值
const lin = {name: '林志颖',tags: ['车手', '歌手', '小旋风', '演员']
};
let {name, tags} = lin;//复杂解构
let wangfei = {name: '王菲',age: 18,songs: ['红豆', '流年', '暧昧', '传奇'],history: [{name: '窦唯'},{name: '李亚鹏'},{name: '谢霆锋'}]
};
//用songs去匹配wangfei中的songs属性,并将匹配好的依次赋值
let {songs: [one, two, three, four], history: [first, second, third]} = wangfei;

注意:频繁使用对象方法、数组元素,就可以使用解构赋值形式

代码演示:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>变量的解构赋值</title>
</head><body><script>//ES6 允许按照一定模式从数组和对象中提取值,对变量进行赋值,//这被称为解构赋值。//1. 数组的结构// const F4 = ['小沈阳','刘能','赵四','宋小宝'];// let [xiao, liu, zhao, song] = F4;// console.log(xiao);// console.log(liu);// console.log(zhao);// console.log(song);//2. 对象的解构const zhao = {name: '赵本山',age: '不详',xiaopin: function(){console.log("我可以演小品");}};// let {name, age, xiaopin} = zhao;// console.log(name);// console.log(age);// console.log(xiaopin);// xiaopin();let { xiaopin, a } = zhao;xiaopin();//我可以演小品console.log(a); //输出为undefined,注意如果是对象的解构,名字要匹配(相同)let wangfei = {name: '王菲',age: 18,songs: ['红豆', '流年', '暧昧', '传奇'],history: [{ name: '窦唯' },{ name: '李亚鹏' },{ name: '谢霆锋' }]};/*用songs去匹配wangfei中的songs属性,并将匹配好的依次赋值*/let { songs: [one, two, three, four], history: [first, second, third] } = wangfei; //红豆 流年 暧昧 传奇 {name: "窦唯"} {name: "李亚鹏"} {name: "谢霆锋"}console.log(one,two,three,four,first,second,third); </script>
</body></html>

2.4.模板字符串

模板字符串(template string)是增强版的字符串,用反引号(`)标识,特点:

​ 1) 字符串中可以出现换行符

​ 2) 可以使用 ${xxx} 形式输出变量

// 定义字符串
let str = `<ul><li>沈腾</li><li>玛丽</li><li>魏翔</li><li>艾伦</li></ul>`;
// 变量拼接
let star = '王宁';
let result = `${star}在前几年离开了开心麻花`;

注意:当遇到字符串与变量拼接的情况使用模板字符串

代码演示:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>模板字符串</title>
</head><body><script>// ES6 引入新的声明字符串的方式 『``』 '' "" //1. 声明let str = `我也是一个字符串哦!`;console.log(str, typeof str); //我也是一个字符串哦! string/*2. 内容中可以直接出现换行符,我们就可以不用再拼接了let str3 = '<tr class="tabPeo"><td>'+ data[i].name+ '</td><td>'+ data[i].address+ '</td><td>'+ data[i].state+ '</td></tr>'*/let str2 = `<ul><li>沈腾</li><li>玛丽</li><li>魏翔</li><li>艾伦</li></ul>`;//3. 变量拼接let lovest = '魏翔';let out = `${lovest}是我心目中最搞笑的演员!!`;console.log(out); //魏翔是我心目中最搞笑的演员!!</script>
</body></html>

2.5.简化对象写法

ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这 样的书写更加简洁。

let name = '尚硅谷';
let slogon = '永远追求行业更高标准';
let improve = function () {console.log('可以提高你的技能');
}
//属性和方法简写
let atguigu = {name,slogon,improve,change() {console.log('可以改变你')}
};

注意:对象简写形式简化了代码,所以以后用简写就对了

代码演示:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>简化对象写法</title>
</head>
<body><script>//ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。//这样的书写更加简洁let name = '尚硅谷';let change = function(){console.log('我们可以改变你!!');}const school = {name, //es6里允许我们只把变量放进来change,improve(){console.log("我们可以提高你的技能");}}/*上面的写法相当于:const school = {name: name,change: change,improve: function(){console.log("我们可以提高你的技能");}}*/console.log(school); //{name: "尚硅谷", change: ƒ, improve: ƒ}</script>
</body>
</html>

2.6.箭头函数

ES6 允许使用「箭头」(=>)定义函数。

/**
* 1. 通用写法
*/
let fn = (arg1, arg2, arg3) => {return arg1 + arg2 + arg3;
}箭头函数的注意点:1) 如果形参只有一个,则小括号可以省略2) 函数体如果只有一条语句,则花括号可以省略,函数的返回值为该条语句的执行结果3) 箭头函数 this 指向声明时所在作用域下 this 的值4) 箭头函数不能作为构造函数实例化5) 不能使用 arguments/**
* 2. 省略小括号的情况
*/
let fn2 = num => {return num * 10;
};/**
* 3. 省略花括号的情况
*/
let fn3 = score => score * 20;/**
* 4. this 指向声明时所在作用域中 this 的值
*/
let fn4 = () => {console.log(this);
}let school = {name: '尚硅谷',getName(){let fn5 = () => {console.log(this);}fn5();}
};

注意:箭头函数不会更改 this 指向,用来指定回调函数会非常合适

代码演示:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>箭头函数</title>
</head><body><script>// ES6 允许使用「箭头」(=>)定义函数。//声明一个函数let fn = (a,b) => {return a + b;}//调用函数let result = fn(1, 2);console.log(result); //3//1. this 是静态的. this 始终指向函数声明时所在作用域下的 this 的值function getName(){console.log(this.name);}let getName2 = () => {console.log(this.name);}//设置 window 对象的 name 属性window.name = '尚硅谷';const school = {name: "ATGUIGU"}//直接调用,this都是指向全局对象windowgetName();//尚硅谷getName2();//尚硅谷//call 方法调用,用call方法改变this指向getName.call(school);//ATGUIGU//尚硅谷,因为箭头函数中的this 始终指向函数声明时所在作用域下的 this 的值getName2.call(school);//2. 不能作为构造函数,实例化对象,以下写法会报错,说Person不是一个构造器// let Person = (name, age) => {//     this.name = name;//     this.age = age;// }// let me = new Person('xiao',30);// console.log(me);//3. 不能使用 arguments 变量,报错,说arguments没有定义// let fn = () => {//     console.log(arguments);// }// fn(1,2,3);//4. 箭头函数的简写//1) 省略小括号, 当形参有且只有一个的时候let add = n => {return n + n;}console.log(add(9));//18//2) 省略花括号, 当代码体只有一条语句的时候, 此时 return 必须省略// 而且语句的执行结果就是函数的返回值let pow = n => n * n;/*相当于let pow = (n) =>{return n * n;}*/     console.log(pow(8));//64</script>
</body></html>

箭头函数的实践与应用场景

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>箭头函数实践</title><style>div {width: 200px;height: 200px;background: #58a;}</style>
</head>
<body><div id="ad"></div><script>//需求-1  点击 div 2s 后颜色变成『粉色』//获取元素/*let ad = document.getElementById('ad');//绑定事件ad.addEventListener("click", function(){//保存 this 的值let _this = this;//定时器setTimeout(function() {//修改背景颜色 this//这里的this指向的是window,而window没有style这个属性,为undefined,//所以下面用this.style.background = 'pink'就会报错console.log(this);// this.style.background = 'pink';//当函数调用时,在当前的作用域下找不到_this,就会往外层的作用域下面去找_this.style.background = 'pink'; }, 2000);});*///箭头函数的写法 let ad2 = document.getElementById('ad');// //绑定事件ad2.addEventListener("click", function(){//保存 this 的值// let _this = this;//定时器setTimeout(() => {//修改背景颜色 this// console.log(this);// _this.style.background = 'pink';//箭头函数中this指向的是在声明时的作用域下的this值,//他是在function的作用域下声明的,所以他指向的是function函数的this值,//而这个函数的this又指向的是事件源adthis.style.background = 'pink';}, 2000);});//需求-2  从数组中返回偶数的元素const arr = [1,6,9,10,100,25];// 不用箭头函数// const result = arr.filter(function(item){//     if(item % 2 === 0){//         return true;//     }else{//         return false;//     }// });const result = arr.filter(item => item % 2 === 0);// 相当于:// const result = arr.filter( item => {//     if(item %2 === 0){//         return true;//     }else{//         return false;//     }// })console.log(result);//[6,10,100]// 箭头函数适合与 this 无关的回调. 定时器, 数组的方法回调// 箭头函数不适合与 this 有关的回调.  事件回调, 对象的方法/*array.filter(function(currentValue,index,arr), thisValue)function(currentValue, index,arr)    必须。函数,数组中的每个元素都会执行这个函数函数参数:参数            描述currentValue  必须。当前元素的值index          可选。当前元素的索引值arr          可选。当前元素属于的数组对象thisValue 可选。对象作为该执行回调时使用,传递给函数,用作 "this" 的值。如果省略了 thisValue ,"this" 的值为 "undefined"返回值:    返回数组,包含了符合条件的所有元素。如果没有符合条件的元素则返回空数组。*/</script>
</body>
</html>

函数参数的默认值设置

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>函数参数默认值</title>
</head>
<body><script>//ES6 允许给函数参数赋值初始值//1. 形参初始值 具有默认值的参数, 一般位置要靠后(潜规则),function add(a,b,c=10) {return a + b + c;}//输出为13,我们没有给c传值,则默认使用初始值,如果我们改为let result = add(1,2,3);则输出为6let result = add(1,2);console.log(result);function add2(a,b,c) {return a + b + c;}let result2 = add2(1,2);//输出为NaN,因为没有给c传值,则c为undefined,1+2+undefined=NaNconsole.log(result2);function add3(a,c=10,b) {return a + b + c;}let result3 = add3(1,2);//输出为NaN, 因为我们传值a=1,c=2,b没有传值所以输出为undefinedconsole.log(result3);//2. 与解构赋值结合function connect({host="127.0.0.1", username,password, port}){console.log(host);console.log(username);console.log(password);console.log(port);}connect({host: 'atguigu.com',//如果我们此处不写这行,则输出为默认给的值127.0.0.1username: 'root',password: 'root',port: 3306})//也可以这样写function connect2(options){let host = options.host;let username = options.username;let password = options.password;let port = options.port;console.log(host);console.log(username);console.log(password);console.log(port);}connect2({host: 'atguigu.com',//如果我们此处不写这行,则输出为默认给的值127.0.0.1username: 'root',password: 'root',port: 3306})</script>
</body>
</html>

类数组对象:arguments

当我们在js中在调用一个函数的时候,我们经常会给这个函数传递一些参数,js把传入到这个函数的全部参数存储在一个叫做arguments的东西里面

在Javascript中arguments是一个类似Array的存在,但它不能等同于Array。

arguments对象不能显式的创建,它只有在函数开始时才可用。

它可以像访问数组那样去访问每一个子元素。PS:arguments[0],arguments[1],。。。。。。。。arguments[n];

Javascript并没有重载函数的功能,但是通过Arguments对象能够模拟重载。

Javascript的每个函数都会有一个Arguments对象实例arguments,它引用着函数的实参,它也有类似于数组的length属性。

​ (1)、arguments.length 为函数实参个数。

​ (2)、arguments.callee 引用函数自身。

1、我们可以借用arguments.length可以来查看实参和形参的个数是否一致:

function add(a, b) {var realLen = arguments.length;console.log("realLen:", arguments.length);var len = add.length;console.log("len:", add.length);if (realLen == len) {console.log('实参和形参个数一致');} else {console.log('实参和形参个数不一致');}
};
add(1,2,3,6,8);

2、我们可以借用arguments.callee来让匿名函数实现递归:

var sum = function(n) {if(n == 1) {return 1;} else {return n + arguments.callee(n-1);}
}
console.log("sum =", sum(5));

3.arguments是什么

function showargs() {console.log( arguments );
}showargs(1,2,3,4,5);

 这里我们可以看到arguments对象将我传入的五个参数以数组的形式保存在里面,
还有保存了我传入函数的实参的个数(length)。而且我们可以看到arguments对象
的 __ proto __ 是指向object的,这也说明了他是个类数组对象,而不是一个数组。有了这个对象我们以后写函数的时候,就不用给所有的形参指定参数名,然后通过
参数名的方式获取参数了,我们可以直接使用arguments对象来获取实参,有些语言在我们给函数指定了参数名之后,当调用函数时,会判断当前传入的参数是否与函数
定义的参数个数相等,不相等就会报错,但是灵活的js(不是我说,js是真的灵活)并不会验证
传递给函数的参数个数是否等于函数定义的参数个数。所以为了代码的简洁度,我们使用arguments
调用参数可以不混淆不同函数之间的参数名。另外为了代码的严整度,我们也能用arguments来
判断当前传入参数的个数是否与我们需要的数量一致。下面举个栗子:
function add() {if( arguments.length == 2 ){return arguments[0] + arguments[1];}else{return '传入参数不合法';}
}console.log( add(2,3) );
console.log( add(1,2,3) );

 最后我们还可以看到arguments还有一个叫做callee的属性,这个属性是表示
的是当前函数的一个引用,简单点说,这个属性里面存储的我们调用的这个函数的代码,
实在无法理解的时候,又到了console.log大显身手的时候了。
function showcallee() {var a = '这里是代码';var b = '这是另一段代码';var c = a + b;console.log(arguments.callee);return c;
}
showcallee();

看到结果的你是不是和我一样惊呆了呢,这不就是我写的代码吗,arguments.callee完完整整的把这个函数的这段代码返回了。

arguments的一些妙用

1.利用arguments实现方法的重载

下面我们利用arguments对象来实现一个参数相加的函数,不论传入多少参数都行,将传入的参数相加后返回。

function add() {var len = arguments.length,sum = 0;for(;len--;){sum += arguments[len];}return sum;
}console.log( add(1,2,3) );   //6
console.log( add(1,3) );     //4
console.log( add(1,2,3,5,6,2,7) );   //26

由于js是一种弱类型的语言,没有重载机制,当我们重写函数时,会将原来的函数直接覆盖,这里我们能利用arguments,来判断传入的实参类型与数量进行不同的操作,然后返回不同的数值。

2.利用arguments.callee实现递归

先来看看之前我们是怎么实现递归的,这是一个结算阶乘的函数

function factorial(num) { if(num<=1) { return 1; }else { return num * factorial(num-1); }
}

但是当这个函数变成了一个匿名函数时,我们就可以利用callee来递归这个函数。

function factorial(num) { if(num<=1) { return 1; }else { return num * arguments.callee(num-1); }
}

这个方法虽然好用,但是有一点值得注意,ECMAScript4中为了限制js的灵活度,让js变得严格,新增了严格模式,在严格模式中我们被禁止不使用var来直接声明一个全局变量,当然这不是重点,重点是arguments.callee这个属性也被禁止了。不过这都不是事儿,ES6为我们新增了很多好用的变量声明方式和新的语法糖,作为一个时髦的前端,我们赶紧学习一些ES6的新语法吧。

2.7. rest 参数

ES6 引入 rest 参数,用于获取函数的实参,用来代替 arguments

/**
* 作用与 arguments 类似
*/
function add(...args){console.log(args);
}
add(1,2,3,4,5);/**
* rest 参数必须是最后一个形参
*/
function minus(a,b,...args){console.log(a,b,args);
}
minus(100,1,2,3,4,5,19);

注意:rest 参数非常适合不定个数参数函数的场景

代码演示:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>rest参数</title>
</head>
<body><script>// ES6 引入 rest 参数,用于获取函数的实参,用来代替 arguments// ES5 获取实参的方式function date(){console.log(arguments);}date('白芷','阿娇','思慧');//它并不是一个数组,而是一个对象// rest 参数,是一个数组,所以我们可以使用数组的方法filter some every map function date2(...args){console.log(args);}date2('阿娇','柏芝','思慧');//它是一个数组// rest 参数必须要放到参数最后,不能放前面或中间,会报错function fn(a,b,...args){console.log(a);console.log(b);console.log(args);}fn(1,2,3,4,5,6);</script>
</body>
</html>

2.8. spread 扩展运算符

扩展运算符(spread)也是三个点(…)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列,对数组进行解包。

/**
* 展开数组
*/
let tfboys = ['德玛西亚之力','德玛西亚之翼','德玛西亚皇子'];
function fn(){console.log(arguments);
}
fn(...tfboys)/**
* 展开对象
*/
let skillOne = {q: '致命打击',
};
let skillTwo = {w: '勇气'
};
let skillThree = {e: '审判'
};
let skillFour = {r: '德玛西亚正义'
};
let gailun = {...skillOne, ...skillTwo,...skillThree,...skillFour};

代码演示:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>扩展运算符</title>
</head>
<body><script>// 『...』 扩展运算符能将『数组』转换为逗号分隔的『参数序列』//声明一个数组 ...const tfboys = ['易烊千玺','王源','王俊凯'];// => '易烊千玺','王源','王俊凯'// 声明一个函数function chunwan(){console.log(arguments);}chunwan(tfboys);// 下面的这行代码相当于 chunwan('易烊千玺','王源','王俊凯')//注意和rest的区分,rest是放在函数声明的形参的位置,而扩展运算符...是放在调用函数的实参里chunwan(...tfboys);</script>
</body>
</html>

扩展运算符的应用

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>扩展运算符应用</title>
</head>
<body><div></div><div></div><div></div><script>//1. 数组的合并 const kuaizi = ['王太利','肖央'];const fenghuang = ['曾毅','玲花'];// es5的做法/*concat() 方法用于连接两个或多个数组。该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。语法  arrayObject.concat(arrayX,arrayX,......,arrayX)参数       描述arrayX     必需。该参数可以是具体的值,也可以是数组对象。可以是任意多个。返回值 返回一个新的数组。该数组是通过把所有 arrayX 参数添加到 arrayObject 中生成的。如果要进行 concat() 操作的参数是数组,那么添加的是数组中的元素,而不是数组。*/// const zuixuanxiaopingguo = kuaizi.concat(fenghuang);//将一个数组转为用逗号分隔的参数序列,这里相当于将两个数组的值拿//出来然后['王太利','肖央','曾毅','玲花']const zuixuanxiaopingguo = [...kuaizi, ...fenghuang];console.log(zuixuanxiaopingguo);//2. 数组的克隆const sanzhihua = ['E','G','M'];const sanyecao = [...sanzhihua];//注意如果是引用数据类型的话是浅拷贝console.log(sanyecao);//3. 将伪数组转为真正的数组const divs = document.querySelectorAll('div');const divArr = [...divs];console.log(divArr);                </script>
</body>
</html>

2.9.Symbol

2.9.1.Symbol 基本使用

ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,是一种类似于字符串的数据类型。

Symbol 特点:

​ 1) Symbol 的值是唯一的,用来解决命名冲突的问题

​ 2) Symbol 值不能与其他数据进行运算

​ 3) Symbol 定义 的 对象属 性 不能 使 用 for…in 循 环遍 历 ,但 是可 以 使 用 Reflect.ownKeys 来获取对象的所有键名

//创建 Symbol
let s1 = Symbol();
console.log(s1, typeof s1);//添加标识的 Symbol
let s2 = Symbol('尚硅谷');
let s2_2 = Symbol('尚硅谷');
console.log(s2 === s2_2);//使用 Symbol for 定义
let s3 = Symbol.for('尚硅谷');
let s3_2 = Symbol.for('尚硅谷');
console.log(s3 === s3_2);

注: 遇到唯一性的场景时要想到 Symbol

代码演示:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>symbol</title>
</head>
<body><script>//创建Symbollet s = Symbol();console.log(s, typeof s);let s2 = Symbol('尚硅谷');//里面的是一个描述字符串,方便我们更好的理解他的作用,就跟注释一样let s3 = Symbol('尚硅谷');//这里的Symbol是一个函数console.log(s2 ===s3);//false,里面的字符串只是一个标志,他们所返回的结果是不一样的//Symbol.for 创建let s4 = Symbol.for('尚硅谷');//此时Symbol是一个对象//通过这种方式创建的话我们是可以描述字符串得出唯一的Symbol值的let s5 = Symbol.for('尚硅谷');console.log(s5, typeof s5);console.log(s4 ===s5);//true//不能与其他数据进行运算//全部报错为:Uncaught TypeError: Cannot convert a Symbol value to a number//    let result = s + 100; //    let result = s > 100;//    let result = s + s;// js的七种数据类型://   undefined//   string  symbol//   object//   null number//   boolean</script>
</body>
</html>

对象添加Symbol类型的属性
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Symbol 创建对象属性</title>
</head>
<body><script>//向对象中添加 up down 方法let game = {name:'俄罗斯方块',up: function(){},down: function(){}};//声明一个对象,这个对象里有两个属性,对应的都是Symbol类型的值let methods = {up: Symbol(),down: Symbol()};// 我们可以向game中添加方法game[methods.up] = function(){console.log("我可以改变形状");}game[methods.down] = function(){console.log("我可以快速下降!!");}console.log(game);//添加Symbol类型的属性let youxi = {name:"狼人杀",// 直接 Symbol() 是不可以的,因为这是表达式,是一个动态的值,//不是一个固定的属性,所以我们应该直接 [Symbol()] 这样的方式来添加属性,//当然我们也可以给他添加一个描述字符串[Symbol('say')]: function(){console.log("我可以发言")},[Symbol('zibao')]: function(){console.log('我可以自爆');}}console.log(youxi)       </script>
</body>
</html>

2.9.2.Symbol 内置值

除了定义自己使用的 Symbol 值以外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法。可以称这些方法为魔术方法,因为它们会在特定的场 景下自动执行。

这些都是Symbol的属性;Symbol.hasInstance  当其他对象使用 instanceof 运算符,判断是否为该对象的实例时,会调用这个方法Symbol.isConcatSpreadable  对象的 Symbol.isConcatSpreadable 属性等于的是一个布尔值,表示该对象用于Array.prototype.concat()时,是否可以展开。Symbol.species  创建衍生对象时,会使用该属性
Symbol.match    当执行 str.match(myObject) 时,如果该属性存在,会调用它,返回该方法的返回值。Symbol.replace    当该对象被 str.replace(myObject)方法调用时,会返回该方法的返回值。Symbol.search    当该对象被 str.search (myObject)方法调用时,会返回该方法的返回值。Symbol.split 当该对象被 str.split(myObject)方法调用时,会返回该方法的返回值。Symbol.iterator 对象进行 for...of 循环时,会调用 Symbol.iterator 方法,返回该对象的默认遍历器Symbol.toPrimitive   该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。Symbol. toStringTag 在该对象上面调用 toString 方法时,返回该方法的返回值Symbol. unscopables 该对象指定了使用 with 关键字时,哪些属性会被 with环境排除。

代码演示:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Symbol内置属性</title>
</head>
<body><script>class Person{// 跟普通的方法不一样,他有自己的自动执行场景,而且还可以把参数传进来,static [Symbol.hasInstance](param){//把instanceof前面的值传递给这个方法,并由他来决定何时执行console.log(param);console.log("我被用来检测类型了");return false;// return true;}}let o = {};console.log(o instanceof Person);const arr = [1,2,3];const arr2 = [4,5,6];console.log(arr.concat(arr2));//表示arr2不展开,作为一个整体arr2[Symbol.isConcatSpreadable] = false;  console.log(arr.concat(arr2));</script>
</body>
</html>

2.10. 迭代器

遍历器(Iterator)就是一种机制。它是一种接口,为各种不同的数据结构提 供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。

  1. ES6 创造了一种新的遍历命令 for…of 循环,Iterator 接口主要供 for…of 消费

  2. 原生具备 iterator 接口的数据(可用 for of 遍历)

​ a) Array

​ b) Arguments

​ c) Set

​ d) Map

​ e) String

​ f) TypedArray

​ g) NodeList

  1. 工作原理

​ a) 创建一个指针对象,指向当前数据结构的起始位置

​ b) 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员

​ c) 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员

​ d) 每调用 next 方法返回一个包含 value 和 done 属性的对象

注: 需要自定义遍历数据的时候,要想到迭代器。

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>迭代器</title>
</head>
<body><script>//声明一个数组const xiyou = ['唐僧','孙悟空','猪八戒','沙僧'];console.log(xiyou);//使用 for...of 遍历数组,保存的是键值for(let v of xiyou){console.log(v);}// 使用 for...in 遍历数组,保存的是键名for(let vv in xiyou){console.log(vv);}//原理实现//Symbol.iterator为xiyou数组原型的方法,//xiyou[Symbol.iterator]对应的是一个函数,加个()得到let iterator = xiyou[Symbol.iterator]();console.log(iterator);//调用对象的next方法console.log(iterator.next());console.log(iterator.next());console.log(iterator.next());console.log(iterator.next());console.log(iterator.next());</script>
</body>
</html>

迭代器应用:自定义遍历数据

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>自定义遍历数据</title>
</head><body><script>//声明一个对象const banji = {name: "终极一班",stus: ['xiaoming','xiaoning','xiaotian','knight'],// 添加接口[Symbol.iterator]() {//索引变量let index = 0;let _this = this;return {next: function () {if (index < _this.stus.length) {const result = { value: _this.stus[index], done: false };//下标自增index++;//返回结果return result;}else{//表示遍历结束return {value: undefined, done: true};}}};}}//遍历这个对象 for (let v of banji) {console.log(v);}</script>
</body></html>

2.11. 生成器

生成器函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同

function * gen(){yield '一只没有耳朵';yield '一只没有尾巴';return '真奇怪';
}
let iterator = gen();console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());代码说明:
1) * 的位置没有限制
2) 生成器函数返回的结果是迭代器对象,调用迭代器对象的 next 方法可以得到 yield 语句后的值
3) yield 相当于函数的暂停标记,也可以认为是函数的分隔符,每调用一次 next 方法,执行一段代码
4) next 方法可以传递实参,作为 yield 语句的返回值

代码演示:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>生成器</title>
</head>
<body><script>    //生成器其实就是一个特殊的函数//异步编程  纯回调函数  node fs  ajax mongodbfunction * gen(){console.log(111);yield '一只没有耳朵';//yield为函数代码的分隔符console.log(222);yield '一只没有尾部';console.log(333);yield '真奇怪';console.log(444);}let iterator = gen();// iterator.next();//输出为111// iterator.next();//输出为222// iterator.next();//输出为333// iterator.next();//输出为444console.log(iterator.next());console.log(iterator.next());console.log(iterator.next());console.log(iterator.next());//遍历for(let v of gen()){console.log(v);}</script>
</body>
</html>

生成器函数参数

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>生成器函数参数</title>
</head>
<body><script>function * gen(arg){console.log(arg);//输出为AAAlet one = yield 111;console.log(one);let two = yield 222;console.log(two);let three = yield 333;console.log(three);}//执行获取迭代器对象let iterator = gen('AAA');console.log(iterator.next());//第一个next方法传参没有结果//next方法可以传入实参//传的这个参数将作为上一个yield的返回结果,这里为oneconsole.log(iterator.next('BBB'));console.log(iterator.next('CCC'));console.log(iterator.next('DDD'));      </script>
</body>
</html>

生成器函数实例1

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>生成器函数实例</title>
</head><body><script>// 异步编程  文件操作 网络操作(ajax, request) 数据库操作// 1s 后控制台输出 111  2s后输出 222  3s后输出 333 // 回调地狱// setTimeout(() => {//     console.log(111);//     setTimeout(() => {//         console.log(222);//         setTimeout(() => {//             console.log(333);//         }, 3000);//     }, 2000);// }, 1000);function one(){setTimeout(()=>{console.log(111);iterator.next();//执行完这个后执行下一个},1000)}function two(){setTimeout(()=>{console.log(222);iterator.next();},2000)}function three(){setTimeout(()=>{console.log(333);iterator.next();},3000)}function * gen(){yield one();yield two();yield three();}//调用生成器函数let iterator = gen();iterator.next();</script>
</body>
</html>

生成器函数实例2

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>生成器函数</title>
</head>
<body><script>//模拟获取  用户数据  订单数据  商品数据 function getUsers(){setTimeout(()=>{let data = '用户数据';//调用 next 方法, 并且将数据传入//这是第二个next方法,他的实参将作为第一个yield语句的返回结果,//我们在下方声明一个变量接收,获取用户数据iterator.next(data);}, 1000);}function getOrders(){setTimeout(()=>{let data = '订单数据';iterator.next(data);}, 1000)}function getGoods(){setTimeout(()=>{let data = '商品数据';iterator.next(data);}, 1000)}function * gen(){//第一秒输出用户数据,第二秒输出订单数据,第三秒输出商品数据let users = yield getUsers();console.log(users);let orders = yield getOrders();console.log(orders);let goods = yield getGoods();console.log(goods);}//调用生成器函数let iterator = gen();iterator.next();//执行第一个yield,并把执行结果返回   </script>
</body>
</html>

2.12. Promise

Promise 是 ES6 引入的异步编程的新解决方案。语法上 Promise 是一个构造函数,
用来封装异步操作并可以获取其成功或失败的结果。 1) Promise 构造函数: Promise (excutor) {} 2) Promise.prototype.then 方法 3) Promise.prototype.catch 方法

Promise基本语法

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Promise基本语法</title>
</head>
<body><script>//实例化 Promise 对象const p = new Promise(function(resolve, reject){setTimeout(function(){//let data = '数据库中的用户数据';// 调用resolve函数//当调用这个后,promise对象p的状态就会变为成功,//有三个状态,分别为初始化、成功、失败resolve(data);let err = '数据读取失败';// 调用rejectreject(err);}, 1000);});//调用 promise 对象的 then 方法,接收两个参数,//第一个函数表示成功,第二个函数表示失败p.then(function(value){console.log(value);//数据库中的用户数据}, function(reason){console.error(reason);})</script>
</body>
</html>

Promise封装读取文件

//1. 引入 fs 模块
const fs = require('fs');//2. 第一种方法:调用方法读取文件
// fs.readFile('./resources/为学.md', (err, data)=>{//     //如果失败, 则抛出错误
//     if(err) throw err;
//     //如果没有出错, 则输出内容
//     console.log(data.toString());
// });//3. 第二种方法:使用 Promise 封装读取文件
const p = new Promise(function(resolve, reject){fs.readFile("./resources/为学.md", (err, data)=>{//判断如果失败if(err) reject(err);//如果成功resolve(data);});
});p.then(function(value){console.log(value);//使用toString将值转换为String类型,不使用的话console.log(value.toString());
}, function(reason){console.log("读取失败!!");
});

Promise封装ajax请求

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>发送 AJAX 请求</title>
</head><body><script>// 接口地址: https://api.apiopen.top/getJokeconst p = new Promise((resolve, reject) => {//1. 创建对象const xhr = new XMLHttpRequest();//2. 初始化//给 https://api.apiopen.top/getJ 发送 get 类型请求,xhr.open("GET", "https://api.apiopen.top/getJ");//3. 发送xhr.send();//4. 绑定事件, 处理响应结果xhr.onreadystatechange = function () {//判断状态if (xhr.readyState === 4) {//判断响应状态码 200-299都是表示成功if (xhr.status >= 200 && xhr.status < 300) {//表示成功//调用上方resolve这个函数来修改promise的状态为成功,//xhr.response表示正常输出resolve(xhr.response);} else {//如果失败//调用上方reject这个函数来修改promise的状态为失败reject(xhr.status);}}}})//指定回调p.then(function(value){console.log(value);}, function(reason){console.error(reason);});</script>
</body>
</html>

Promise.prototype.then方法

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Promise.prototype.then</title>
</head><body><script>//创建 promise 对象const p = new Promise((resolve, reject) => {setTimeout(() => {resolve('用户数据');// reject('出错啦');}, 1000)});//调用 then 方法  then方法的返回结果是 Promise 对象, 对象状态由回调函数的执行结果决定const result = p.then(value => {console.log(value);// 不写 return ,函数内部默认返回结果为undefined}, reason => {console.warn(reason);});console.log(result);const result2 = p.then(value => {console.log(value);//1. 非 promise 类型的属性return 'iloveyou';}, reason => {console.warn(reason);});console.log(result2);//1. 如果回调函数中返回的结果是 非 promise 类型的属性, 状态为成功, 返回值为对象的成功的值const result3 = p.then(value => {console.log(value);//2. 是 promise 对象return new Promise((resolve, reject)=>{// resolve('ok');reject('error');});}, reason=>{console.warn(reason);});console.log(result3);const result4 = p.then(value => {console.log(value);//3. 抛出错误throw new Error('出错啦!');// throw '出错啦!';}, reason=>{console.warn(reason);});console.log(result4);//链式调用// p.then(value=>{// },reason =>{// }).then(value=>{// },reason =>{// });</script>
</body></html>

Promise实践:读取多个文件

//引入 fs 模块
const fs = require("fs");// 回调地狱的方式,这种我们通常用于用户与订单的层层递进关系
// fs.readFile('./resources/为学.md', (err, data1)=>{//     fs.readFile('./resources/插秧诗.md', (err, data2)=>{//         fs.readFile('./resources/观书有感.md', (err, data3)=>{//             let result = data1 + '\r\n' +data2  +'\r\n'+ data3;  //\r\n表示换行
//             console.log(result);
//         });
//     });
// });//使用 promise 实现
const p = new Promise((resolve, reject) => {fs.readFile("./resources/为学.md", (err, data) => {resolve(data);//resolve可以改变promise的状态变为成功});
});p.then(value => {//value是第一个文件内容return new Promise((resolve, reject) => {fs.readFile("./resources/插秧诗.md", (err, data) => {//data是第二个文件内容resolve([value, data]);});});
}).then(value => {//这里的value变为第一个文件和第二个文件形成的数组return new Promise((resolve, reject) => {fs.readFile("./resources/观书有感.md", (err, data) => {//data是第三个文件//压入value.push(data);//将第三个文件加入,此时value为三个文件的结果//resolve成功,则new的promise又成功了,然后then方法返回的方法有成功了,//它一成功我们再来一个then方法resolve(value);});})
}).then(value => {//再一个then方法console.log(value.join('\r\n'));//为一个数组,用join拼接,用\r\n拼接
});

Promise.prototype.catch方法

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>catch方法</title>
</head>
<body><script>// catch方法用来指定promise失败的回调const p = new Promise((resolve, reject)=>{setTimeout(()=>{//设置 p 对象的状态为失败, 并设置失败的值reject("出错啦!");}, 1000)});// 第一种方法:通过then方法// p.then(function(value){}, function(reason){//     console.error(reason);// });// 第二种方法:catch方法,失败的回调p.catch(function(reason){console.warn(reason);});</script>
</body>
</html>

2.13. Set(集合)

ES6 提供了新的数据结构 Set(集合)。它类似于数组,但成员的值都是唯 一的,
集合实现了 iterator 接口,所以可以使用『扩展运算符』和『for…of…』进 行遍历,集合的属性和方法: 1) size 返回集合的元素个数 2) add 增加一个新元素,返回当前集合 3) delete 删除元素,返回 boolean 值 4) has 检测集合中是否包含某个元素,返回 boolean 值 5) clear 清空集合,返回 undefined
//创建一个空集合
let s = new Set();//创建一个非空集合
let s1 = new Set([1,2,3,1,2,3]);//集合属性与方法
//返回集合的元素个数
console.log(s1.size);//添加新元素
console.log(s1.add(4));//删除元素
console.log(s1.delete(1));//检测是否存在某个值
console.log(s1.has(2));//清空集合
console.log(s1.clear());

代码演示:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>集合</title>
</head>
<body><script>//声明一个 setlet s = new Set();let s2 = new Set(['大事儿','小事儿','好事儿','坏事儿','小事儿']);console.log(s2,typeof s2);//会自动去重//元素个数console.log(s2.size);//添加新的元素s2.add('喜事儿');console.log(s2.size);//删除元素s2.delete('坏事儿');console.log(s2.size);//检测console.log(s2.has('糟心事'));//清空// s2.clear();console.log(s2);// 集合实现了 iterator 接口,所以我们可以实现遍历for(let v of s2){console.log(v);}</script>
</body>
</html>

集合实践

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Set 实践</title>
</head>
<body><script>let arr = [1,2,3,4,5,4,3,2,1];//1. 数组去重let result = new Set(arr);//result为一个集合let result2 = [...new Set(arr)];//通过扩展运算符...将集合变为数组console.log(result);console.log(result2);//2. 交集let arr2 = [4,5,6,5,6];//[...new Set(arr)] 表示变为一个去重之后的数组,然后调用filter方法 迭代(遍历)数组元素let result3 = [...new Set(arr)].filter(item => {let s2 = new Set(arr2);// 4 5 6if(s2.has(item)){//如果s2集合中存在这个元素,即为交集return true;}else{return false;}});// 对上面代码的简化// let result3 = [...new Set(arr)].filter(item => new Set(arr2).has(item));console.log(result3);//3. 并集//[...arr, ...arr2]表示将数组合并,然后通过new Set去重,最后通过扩展运算符...将集合变为数组let union = [...new Set([...arr, ...arr2])];console.log(union);//4. 差集,差集是相对来说的,比如arr中为1,2,3,arr2中为3,4,5,//则arr和arr2的差集为1,2,反过来arr2和arr的差集为4,5let diff = [...new Set(arr)].filter(item => !(new Set(arr2).has(item)));console.log(diff);let diff2 = [...new Set(arr2)].filter(item => !(new Set(arr).has(item)));console.log(diff2);</script>
</body></html>

2.14. Map

ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合。但是“键”的范围不限于字符串,
各种类型的值(包括对象)都可以当作键。Map 也实现了iterator 接口,所以可以使用
『扩展运算符』和『for…of…』进行遍历。Map 的属性和方法:1) size 返回 Map 的元素个数2) set 增加一个新元素,返回当前 Map3) get 返回键名对象的键值4) has 检测 Map 中是否包含某个元素,返回 boolean 值5) clear 清空集合,返回 undefined
//创建一个空 map
let m = new Map();//创建一个非空 map
let m2 = new Map([['name','尚硅谷'],['slogon','不断提高行业标准']
]);//属性和方法
//获取映射元素的个数
console.log(m2.size);//添加映射值
console.log(m2.set('age', 6));//获取映射值
console.log(m2.get('age'));//检测是否有该映射
console.log(m2.has('age'));//清除
console.log(m2.clear());

代码演示:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Map</title>
</head>
<body><script>//声明 Maplet m = new Map();//添加元素m.set('name','尚硅谷');//键为name,值为尚硅谷m.set('change', function(){console.log("我们可以改变你!!");});let key = {school : 'ATGUIGU'};//键是对象,值是数组 ['北京','上海','深圳']m.set(key, ['北京','上海','深圳']);console.log(m);//size表示获取Map中的元素个数console.log(m.size);//删除// m.delete('name');//获取//get方法,在参数里传入键,获取值console.log(m.get('change'));console.log(m.get(key));//清空// m.clear();//遍历for(let v of m){console.log(v);}</script>
</body>
</html>

2.15. class 类

 ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过 class 关键字,可以定义类。基本上,ES6 的 class 可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。知识点:1) class 声明类2) constructor 定义构造函数初始化3) extends 继承父类4) super 调用父级构造方法5) static 定义静态方法和属性6) 父类方法可以重写
//父类class Phone {//构造方法constructor(brand, color, price) {this.brand = brand;this.color = color;this.price = price;}//对象方法call() {console.log('我可以打电话!!!')}}//子类class SmartPhone extends Phone {constructor(brand, color, price, screen, pixel) {super(brand, color, price);this.screen = screen;this.pixel = pixel;}//子类方法photo() {console.log('我可以拍照!!');}playGame() {console.log('我可以玩游戏!!');}//方法重写call() {console.log('我可以进行视频通话!!');}//静态方法static run() {console.log('我可以运行程序')}static connect() {console.log('我可以建立连接')}}//实例化对象const Nokia = new Phone('诺基亚', '灰色', 230);const iPhone6s = new SmartPhone('苹果', '白色', 6088,'4.7inch', '500w');//调用子类方法iPhone6s.playGame();//调用重写方法iPhone6s.call();//调用静态方法SmartPhone.run();

class语法

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>类声明</title>
</head>
<body><script>// es5的写法,通过构造函数实例化对象//以手机为例子,function Phone(brand, price){//表示对实例对象的属性作初始化this.brand = brand;this.price = price;this.sing = function(){//不推荐这样直接添加方法,我们一般用原型对象添加console.log("我会唱歌");}}//添加方法,我们通过原型对象去添加,这样可以做到重用Phone.prototype.call = function(){console.log("我可以打电话!!");}//实例化对象let Huawei = new Phone('华为', 5999);Huawei.call();Huawei.sing();console.log(Huawei);//es6的写法,通过classclass Shouji{//构造方法 constructor名字不能修改,是固定的,我们通过//new + 类名 的时候就会自动执行实例对象constructor方法constructor(brand, price){this.brand = brand;this.price = price;}//方法必须使用该语法, 不能使用 ES5 的对象完整形式,例如call: function(){}call(){console.log("我可以打电话!!");}}let onePlus = new Shouji("1+", 1999);console.log(onePlus);</script>
</body>
</html>

class静态成员(static标注)

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>静态成员</title>
</head>
<body><script>function Phone(){}//我们直接在函数对象上添加属性和方法,但是要注意这是属于函数对象的,它并不属于实例对象Phone.name = '手机';Phone.change = function(){console.log("我可以改变世界");}Phone.prototype.size = '5.5inch';let nokia = new Phone();//输出为undefined,我们不能使用Phone上直接添加的属性和方法console.log(nokia.name);// nokia.change();//报错,没有这个方法//不会报错,输出为 5.5inch ,我们通过给Phone函数的原型对象添加方法console.log(nokia.size);class Phone2{//静态属性//static标注的属性和方法,它属于类而不属于实例对象static name = '手机';static change(){console.log("我可以改变世界");}}let nokia2 = new Phone2();console.log(nokia2.name);//undefinedconsole.log(Phone2.name);//手机</script>
</body>
</html>

es5构造函数继承

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>对象继承</title>
</head>
<body><script>//手机function Phone(brand, price){this.brand = brand;this.price = price;}Phone.prototype.call = function(){console.log("我可以打电话");}//智能手机function SmartPhone(brand, price, color, size){//通过call改变Phone中this值,让这个this指向SmartPhone中的this,//也就是SmartPhone的实例对象Phone.call(this, brand, price);this.color = color;this.size = size;}//设置子级构造函数的原型//SmartPhone的实例对象上就会存在Phone上的方法SmartPhone.prototype = new Phone;//矫正,让constructor重新指向SmartPhoneSmartPhone.prototype.constructor = SmartPhone;//声明子类的方法SmartPhone.prototype.photo = function(){console.log("我可以拍照")}SmartPhone.prototype.playGame = function(){console.log("我可以玩游戏");}const chuizi = new SmartPhone('锤子',2499,'黑色','5.5inch');console.log(chuizi);</script>
</body>
</html>

es6中class的继承

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>类继承-2</title>
</head>
<body><script>class Phone{//构造方法constructor(brand, price){this.brand = brand;this.price = price;}//父类的成员属性call(){console.log("我可以打电话!!");}}class SmartPhone extends Phone {//构造方法constructor(brand, price, color, size){// super表示父类的constructor的方法super(brand, price);this.color = color;//子类属性的初始化this.size = size;}photo(){console.log("拍照");}playGame(){console.log("玩游戏");}// 子类对父类方法的重写call(){//我们想调用父类的同名方法,这样写是错误的,在普通的成员方法//里不可以出现super去调用父类的同名方法// super();console.log('我可以进行视频通话');}}const xiaomi = new SmartPhone('小米',799,'黑色','4.7inch');console.log(xiaomi);xiaomi.call();xiaomi.photo();xiaomi.playGame();</script>
</body>
</html>

class中的getter和setter设置

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>get 和 set</title>
</head>
<body><script>// get 和 set  ,es5中可以对对象的属性进行方法的绑定,//当对某个属性进行获取时执行get这个函数,当对某个属性进行设置时执行set这个函数class Phone{get price(){console.log("价格属性被读取了");//如果不加返回值,则输出为undefined,因为函数默认的返回值为undefinedreturn 'iloveyou';}// set必须要有一个参数,没有会报错set price(newVal){console.log('价格属性被修改了');}}//实例化对象let s = new Phone();console.log(s.price);s.price = 'free';</script>
</body>
</html>

2.16. 数值扩展

2.16.1. 二进制和八进制

ES6 提供了二进制和八进制数值的新的写法,分别用前缀 0b 和 0o 表示。

2.16.2. Number.isFinite() 与 Number.isNaN()

Number.isFinite() 用来检查一个数值是否为有限的

Number.isNaN() 用来检查一个值是否为 NaN

2.16.3. Number.parseInt() 与 Number.parseFloat()

ES6 将全局方法 parseInt 和 parseFloat,移植到 Number 对象上面,使用不变。

2.16.4. Math.trunc

用于去除一个数的小数部分,返回整数部分。

2.16.5. Number.isInteger

Number.isInteger() 用来判断一个数值是否为整数

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>数值扩展</title>
</head>
<body><script>//0. Number.EPSILON 是 JavaScript 表示的最小精度//EPSILON 属性的值接近于 2.2204460492503130808472633361816E-16,//在js中,如果两个数的差值小于这个,则认为这两个数相等function equal(a, b){// Math.abs(a-b)表示求差值的绝对值if(Math.abs(a-b) < Number.EPSILON){return true;}else{return false;}}console.log(0.1 + 0.2);//浮点运算,实际计算的值为 0.30000000000000004console.log(0.1 + 0.2 === 0.3);//falseconsole.log(equal(0.1 + 0.2, 0.3))//true//1. 二进制和八进制let b = 0b1010;//二进制let o = 0o777;//八进制let d = 100;//十进制let x = 0xff;//十六进制console.log(b);//10console.log(o);//511console.log(d);//100console.log(x);//255//2. Number.isFinite  检测一个数值是否为有限数console.log(Number.isFinite(100));//trueconsole.log(Number.isFinite(100/0));//false//false, Infinity表示无穷,分为正无穷+Infinity和负无穷-Infinityconsole.log(Number.isFinite(Infinity));//3. Number.isNaN 检测一个数值是否为 NaN // 在es5中 isNaN 是一个单独的函数,在es6中作为了number的一个方法console.log(Number.isNaN(123)); //false//4. Number.parseInt Number.parseFloat字符串转整数console.log(Number.parseInt('5211314love'));//5211314console.log(Number.parseFloat('3.1415926神奇'));//3.1415926//5. Number.isInteger 判断一个数是否为整数console.log(Number.isInteger(5));//trueconsole.log(Number.isInteger(2.5));//false//6. Math.trunc 将数字的小数部分抹掉  console.log(Math.trunc(3.5));//3//7. Math.sign 判断一个数到底为正数 负数 还是零,如果是正数返回1,0返回0,负数返回-1console.log(Math.sign(100));//1console.log(Math.sign(0));//0console.log(Math.sign(-20000));//-1</script>
</body>
</html>

2.17. 对象方法扩展

ES6 新增了一些 Object 对象的方法1) Object.is 比较两个值是否严格相等,与『===』行为基本一致(+0 与 NaN)2) Object.assign 对象的合并,将源对象的所有可枚举属性,复制到目标对象3) __proto__、setPrototypeOf、 setPrototypeOf 可以直接设置对象的原型

代码演示:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>对象方法扩展</title>
</head>
<body><script>//1. Object.is 判断两个值是否完全相等,它跟全等有些类似,但是要注意NaN的比较是不同的 console.log(Object.is(120, 120));//trueconsole.log(Object.is(NaN, NaN));//true console.log(NaN === NaN);//false,NaN跟任何一个做是否相等比较,返回结果都为false,//2. Object.assign 对象的合并const config1 = {host: 'localhost',port: 3306,name: 'root',pass: 'root',test: 'test'};const config2 = {host: 'http://atguigu.com',port: 33060,name: 'atguigu.com',pass: 'iloveyou',test2: 'test2'}//后面的会把前面的所覆盖掉,但是要注意只是覆盖重名的,不重名的不会覆盖console.log(Object.assign(config1, config2));//3. Object.setPrototypeOf 设置原型对象,//不推荐使用这样设置原型  Object.getPrototypeof 获取原型对象const school = {name: '尚硅谷'}const cities = {xiaoqu: ['北京','上海','深圳']}Object.setPrototypeOf(school, cities);//为school对象设置原型console.log(Object.getPrototypeOf(school));//获取原型console.log(school);</script>
</body>
</html>

2.18. 模块化

模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。

2.18.1. 模块化的好处

模块化的优势有以下几点: 1) 防止命名冲突 2) 代码复用 3) 高维护性

2.18.2. 模块化规范产品

ES6 之前的模块化规范有:es6之前js没有模块化 1) CommonJS => NodeJS、Browserify 2) AMD => requireJS 3) CMD => seaJS

2.18.3. ES6 模块化语法

模块功能主要由两个命令构成:export 和 import。 export 命令用于规定模块的对外接口  import 命令用于输入其他模块提供的功能

代码演示:

m1.js
//这种写法称为 分别暴露
export let school = '尚硅谷';export function teach() {console.log("我们可以教给你开发技能");
}
m2.js
let school = '尚硅谷';function findJob(){console.log("我们可以帮助你找工作!!");
}//统一暴露
export {school, findJob};
m3.js
//默认暴露
export default {school: 'ATGUIGU',change: function(){console.log("我们可以改变你!!");}
}
app.js
//入口文件//模块引入
import * as m1 from "./m1.js";
import * as m2 from "./m2.js";
import * as m3 from "./m3.js";// console.log(m1);
// console.log(m2);
// console.log(m3);// m1.teach();
// m2.findJob();
// m3.default.change();//此部分对应home.html
//修改背景颜色为粉色
// es6导入 npm 包的语法,此次导入jQuery的包,下载jQuery为 npm i jquery
import $ from 'jquery';// const $ = require("jquery");
$('body').css('background','pink');
index.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>ES6 模块化</title>
</head>
<body><!-- 以前的写法 --><!-- <script src="./src/js/m1.js"></script> --><script type="module">//第一种导入方式: 通用的导入方式//引入 m1.js 模块内容//引入m1.js这个模块中所有暴露的数据(前面加了export的)存入m1这个变量中import * as m1 from "./src/js/m1.js"; console.log(m1);// 引入 m2.js 模块内容import * as m2 from "./src/js/m2.js";console.log(m2);// 引入 m3.js import * as m3 from "./src/js/m3.js";console.log(m3);m3.default.change();//默认暴露要加上 default 调用//第二种导入方式: 解构赋值形式//我们可以直接使用这两个变量import {school, teach} from "./src/js/m1.js";console.log(school,teach);//如果js模块的命名在引入时重名了,我们直接 as 命名import {school as guigu, findJob} from "./src/js/m2.js";console.log(guigu);//引入默认暴露时必须对default进行重命名import {default as m33} from "./src/js/m3.js";console.log(m33);//第三种导入方式: 简便形式  只能针对默认暴露import m333 from "./src/js/m3.js";//直接写变量console.log(m333);</script><!-- 像上面这样一个一个的引入太过于繁琐,我们可以直接设置一个js入口文件,并在其中引入所有的js文件,然后再在html文档中引入这个js文件 --><!-- <script src="./src/js/app.js" type="module"></script> -->
</body>
</html>
home.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><!-- 1. 安装工具 npm i babel-cli babel-preset-env browserify(webpack) -D2. 编译 npx babel src/js -d dist/js --presets=babel-preset-env3. 打包 npx browserify dist/js/app.js -o dist/bundle.jsbabel-cli           是babel命令行的一个工具babel-preset-env    将最新的es语法转换为es5的语法browserify          打包工具-D                  表示局部安装, -g 为全局安装,如果为全局安装就不用再前面加 npx 了-o                  后面的表示输出路径--><script src="dist/bundle.js"></script>
</body>
</html>

2.19 补充

a.js

export const name = 'jack';
export const age = 18;

b.js

// 引入a.js的所有内容并重命名为a , a 实际上是一个对象,有name属性和age属性
import * as a from './a';// 把a对象再作为对象暴露出去
export { a };

index.js

import * as b from './b';// 拿到name的值
console.log(b.a.name);  // jack

【ES6】export default 和 export 区别

export default 和 export 区别:1.export与export default均可用于导出常量、函数、文件、模块等2.你可以在其它文件或模块中通过import + (常量 | 函数 | 文件 | 模块)名的方式,将其导入,以便能够对其进行使用3.在一个文件或模块中,export、import可以有多个,export default仅有一个4.通过export方式导出,在导入时要加{ },export default则不需要
1.export//a.js
export const str = "blablabla~";
export function log(sth) { return sth;
}
对应的导入方式://b.js
import { str, log } from 'a'; //也可以分开写两次,导入的时候带花括号2.export default//a.js
const str = "blablabla~";
export default str;
对应的导入方式://b.js
import str from 'a'; //导入的时候没有花括号

使用export default命令,为模块指定默认输出,这样就不需要知道所要加载模块的变量名

//a.js
let sex = "boy";
export default sex(sex不能加大括号{})原本直接export sex外部是无法识别的,加上default就可以了.但是一个文件内最多只能有一个export default。其实此处相当于为sex变量值"boy"起了一个系统默认的变量名default,自然default只能有一个值,
所以一个文件内不能有多个export default。
// b.js
本质上,a.js文件的export default输出一个叫做default的变量,然后系统允许你为它取任意名字。
所以可以为import的模块起任何变量名,且不需要用大括号包含import any from "./a.js"
import any12 from "./a.js"
console.log(any,any12)   // boy,boy

【ES6】var、let、const的区别

ECMAScript 和 JavaScript 到底是什么关系?ECMAScript是一个国际通过的标准化脚本语言。JavaScript由ECMAScript和DOM、BOM三者组成。可以简单理解为:ECMAScript是JavaScript的语言规范,JavaScript是ECMAScript的实现和扩展。2011 年,ECMAScript 5.1 版发布。之前我们大部分人用的也就是ES52015 年 6 月,ECMAScript 6 正式通过,成为国际标准。

1. 块级作用域 {}

​ ES5 中作用域有:全局作用域、函数作用域。没有块作用域的概念。

​ ES6 中新增了块级作用域。块作用域由{ }包括,if语句和 for语句里面的{ }也属于块作用域。

<script type="text/javascript"> {var a = 1;console.log(a); // 1}// 通过var定义的变量可以跨块作用域访问到。console.log(a); // 1(function A() {var b = 2;console.log(b); // 2})();// 可见,通过var定义的变量不能跨函数作用域访问到// console.log(b); // 报错,// if语句和for语句中用var定义的变量可以在外面访问到,// 可见,if语句和for语句属于块作用域,不属于函数作用域。 if(true) {var c = 3;}console.log(c); // 3for(var i = 0; i < 4; i ++) {var d = 5;};console.log(i); // 4   (循环结束i已经是4,所以此处i为4)console.log(d); // 5
</script>

2. var、let、const的区别

  1. var定义的变量,没有块的概念,可以跨块访问, 不能跨函数访问。
  2. let定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问。
  3. const用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改。但是变量的值依旧可以改变。
const person = {name:"xiaowang"
}person.name="laowang" //依旧可以修改
person = {} //报错,不能被重新赋值
<script type="text/javascript">// 块作用域{var a = 1;let b = 2;const c = 3;// c = 4; // 报错,不能修改var aa;let bb;// const cc; // 报错,必须赋值console.log(a); // 1console.log(b); // 2console.log(c); // 3console.log(aa); // undefinedconsole.log(bb); // undefined}console.log(a); // 1// 报错,let定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问。// console.log(b); // 报错,const用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改。// console.log(c); // 函数作用域(function A() {var d = 5;let e = 6;const f = 7;console.log(d); // 5console.log(e); // 6  console.log(f); // 7 })();// 报错,var定义的变量,没有块的概念,可以跨块访问, 不能跨函数访问。// console.log(d); // 报错,let定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问。// console.log(e); // 报错,const用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改。// console.log(f);
</script>

3. const定义的对象属性是否可以改变

 const person = {name : 'jiuke',sex : '男'}person.name = 'test'console.log(person.name)  // test

运行上述代码,发现person对象的name属性确实被修改了,这是怎么回事呢?

因为对象是引用类型的,person中保存的仅是对象的指针,这就意味着,const仅保证指针不发生改变,修改对象的属性不会改变对象的指针,所以是被允许的。也就是说const定义的引用类型只要指针不发生改变,其他的不论如何改变都是允许的。

然后我们试着修改一下指针,让person指向一个新对象,果然报错

const person = {name : 'jiuke',sex : '男'
}
// 报错
person = {name : 'test',sex : '男'
}

源码获取

至此,我们的JavaScript进阶(四)就讲解完成了。下篇将基于尚硅谷2020版Web前端ES6教程,涵盖ES6-ES11进行讲解ES7、ES8、ES9、ES10、ES11新特性源码素材可以通过关注我的微信公众号我爱学习呀嘻嘻 ,回复关键字JavaScript进阶源码素材进行获取哦。

JavaScript进阶(五):ES7、ES8、ES9、ES10、ES11新特性

JavaScript进阶(一):类、对象、构造函数、原型对象、继承

JavaScript进阶(二):函数、this、严格模式、高阶函数、闭包、递归

JavaScript进阶(三):正则表达式、ES6语法、ES6内置对象

JavaScript入门链接:

JavaScript入门(一):JavaScript基础知识

JavaScript入门(二):结合代码讲解①

JavaScript入门(三):结合代码讲解②

JavaScript入门(四):结合代码讲解③

JavaScript入门(五):结合代码讲解④

JavaScript入门(六):结合代码讲解⑤

JavaScript进阶(四)相关推荐

  1. JavaScript进阶(二)

    JavaScript进阶(二) 2019版黑马程序员javaScript进阶面向对象ES6 122集教程,哔哩哔哩链接:https://www.bilibili.com/video/BV1Kt411w ...

  2. JavaScript进阶(三)

    JavaScript进阶(三) 2019版黑马程序员javaScript进阶面向对象ES6 122集教程,哔哩哔哩链接:https://www.bilibili.com/video/BV1Kt411w ...

  3. JavaScript进阶(一)

    JavaScript进阶(一) 2019版黑马程序员javaScript进阶面向对象ES6 122集教程,哔哩哔哩链接:https://www.bilibili.com/video/BV1Kt411w ...

  4. 自学JavaScript第四天- JS 进阶:AJAX Promise Canvas

    自学JavaScript第四天- JS 进阶:AJAX Promise Canvas AJAX 使用 XMLHttpRequest 使用 fetch() 方法 处理 AJAX 数据 安全限制 跨域方案 ...

  5. JavaScript进阶3-学习笔记

    文章目录 JavaScript进阶3-学习笔记 闭包 JavaScript进阶3-学习笔记 //组件封装 什么是封装? 将冗余的代码精简化 例如:写代码:a页面有一个函数,b页面也需要这个函数,很多人 ...

  6. JavaScript—进阶篇

    JavaScript-进阶(笔记) 第1章 系好安全带,准备启航 1-1 让你认识JS 第二章 你要懂的规则(JS基础语法) 2-1 什么是变量 2-2 给变量取个名字(变量命名) 2-3 确定你的存 ...

  7. javascript 进阶篇(集合)

    目录 第一章 系好安全带,准备启航 1-1 让你认识JS 任务 1-2 编程练习 任务 第二章 你要懂的规则(JS基础语法) 2-1 什么是变量 2-3 确定你的存在(变量声明) 任务 2-4 多样化 ...

  8. JavaScript 进阶篇的学习~

    ---恢复内容开始--- 让你认识JS 你知道吗,Web前端开发师需要掌握什么技术?也许你已经了解HTML标记(也称为结构),知道了CSS样式(也称为表示),会使用HTML+CSS创建一个漂亮的页面, ...

  9. AngularJS进阶(四十)创建模块、服务

    AngularJS进阶(四十)创建模块.服务 学习要点 使用模块构架应用 创建和使用服务 为什么要使用和创建服务与模块? 服务允许你打包可重用的功能,使之能在此应用中使用. 模块允许你打包可重用的功能 ...

最新文章

  1. FD.io/VPP — VPP Agent — 架构设计
  2. 用python画烟花-python 实现漂亮的烟花,樱花,玫瑰花
  3. 一款java代码生成器(我受够了加班),走起!
  4. HH SaaS电商系统的线上服务商品库存和采购设计
  5. 20190805:两个队列实现栈
  6. 实验1-4 输出三角形 (5 分)
  7. 入职地府后我成了人生赢家_129、新鬼入职|入职地府后我成了人生赢家|入职地府后我成了人生赢家章节阅读-龙族小说网...
  8. 《分布式计算云计算与大数据》第一章
  9. 匠心开岁月·车库创乾坤 —— 微软创新节暨中国创新车库开幕
  10. linux错误码分析
  11. 安装virtualbox快完成时立即回滚,并提示安装出现严重错误
  12. python中else是什么意思中文翻译_else是什么意思
  13. 无需任何配置,初学者必会的一键获取微信小程序源代码的快捷方法与工具使用
  14. MyBatis之如何解决数据库数据加密解密
  15. UAV-PPK数据处理软件使用
  16. 扩展 EdgeX Foundry 混合开发环境
  17. Win10系统路由器挂载的硬盘无法打开的解决方法
  18. 用友U8案例教程生产订单前台操作
  19. rabbitmq队列模式以及交换机模式
  20. 创建百度百科需要多少钱?全网天下告诉你

热门文章

  1. 联盛德W806-KIT开发板试用评测系列之二:ADC 功能使用与测试
  2. Daily Scrum Meeting 11.04
  3. JSD-2204-VueCLI-Day01
  4. 基于 Squid 实现爬虫代理服务
  5. 计算机机型分pc机和什么,三种等分的电脑打印纸一般指什么尺寸
  6. 2021年中国机动车、汽车和新能源汽车保有量及驾驶人和驾驶证业务办理情况分析「图」
  7. Python_删除TXT文件中不想要的内容
  8. 更新xcode至12.3,编译报错Building for iOS, but the linked and embedded framework ‘xxx.framework’ was buil...
  9. outlook邮箱收件服务器密码,微软邮箱(hotmail+outlook):应用密码获取+STARTTLS加密...
  10. C语言输入三角形三条边边长 算三角形面积