es6入门到五连绝世之一血(first blood)
欢迎来到e6语法笔记教学
- ES6, 全称 ECMAScript 6.0 ,是 JavaScript 的下一个版本标准,2015.06 发版。来学习的都应该对其有一定的了解了,在此不做过多陈述,这篇帖子主要是根据《 ES6从一脸懵逼到灵活运用》做的学习笔记。
- 这篇博客的记录手法,是通过 demo 记录 es6 要点,注释详细,运行结果也已备注,可能对于技术上的讨论很少,主要面向 es6 入门读者,篇幅可能很长,但是各个节点区分明确,可选择学习…
- 本来是想一篇博客全记录的,但是发现好像有点困难,不利于定位查询。故在此分章节,目录查询如下,博客右侧目录图标:
一、变量声明
1.1、变量声明有三种方式
<script type="text/javascript">
var price = 100;
let age = 20;
const id = 'abc123';
</script>
1.2、JS中的块级作用域,var、let、const 三者的区别
参考:https://blog.csdn.net/hot_cool/article/details/78302673#commentBox
1.2.1、var 的定义
<script type="text/javascript">
//1、var声明的变量是可以重新赋值和重复声明的
var price = 100;
price = 80; //重新赋值
var price = 50; //重复声明
console.log(price);//50
//2、作用域简析
{
//块级作用域定义
var minPrice = 100;
}
console.log(minPrice);//全局作用域中可见,打印100,可跨块
//函数块中定义
function getPrice(){
var maxPrice = 200;
console.log(maxPrice);
}
getPrice();//调用函数,函数中可见打印200
//全局中不可见,Error,则跨函数不能访问
console.log(maxPrice);
</script>
1.2.2、let,coust 的定义
标题 let,coust 定义主要作用是在 块级 { } 作用域中,而const是定义常量,一经定义无法修改。
<script type="text/javascript">
//1、常量和变量的区别
let apple = 5;
//let apple = 20; //SyntaxError,不能重复声明
apple = 8; //重新赋值是可以的
const banana = 5; //一经定义,必须初始化赋值
//const banana = 8; //SyntaxError,不能重复声明
//banana = 8; //TypeError,常量一经定义赋值就无法修改
//2、不同的作用域,看似重复定义,前提是定义的变量不能跨域
let pear = 5; //全局作用域中
{
let pear = 8; //块级作用域中
}
//3、使用场景
var price = 100;
var count = 6;
if(count > 5){
//discount只是作为临时变量,执行完就销毁,故不用var ,而let,const都可以定义在当前块中即可
let discount = price * 0.6;
console.log(`The discount is ${
discount }`);//es6 字符串的拼接
}
console.log(discount);//ReferenceError,已销毁
</script>
const 定义对象解析:
<script type="text/javascript">
//关于const对象引用
const person = {
'name':'tom','age':'5'};
console.log(person); //{name: "tom", age: "5"}
//重新赋值一个对象的引用,也就是改变成另一个人,当然不行
//person = {'name':'Jerry','age':'4'}; //TypeError
//这个修改引用对象中的属性值,person所指向的原始引用是没变化的,所以 no problem
person.name = '大鱼';
console.log(person); //{name: "大鱼", age: "5"}
</script>
内存污染问题解析
<script type="text/javascript">
//console.log(name); //并未报错,而是null,这里打印的其实是 window 对象的name
var name = "tom";
//js以前版本,只有var定义,由于其可重复声明,则可能覆盖掉已定义的变量,造成内存污染,如果此处用let,就会报错提示了
console.log(name);//tom
console.log(window.name);//tom
</script>
解决方案:
案例解析的讲解不够深入,还请见谅…
<script type="text/javascript">
//1、IIFE(立即调用函数表达式),利用返回值接受
//参考地址:https://developer.mozilla.org/zh-CN/docs/Glossary/%E7%AB%8B%E5%8D%B3%E6%89%A7%E8%A1%8C%E5%87%BD%E6%95%B0%E8%A1%A8%E8%BE%BE%E5%BC%8F
var result = (function () {
var name = "Barry";
return name;
})();
console.log(result);// "Barry"
console.log(window.name);// 打印结果为空
//2、利用块级作用域{ }
{
//注意:利用块级作用域当然只能使用let,const定义,用var定义会跨域,同样会污染全局作用域中的变量
let name = "Barry";
console.log(name);
}
console.log(window.name);// 打印结果为空
//3、经典案例解析
/*
可以看出每次console.log(i) 打印是正常的,因为其执行顺序的原因
而定时器执行时,var定义的 i 自身都在全局作用域,且可以重定义,i 最后输出值为4,再执行 i++,i的值为5,跳出循环
--则定时器开始打印时,i已经是5,循环了5次,打印5个5
*/
for(var i=0;i<5;i++){
console.log(i); // 0 1 2 3 4
//定时器1秒后执行
setTimeout(function() {
// 5 5 5 5 5
console.log(i);
}, 1000);
}
/*
这种打印结果是不是你想要的,因为 let 是定义在块级作用域
则for循环的 { } 循环体会创建 5 次,各个之间互不干扰,就是如下输出
*/
for(let i=0;i<5;i++){
console.log(i); // 0 1 2 3 4
//定时器1秒后执行
setTimeout(function() {
// 0 1 2 3 4
console.log(i);
}, 1000);
}
</script>
1.2.3、Temporal Dead Zone (TDZ) 临时性死区
<script type="text/javascript">
/*
1、首先看下面的代码,在声明之前使用了变量color,打印却没有报错!
--因为这是在js中存在变量提升,也就是在当前作用域声明的变量会被提升到作用的顶部
*/
console.log(color); // undefined
var color = 'yellow';
//2、上面变量提升效果实际上等同于这样
var color; //变量提升,只是声明了变量,并未赋值
console.log(color); // undefined
color = 'yellow';
/*
3、现在测试一下 let,const 同理不做演示
-- 未定义导致的错误,其实在let中也存在变量提升,但是在let和const中还存在一个叫做 “临时性死区” 的概念。
在这个作用域的开始直到变量的声明之前,这个变量都是处在 “临时性死区” 当中的,这个时候引用他的话会报referenceError的错误,
其实这个特征可以帮助我们养成在变量未声明之前不要使用它的好习惯
*/
console.log(book); //ReferenceError
let book = 'cat';
</script>
看到这里,对 es6 中变量的选择心里就有个小想法了,当需要重新绑定或者更新的时候就使用 let,常量就是用 const,尽量不使用 var , 因为 var 可能会产生一些重复声明,以及在定义之前使用它不太容易找到的错误,所以尽量使用 const 和 let。
二、Arrow Function 箭头函数
2.1、简明的语法
<script type="text/javascript">
//1、基本写法。利用Array.map()函数循环
const numbers = [5, 9, 13, 25, 30, 7, 11];
const double = numbers.map(function(number){
return number * 2;
});
console.log(double); // [10, 18, 26, 50, 60, 14, 22]
//2、箭头函数改写
const double2 = numbers.map((number) => {
return number * 2;
});
console.log(double2); // [10, 18, 26, 50, 60, 14, 22]
//2.1、如果箭头函数的传入参数只有一个,那么是可以不写括号的
const double3 = numbers.map(number => {
return number * 2;
});
//2.2、如果多个参数,以逗号分割。例如新增打印索引
const double4 = numbers.map((number, i) => {
return `${
i}:${
number * 2}`;
});
console.log(double4);//["0:10", "1:18", "2:26", "3:50", "4:60", "5:14", "6:22"]
//2.3、如果没有传入参数,则()是需要保留的
const double4 = numbers.map(() => {
return `Hello World`;
});
</script>
2.2、隐式返回
<script type="text/javascript">
const numbers = [5, 9, 13, 25, 30, 7, 11];
/*
箭头函数的隐式返回
1、删除 return 关键字
2、去掉方法体的 {}
3、并做一行
*/
const double = numbers.map(number => number * 2);
console.log(double);//[10, 18, 26, 50, 60, 14, 22]
</script>
2.3、匿名函数
<script type="text/javascript">
//1、显示函数定义
function getName(name){
alert(`hello,${
name}!`);
}
//函数调用
getName("tom");
//2、箭头函数是一个匿名函数,所以通常使用变量接受其返回值,然后打印即可
const getname = name => alert(`hello,${
name}!`);
//变量传入参数name调用
getname("jerry");
</script>
2.4、箭头函数的 this 值
<script type="text/javascript">
/*
1、this 对象是在程序运行时绑定的
printHobbies() 中的this 是Jerry 对象调用,所以绑定的是Jerry
Array.map() 是一个独立的函数,独立运行时是不作为对象的方法调用的,故直接绑定到顶层的 window 对象
*/
const Jerry = {
name: "Jerry",
hobbies: ['Coding','Sleeping','Reading'],
printHobbies: function(){
console.log(this); //打印的是 Jerry 对象
this.hobbies.map(function(hobby){
console.log(this); // 打印的是 window 对象
console.log(`${
this.name} love ${
hobby}`); //循环分别打印 love Coding;love Sleeping;love Reading
});
}
}
Jerry.printHobbies();
/*
2、改为箭头函数打印如下:
是因为箭头函数没有自己的 this 绑定,默认是继承父作用域的 this 值
箭头函数this值是词法作用域,定义时即指定,以后也不会随调用方法的改变而改变
*/
const Tom = {
name: "Tom",
hobbies: ['Coding','Sleeping','Reading'],
printHobbies: function(){
console.log(this); //打印的是 Tom 对象
this.hobbies.map(hobby => {
console.log(this); // 打印的是 Tom 对象
console.log(`${
this.name} love ${
hobby}`); //Tom love Coding;Tom love Sleeping;Tom love Reading
});
}
}
Tom.printHobbies();
</script>
2.5、箭头函数不适用的场合
1、箭头函数虽然用起来很简便,但是也有如下不适用的情况,下面举例如下:
<script type="text/javascript">
//1、作为构造函数,需要绑定this对象
const Person = (name, age) => {
this.name = name;
this.age = age;
}
const Jerry = new Person('Jerry', 23);//TypeError: Person is not a constructor8
//2、为Person 对象添加方法,需要this指定当前对象
Person.prototype.updateAge = () => {
console.log(this); //this 绑定的是父级对象,window
this.age ++;
console.log(this.age); // NaN
}
console.log(Jerry.updateAge());
//3、当真的需要 this 值的时候
const button = document.querySelector('.zoom'); //获取一个 class='.zoom'的元素对象
button.addEventListener('click', () => {
console.log(this); //window
this.classList.add('in'); //当前绑定的对象添加一个 in 样式
setTimeout(() => {
//两秒后移出当前 in 样式
this.classList.remove('in');
}, 2000);
});
//4、需要使用 arguments 对象,arguments就是传入的实参,js中每个函数都具有
const sum = () => {
return Array.form(arguments) //将传入的实参格式化成一个数组对象
/*
reduce 为数组中的每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元素,
接受四个参数:初始值(或者上一次回调函数的返回值),当前元素值,当前索引,调用 reduce 的数组
prevSum : 每次回调函数的返回值(prevSum + value的值)
value : 当前迭代数组中的元素值
0 : 从指定下标处开始计算,一般还是记作0
可参考:https://blog.csdn.net/Web_J/article/details/88870515
*/
.reduce((prevSum, value) => prevSum + value, 0);
}
console.log(sum(1, 2, 3)); //ReferenceError: arguments is not defined
</script>
2、对上面的4个用例改回原来函数的操作如下:
<script type="text/javascript">
//1、作为构造函数,需要绑定this对象
const Person = function(name, age){
this.name = name;
this.age = age;
}
const Jerry = new Person('Jerry', 23);
console.log(Jerry); //Person {name: "Jerry", age: 23}
//2、为Person 对象添加方法,需要this指定当前对象
Person.prototype.updateAge = function(){
this.age ++;
console.log(this.age); // 24
}
Jerry.updateAge();
//3、当真的需要 this 值的时候
const button = document.querySelector('html');
button.addEventListener('mousedown', function(){
console.log(this); // this对象
this.classList.add('in');
setTimeout(() => {
console.log(this); // 需要注意的是这里绑定父级对象,即定义的 button
this.classList.remove('in');
}, 2000);
});
//4、需要使用 arguments 对象,arguments就是传入的实参,js中每个函数都具有
const sum = function(){
return Array.from(arguments) //将传入的实参格式化成一个数组对象
.reduce((prevSum, value) => prevSum + value, 0);
}
console.log(sum(1, 2, 3)); //6
</script>
三、函数参数默认值
<script type="text/javascript">
// 1、 es6 之前给参数赋默认值
function multiply(a, b){
/*
赋值默认参数,a 传入值则为其本身赋值,a 没有传入值赋默认值5
注意:js中参数默认是声明的,不能重复声明
*/
//const a = a || 5; //SyntaxError
a = a || 5;
b = b || 3;
return a * b;
}
console.log(multiply(2, 4)); //传入参数,打印 8
console.log(multiply()); //使用默认参数,打印 15
/*
2、 es6 写法
2.1、可减少代码量
2.2、可简明发现哪些代码是可以省略的,提高代码的可读性
*/
function multiply2(a = 5, b = 3){
return a * b;
}
console.log(multiply2(2, 4)); //打印 8
console.log(multiply2()); //打印 15
//传入第一个参数写法
console.log(multiply2(4)); //打印 12
/*
参入的参数不是第一个,则前面的参数值要是使用默认值,需用undefined代替传入
内部实现: typeof a === undefined 才能使用默认值
*/
console.log(multiply2(undefined, 4)); //打印 20
//console.log(multiply2(null, 4)); //不行,打印 0
</script>
四、模板字符串(Template StringsLiterals)
4.1、模板字符串简介
4.1.1、基本语法
<script type="text/javascript">
//1、使用一对反引号包裹,简单明了不易出错
const person = 'Jerry';
let age = 23;
const sentence = person + ' is ' + age + ' years old'; //字符串拼接易出错
console.log(sentence); //Jerry is 23 years old
/*
相对于传统更显简洁,便利
1、一对反引号包裹所有的字符串内容
2、字符串中引用变量,表达式使用 ${} 包裹即可
*/
const sentence1 = `${
person} is ${
age * 5} years old`;
console.log(sentence1); //Jerry is 115 years old
//2、定义模板,能更好的体现其层级关系
const template = `
<div class="greet">
<p>Hello</p>
</div>
`.trim(); //模板字符串会保留里面的空格,可用trim()去掉字符串前后空格
console.log(template);
</script>
4.1.2、示例演示
这其中有模板字符串中再嵌套模板字符串,展示在讲解 join() 方法 的注释中,但是在实际操作中,将其注释掉进行由方法的封装,解耦操作。
<script type="text/javascript">
//1、字符串模板的案例
const Jelly = {
name: 'Jelly',
date: '2019-08-19',
todos: [
{
name: 'Go to Store', completed: false},
{
name: 'Watch Movie', completed: true},
{
name: 'Running', completed: true}
]
}
/*
join() 方法用于把数组中的所有元素放入一个字符串。✔
元素是通过指定的分隔符进行分隔的(本数数组默认已逗号分割)
语法:arrayObject.join(separator) ,separator 可选,指定要使用的分隔符。如果省略该参数,则使用逗号作为分隔符
const template = `
<strong>${Jelly.name}</strong>
<ul>
${Jelly.todos.map(todo => `<li>${todo.name},${todo.completed ? '✔':'✘'}</li>`).join('')}
</ul>
<strong>${Jelly.date}</strong>
`;*/
//将上面注释的代码,进行解耦操作,同时也跟有利于代码的解读
function renderTodos(todos){
return (
`<ul>
${
todos.map(todo => `<li>${
todo.name},${
todo.completed ? '✔':'✘'}</li>`).join('')}
</ul>
`
)
}
//${} 中调用方法
const template = `
<strong>${
Jelly.name}</strong>
${
renderTodos(Jelly.todos)}
<strong>${
Jelly.date}</strong>
`;
document.body.innerHTML = template;
</script>
4.2、标签模板字符串
4.2.1、标签模板字符串的定义及示例
这里写了一个高亮的 css 样式,后期如果使用样式用例,我会尽量使用这个,为了节省篇幅,此高亮样式后期不再贴出。
<style>
.highlight{
padding: 2px 5px;
background: #00adb4;
color: white;
}
</style>
<script type="text/javascript">
//1、标签模板字符串
/*
这个方法参看下面定义 const sentence = ...;
根据标签模板字符串的定义进行构造的函数,sentence 会得到函数 highlight() 的返回值
参数讲解:
1、string --模板字符串中的 `` 包裹的字符串
2、user --模板字符串中 ${user} 变量值
3、topic --模板字符串中 ${topic} 变量值
*/
//function highlight(string, user, topic){ }
//根据上面的方法,如果有多个变量参数,显然不适用,使用es6剩余参数改写
// 三个点加上变量名,values是一个数组哦
function highlight(strings, ...values){
//debugger; //可手动解开此注释进行调试,如下图
const highlighted = values.map( value => `<span class='highlight'>${
value}</span>`);
let str = '';
//这个拼接有点意思,${string} 第一次是 null,第三次也是null, ${highlighted[i]}第3次是undefined,使用默认值填补''
//strings.forEach((string, i) => str += `${string}${highlighted[i] || ''}`);
//对上面的 forEach 函数用 reduce 改写
str = strings.reduce((prev, curr, i) => `${
prev}${
curr}${
highlighted[i] || ''}`,'');
return str;
}
const user = 'Mary';
const topic = 'Learn to use markdown';
//标签(highlight,对应js的函数名)模板字符串(``中的内容,对应js函数中的参数)
const sentence = highlight`${
user} has commented on your topic ${
topic}`;
document.body.innerHTML = sentence;
</script>
这里就是上面js中,开启 debugger 调试的界面效果,作为前端调试是很重要的,望知悉学习;
4.2.2、输入字符串过滤,防止XSS攻击
// 引入 dompurify 过滤工具
<script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/1.0.11/purify.min.js" type="text/javascript"></script>
<script type="text/javascript">
//标签模板字符串过滤用户注入脚本,预防xss攻击
//以下内容简单模拟
let user = "Mary";//当前用户
//假设用户在你的输入文本框中输入了如下内容,提交后台之前你使用js获取了用户提交的内容
let content = 'Hello,Cutie! <br/><img src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1566375153572&di=c1a298fbb9ed56dca31f61ca8602ed2b&imgtype=0&src=http%3A%2F%2Fi3.hexunimg.cn%2F2014-12-10%2F171277207.jpg" οnlοad="alert(`大吉大利,今晚抓鸡`);" />';
//1、如果对用户提交不处理,请解开下面注释查看效果
//document.body.innerHTML = content;
//2、以下内容为使用DOMPurify过滤的操作效果
function sanitize(strings, ...values){
//其实下面的拼接思想的核心是,模板字符串 strings 数组的生成是通过此模板字符串中的变量 ${} 分割的
// 这一步是将要展示的模板字符串进行拼接
const dirty = strings.reduce((prev, curr, i) => `${
prev}${
curr}${
values[i] || ''}`,'');
// 这一步就是调用 Purify 的方法将要打印的字符串 dirty 过滤
return DOMPurify.sanitize(dirty);
}
// 标签模板字符串:将要打印的数据 通过调用自定义函数 sanitize 进行处理
// 此处过滤掉 js ,界面已无弹窗警告
document.body.innerHTML = sanitize`
<span class="highlight">${
user}</span>
<div style="width:80px;height:50px;" >
${
content}
</div>
`;
</script>
4.3、es6 新增的字符串方法
- startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
- endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
- includes():返回布尔值,表示是否找到了参数字符串。
- repeat() :复制字符串指定的次数
<script type="text/javascript">
const id = '51030019801106542x';
const fan = 'I Love Laravist';
//1、startsWith() 从头部判断
console.log(id.startsWith('51')); //判断id是否51开头,true
console.log(id.startsWith('1980',6)); // id 第6位是否1980开头,true
// startsWith 是大小写敏感的
console.log(fan.startsWith('I')); //true
console.log(fan.startsWith('i')); //false
//2、endsWith() 从尾部判断,同理
console.log(id.endsWith('x')); //true
console.log(id.endsWith('X')); //false
console.log(fan.endsWith('Love',6));//true, 这里从尾部开始计算,这里的6传入的是从I往后数 e 的位置,空格也要算一位
//3、includes() 判断是否查找到指定字符串
console.log(fan.indexOf('Laravist')!=-1); // true, es6以前判断fan中是否存在指定字符串'Laravist'
console.log(fan.includes('Laravist')); // true,直接判断
console.log(fan.includes('Laravist',10)); // false,判断第10位之后是否还存在指定字符串
//4、repeat() 复制字符串指定的次数
console.log('哈'.repeat(5)); //哈哈哈哈哈 ,将'哈'复制5次打印
//4.1、美化字符串
const heading = `${
'='.repeat(5)}${
fan}${
'='.repeat(5)}`;
console.log(heading); //=====I Love Laravist=====
//4.2、实现字符串右对齐
function padder(string, length=25){
//默认总长度为25
/*
Math.max():函数返回一组数中的最大值。如果给定的参数中至少有一个参数无法被转换成数字,则会返回 NaN。
' '字符复制次数为 (length-string.length),0 这两个数中最大的那个数
注:再传入一个0的意义是防止,出现负数,而导致repeat()函数出现 RangeError: Invalid count value
*/
return `${
' '.repeat(Math.max(length-string.length, 0))}${
string}`;
}
console.log(padder(id)); // 51030019801106542x
console.log(padder(fan)); // I Love Laravist
</script>