JavaScript不会?25分钟带你上手JavaScript ES5-ES6
前言:
全文总计10000字(不包含代码)主要讲述的是ES5的一些语法总结,以及ES6的新特性和部分代码的教学,预计阅读时间25分钟,预计跟练事件约为90分钟(理解+会用)
文章目录
- JavaScript不会?25分钟带你上手JavaScript ES5-ES6
-
- ES5新语法 -- 2009年出品
- JavaScript ES6新特性
ES5新语法 – 2009年出品
严格模式:JS提供了更多的报错,辅助程序员写出更健康的代码
- 开启方式,在脚本开头书写:
‘use strict’
- 具体功能
- 变量必须先声明
- 函数必须先声明再使用
- 函数中的this关键词:如果是window则改为undefined
- 对象书写的精确配置 – 菜鸟的鸡肋,高手的神器
- objecttigondedefinePrroperty
- writable 是否可重新赋值
- enumerable:是否可遍历
- configurable : 是否可重配
- value:值
- get:计算属性 – 使用时不要()就能触发
- set:监听器
- 保护对象的方式
- prexxxx:阻止扩展,阻止增加新的书写
- seal:阻止增删属性
- freeze:阻止增删改
JavaScript ES6新特性
JavaScript ES6的由来
- 2015年6约出品,属于JS的里程碑
- 新的作用域:把必须用let和const来声明变量
- 对应window全局作用域,用于存放自定义属性
- ES6之前:必须开局先写一个匿名函数自调用–来提供局部作用域
- 块级作用域:用{}配合let和const使用
- 对应函数作用域,取代的黎明函数作用域,可以提供为函数提供私有的变量
- let和const
- let:变化量,声明的变量后续可以重新赋值
- const:常量,声明时必须赋值,后续不可重新赋值
- 关于声明提升
- 有声明提升,暂存死区:暂时提升,存放在一个暂存死区里面,不能用,必须是 执行到
let / const
代码所在行以后才能用
作者本意:为了兼容之前的依赖声明提升的代码,作者不敢删除声明提升特性,用报错强制要求用户 必须先声明变量,再使用变量;如果先用变量就报错
- 模板字符串
-专为html代码拼接而生
- 支持字符串局部内容的替换 – ${}
- 支持换行操作
JavaScript ES6新特性
ES6也叫ES2015,是个里程碑的版本
吸收了各种先进编程语言的优点,增加了大量的新特性
推荐文档:JavaScript ES6讲解
let 和 const 命令
let 和 const 命令和var都是定义属性,let和const一个是变量和常量
- ES6新增了两个作用域–与之前的并称 4大作用域
- 全局作用域:window对象–储存系统API代码
- 局部作用域:函数运行时
临时
生成的作用域 - 脚本作用域:避免全局污染新开的作用域
- 块级作用域:用来代替ES6之前的匿名函数自调用的
JavaScript 作用域
是什么作用域 ?JavaScript万物皆为对象
作用域是具有特殊功能
的对象
es6之前的有两大作用域:全局作用域和局部作用域
global全局作用域:对象中存储了系统提供的API,用于操作宿主环境
声明提升
声明提升:是JS一个独有的一个不友好的设定
特点:JS代码在运行时,会把声明操作提升到最大作用域的顶部
问题:程序员远在阅读代码是,造成极大的困扰,不能顺序去读
比如:
<script>
console.log('a:',a);
var a
</script>
但是到了ES6之后
<script>
console.log('a:',a);
var a
</script>
翻译:
为什么会出现这种情况呢?是没有声明提升吗?
暂存死区:暂时提升,存放在一个暂存死区里面,不能用,必须是 执行到 let / const
代码所在行以后才能用
作者本意:为了兼容之前的依赖声明提升的代码,作者不敢删除声明提升特性,用报错强制要求用户 必须先声明变量,再使用变量;如果先用变量就报错
脚本作用域
如何给函数制作私有的变量?
ES6之前实现局部作用域
利用闭包的方式来制作
<script>
var add = (function () {
var num = 1;
// 属于匿名函数作用域的变量
return function () {
num++;
console.log("num: ", num);
};
})();
add();
add();
add();
</script>
ES6之后就为脚本作用域
// ES6之后
{
let num = 1;
function add1() {
num++;
console.log("num:", num);
}
}
add1();
add1();
add1();
解析:
{ }
配合let/const
使用,会形参一个块级作用域
本质就是简化了 匿名函数自调用
的语法(function(){})()
我们查看一下他的scopes
就会发现
宿主函数:寄生虫与数组的关系在这里就是浏览器,node与js的的关系
如果时nodejs为宿主,那全局对象就是globalThis
当宿主为浏览器的时候全局对象就是window
local
局部作用域:函数被调用后
的临时生成的对象,用于存储函数中声明的变量
没调用后之前就是静态
作用域链:
当出现多重作用域嵌套的场景——函数中套函数
当内层函数使用一变量时会按照就近原则,从长曾作用域中查找时用最近的那个变量
声明提升:JS独有的设计 – 非常垃圾
会造成写出来的代码产生的效果和原来的不一样
声明操作:
生明操作会被js的编辑器优先阅读并提升到,所在所用与的顶部,然后再去执行调整过顺序的代码;常认为—函数的提升比var优先
function :整体提升–函数名++函数体
var 提升声明不提升赋值
闭包
当a函数中有一个b函数,b函数中使用了a变量
function a(){
var c=11
function b(){
console.log(c);
}
// 由于函数作用域时临时的调用结束后会自动销毁,所以
// b函数为了保证自己能顺利执行,所以会把外部作用与保存在兹省的scopes属性里
// 这个被保存在scopes中的函数作用域-- 称呼闭包
}
闭包的用途:为函数制作私有的属性
var 函数 =(function(){
var 变量=值
return function(){
}
})()
闭包的缺点:
函数作用域是临时的,运行完毕之后会销毁来节省内存
闭包的话因为为了保证使用了其函数的正常使用内部的作用域保存下来,不销毁,所以会浪费内存
arguments:函数
arguments:函数中 ·隐式·带有的一个变量;保存了函数调用时收到的所有传参
用途:常用于实现 实参个数不固定的函数
函数重载:
利用if判断配合arguments变量,判断实参的个数和类型不同,重新载入不同的逻辑处理
访问器语法:
对象.属性名:点语法
对象[js]:方括号语法 --由于属性名时js代码,所以比较灵活
ES6之前如何让避免全局污染的?
开局书写匿名函数自调用,创建局部作用域,在这里声明的变量都输入局部作用域,防止了全局污染
ES6之后如何让避免全局污染的?
用let/const 声明变量,自动存储在脚本作用域中这就是专门设计用来存放变量的对象
实验代码:
let a=10;
const b=20;
var c=30;
如下图:
所以理论上:以后不用var只用let或者const
假设考虑兼容性:兼容2015年之前的浏览器,就不能用let/const
但是有一款软件可以把ES6的代码转换为ES6之前也能使用的代码实现兼容性—babel
let和const的区别
let:声明的变量可以重新赋值
// 声明变量存储会变化用let
// 开始工资
let salary = 6000;
// 一年后
salary = 10000;
// 三年后
salary = 17000;
const:声明的变量以后不能重新赋值,
初始化赋值之后无法再去修改超级安全可靠
// 声明变量存储不会变化用const
const wife = "xxxx";
wife = "yyy";
能修改的const属性
当声明的为字符串时
// 修改const
const names=['阿山','beiebi']
names.push('xx')
console.log(names);
模板字符串
模板字符串:属于es6的字符串增强语法——特别适合书写HTML语法
演示:
首先先假设从服务器获取了一段数据
let baidu={title:"百度",website:"http://www.baidu.com"}
如果我们用ES6之前的方式来做的话
// ES6之前
const a1='<a href="'+baidu.website+'">'+baidu.title+'</a>'
box.innerHTML = a1
效果为:
在ES6的时候
// ES6
box.innerHTML =`<a href="${baidu.website}">${baidu.title}</a>`
最重要的是ES6支持换行
比如:
<script>
let baidu = {
name: "baidu",
title: "百度",
website: "http://www.baidu.com",
};
box.innerHTML += `
<div id="${baidu.name}">
<a href="${baidu.website}">${baidu.title}</a>
<a href="${baidu.website}">${baidu.title}</a>
<a href="${baidu.website}">${baidu.title}</a>
<a href="${baidu.website}">${baidu.title}</a>
</div>`;
</script>
效果为
箭头函数
ES6中提供了新的箭头函数语法;来代替旧版本的匿名函数,而且拥有新的this指向
- 箭头函数
- 更简单的匿名函数写法
- ()=>{ } 》》》 function(){ }
- 提供了两个语法糖
-
- 形参只有一个的时候,小括号可以省–x=>{ }
-
- 函数体只有一行时可以省略{return}
- this指向
-
- 函数(): window
-
严格模式下时undefind
-
- 对象.函数():对象
-
- new 函数 () :构造出来的对象
-
- 箭头函数() :自身 没有按照作用域链查找
旧版的匿名函数
<script>
const a=function(){}
</script>
新版的语法:即箭头函数
把function
省略换位=>
<script>
const b=()=>{};
</script>
作者为箭头函数提供了两个语法糖
糖一:形参只有一个时,省略小括号
<script>
const c=(x)=>{return x*2}
console.log(c(20));
</script>
使用语法糖后就为
<script>
const c= x =>{return x*2}
console.log(c(20));
</script>
糖二:
函数体中只有一行代码可以省略return
<script>
const d = (x) => x * 2;
console.log(d(20));
</script>
简化一下下面的函数
<script>
const h=(a,b)=>{return {a:a,b:b};};
</script>
如果返回值是对象,其带有{}
包围,会被错误的识别为函数的{}
必须用( )
包裹,避免带来歧义
{属性名:值}
:如果值时变量 而且变量名和属性名一样
触发合并语法糖{a,b} - > {a}
<script>
const h =(a,b)=>({
a:a,
b:b
})
</script>
回调地狱
回调函数:回调函数最常见于异步操作网络请求
如果出现多个网络请求必须同步执行的场景,代码就会变得特别复杂
回调地狱:一个回调函数里触发下一个,下一个里面再去触发下下一个
Promise对象
ES6 提供的一个对象,用来解决回调地狱的解决方法
promise对象:可以解决回调地狱——解决回调地狱的格式
*
// promise:承诺 resolve:解决 reject:拒绝
new Promise((resolve, reject) => {
// resolve函数触发时会把其参数传递给then函数中的res
resolve({ msg: "resolve", code: 200 });
// reject触发时会把参数传递给。catch中的参数
reject({ msg: "resolve", code:400});
})
.then((res) => {
console.log("res:", res);
})
// then:然后 res响应结果
.catch((err) => {
console.log("err:", err);
});
// catch:抓取 err错误
promise的几种状态
待定:pending
– 没有调用resolve或者reject时
满足:fulfilled
– 调用resolve函数后进入then
拒绝:rejected
– 调用reject进入catch
promise的实战
下面的模仿nodejs后端的一个接口简化版(数据写死等)
<script>
var arr = { name: "startrr", passworld: "123456" };
new Promise((resolve, reject) => {
console.log("验证用户名");
// 模拟延迟异步操作,耗时1秒
setTimeout(() => {
if (arr.name == "startrr" && arr.passworld == "123456") {
resolve({ msg: "登录成功", code: 200 });
} else {
resolve({ msg: arr.name + "是错误的", code: 404 });
}
}, 1000);
})
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
});
</script>
如图:
promise的async异步
async:异步的:一种语法糖,可以修改promise
使用的格式
async的使用
前提:在函数前面添加async关键字
系统会认为这个函数里的异步函数需要同步执行
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body style="background-color: #333">
<script>
function charuname() {
return new Promise((a, b) => {
console.log("验证用户名");
setTimeout(() => {
const n = Math.random().toFixed(1);
if (n <= 0.3) {
a({ n, msg: "用户名正确", code: 200 });
} else {
b({ n, msg: "用户名错误", code: 404 });
}
});
}, 1000);
}
function charemail() {
return new Promise((resolve, reject) => {
console.log("开始验证邮箱");
setTimeout(() => {
const n = Math.random().toFixed(1);
if (n <= 0.3) {
resolve({ n, msg: "邮箱正确", code: 200 });
} else {
reject({ n, msg: "邮箱错误", code: 404 });
}
});
}, 1000);
}
function charphone() {
return new Promise((resolve, reject) => {
console.log("验证手机号");
setTimeout(() => {
const n = Math.random().toFixed(1);
if (n <= 0.3) {
resolve({ n, msg: "手机号正确", code: 200 });
} else {
reject({ n, msg: "手机号错误", code: 404 });
}
});
}, 1000);
}
charuname()
.then((res) => {
console.log("res:", res);
return charemail();
})
.then((res) => {
console.log("res:", res);
return charphone();
})
.then((res) => {
console.log("res:", res);
})
.catch((err) => {
console.log("err:", err);
});
// 前提:在函数前面添加async关键字
// 系统会认为这个函数里的异步函数需要同步执行
async function show(){
// await 等待
var a=await charuname()
console.log(a);
var b=await charemail()
console.log(b);
var c=await charphone()
console.log(c);
}
console.log(show());
</script>
</body>
</html>
对象是引入类型
内存分为堆内存和栈内存
栈内存:查询速度快,存放小型的数据,存基础数**据类型**+对象地址相当于地址目录
堆内存:存放大型数据-对象类型拥有地址内存
对象浅拷贝
限制做一个空的对象,遍历原有的对象 把其中的属性,挨个复制到新的对象里面
适用类型:如果是对象类型,则用深拷贝
函数this配合对象使用
由于this的存在函数的传参发生了变更
方案1-传统:函数通过形参 来接受对象的实参,然后处理函数
方案2-新式:把函数放到对象里面,this关键词就代表函数 工作时
所在的对象
构造函数
就是一个函数,单数由于工作特殊 - - 创建构造函数
对象的函数
命名规范:一般使用大驼峰
,用于区分
创建构造函数步骤
1.创建空对象
2.把收到的实参存入上面的空对象
原型概念
目的:节省内存,避免每次创建对象 都重复生成相同的函数
实现:构造函数有一个prototype,其中可以存储共享的函数们
当生成对象时
,把其中的__proto__属性、链接、 构造函数prototype
对象的原型连机制:当使用对象的一个属性是,如果对象没有,则自动到
new运算符__proto__中寻找
new运算符
当new元素符放在 函数前,则函数会自动完成四行代码
var this
this = {}
this.__proto__ =函数.prototype
return this
新的this指向
函数():没有前缀的就是window
对象.函数():对象--运行时所在的对象
new函数():构造函数
箭头函数的this的关键词:箭头函数没有this关键词
当函数中,使用自身没有的的变量会怎么办?
出发作用域链机制:向上层作用域查找this
运行示例
我们发现打印出来的就是window(自己没有就往上找
)
那么再函数里面的函数是?
看下面的代码
var emp ={
ename:'qiushan',
show(){
const b=()=>{
console.log('this:',this);
}
b()
},
}
emp.show()
// show 函数中的this是其前方的emp
数组的高阶函数
高阶函数:函数中使用了其他函数,就叫高阶函数
ES6为数组类型 新增了更多的方法
打印数组的原型可以查看这些方法
console.log(Array.prototype)
由于前后端项目的流行,在前端,利用JS处理网页数据的需求,激增
前后端分离套路:利用Ajax,请求服务器的数据,在前端(浏览器)上临时拼接为HTML代码,最后显示
// 每个用户的浏览器各自拼接--分担服务器压力
前后端不分离:后端直接把数据拼接为HTML,发个浏览器显示
// 服务器压力大,还要拼接 --大网站扛不住
服务器发送的数据通常是数组–从数据库查出来的
所以需要前端JS提高对数组的处理能力
实验:判断下面数组里面的数据是否都大于0
<script>
// 高阶函数:函数中使用了其他函数,就叫高阶函数
console.log(Array.prototype);
var nums = [12, 13, 14, 15, 16, -1, 18];
// 判断配个元素都大于0
var a = nums.every((value, index, array) => {
return value > 0;
});
console.log('a:',a);
</script>
当都有一个是负的时候
反之则为true
上图nums中的元素依次被箭头函数检测,每个元素都要提供三个值:值,序号来自数组 箭头函数要做判断 元素是否符合条件
具体看需求
修改代码:
<script>
// 高阶函数:函数中使用了其他函数,就叫高阶函数
console.log(Array.prototype);
var nums = [12, 13, 14, -15, 16, -1, 18];
// 判断配个元素都大于0
var a = nums.every((value, index, array) => {
console.log(value,index,array);
return value > 0;
});
console.log('a:',a);
</script>
运行代码:
我们发现在出现负数的时候代码运行就中断了
其实every这个参数相当于只要有一个不满足要求就返回值,并且结束运行
简化函数
// 1,没有使用的形参可以不写
a = nums.every((value) => {
console.log(value);
return value < 0;
});
// 3,箭头函数形参只有一个省略()
a = nums.every((value) => {
console.log(value);
return value < 0;
});
// 4,箭头函数的函数体只有一行省略return
a = nums.every((value) => {
value < 0;
});
练习:判断下列数组是否为偶数
var nums=[12,5,32,1231,1223,-1,123,43]
实现效果:
练习:判断下列数组是否又负数
var nums=[12,5,32,1231,1223,-1,123,43]
实现效果:
高阶函数some
some:一些,有一些满足条件的
只要有真的就行,–类似于逻辑或,又真则真
像下面:判断是否有大于500的项目
// 高阶函数some
// some:一些,有一些满足条件的
// 只要有真的就行,--类似于逻辑或,又真则真
var nums=[12,564,123,1231,5346,68456]
var mians=nums.some(value=>value>=500)
console.log(mians ? '有' : '没有');
结果:
map映射
把数据映射为HTML代码
map :自动便利数组,把每个元素用箭头函数处理后返回值组成新的数组
join: 默认是用逗号来间隔字符串,可以通过传参来进行传参
效果
练习:把下面数组里面的数据映射到网页上
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul id="cart">
</ul>
<script>
var cars=['冬瓜','南瓜','西瓜','美离间','奇异果','西红柿','桃子']
// 把元素放到li标签里面
</script>
</body>
</html>
</body>
</html>
答案:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul id="cart">
</ul>
<script>
var cars=['冬瓜','南瓜','西瓜','美离间','奇异果','西红柿','桃子']
// 把元素放到li标签里面
var car=cars.map((value)=>{
return `<li>${value}</li>`
})
var a=car.join('<br/>')
console.log(a);
cart.innerHTML =a
</script>
</body>
</html>
</body>
</html>
例如我们还可以来玩的花一点
<body>
<ul id="list">
<!-- 占位 -->
</ul>
<script>
var stus=[
{name:"宝宝",age:"22",color:"red"},
{name:"山海",age:"42",color:"blue"},
{name:"秋慧",age:"21",color:"yellow"},
{name:"玛丽",age:"62",color:"red"},
{name:"凯瑞",age:"82",color:"red"},
{name:"小李",age:"21",color:"red"},
{name:"丘丘人",age:"20",color:"skyblue"},
]
var strs=stus.map((value)=>{
return `<li style="background-color:${value.color}">${value.name}-${value.age}</li>`
})
str=strs.join("<br />")
list.innerHTML=str
</script>
</body>