ECMAScript 是 JS 的语言标准。而 ES6 是新的 JS 语法标准。
PS:严格来说,ECMAScript 还包括其他很多语言的语言标准。
很多人在做业务选型的时候,会倾向于选jQuery。其实jQuery的语法是偏向于ES3的。而现在主流的框架 Vue.js 和React.js的语法,是用的ES6。
ES6中增加了很多功能上的不足。比如:常量、作用域、对象代理、异步处理、类、继承等。这些在ES5中想实现,比较复杂,但是ES6对它们进行了封装。
版本 | 发布时间 | 发布内容 |
---|---|---|
第一版 | 1997年 | 制定的基本语法(1995年ECMAScript 诞生时间) |
第二版 | 1998年 | 较小改动 |
第三版 | 1999年 | 引入正则、异常处理、格 式化输出等。IE 开始支持 |
第 4 版 | 2007 年 | 过于激进,未发布 |
第 5 版 | 2009 年 | 引入严格模式、JSON,扩 展对象、数组、原型、字 符串、日期方法 |
第 6 版 | 2015 年 | 模块化、面向对象语法、 Promise、箭头函数、let、 const、数组解构赋值等等 |
第 7 版 | 2016 年 | 幂运算符、数组扩展、 Async/await 关键字 |
第 9版 | 2017 年 | Async/await、字符串扩展 |
第 10 版 | 2018 年 | 对象解构赋值、正则扩展 |
第 11 版 | 2019 年 | 扩展对象、数组方法 |
ES6 新增了 let 和 const 来声明变量
var
:ES5 和 ES6中,定义全局变量(是variable的简写)。
let
:定义局部变量,替代 var。
const
:定义常量(定义后,不可修改)。
使用 let 声明的变量有几个特点:
不允许重复声明
let
不允许在相同作用域内,重复声明同一个变量。
let test = 10;
let test = 20;
// 报错:Uncaught SyntaxError: Identifier 'test' has already been declared
块儿级作用域
ES5 只有全局作用域 和 函数作用域,没有块级作用域,这就会造成一些问题,如:
用来计数的循环变量泄露为全局变量。
for (var i = 0; i < 10; i++) {
console.log('循环体中:' + i);
// 每循环一次,就会在 { } 所在的块级作用域中,重新定义一个新的 i
}
console.log('循环体外:' + i);
变量 i
只是用来控制循环,但是循环结束后,并没有消失,并且泄露给了全局变量
再来看下ES6 的 let 声明
var a = 2;
{
let a = 10;
}
console.log(a);
上方代码的输出结果为 2。用 let 声明的变量,只在局部(块级作用域内)起作用。
使用 let
解决 var
在for循环时定义造成的变量泄露到全局行为
for (let i = 0; i < 10; i++) {
console.log('循环体中:' + i);
}
console.log('循环体外:' + i);
{ }
这个块级作用域里生效。不存在变量提升
使用 ver 会发生 “变量提升现象”,就是在变量声明之前就使用,值为undefined
,为了纠正这种奇怪的现象,let
命令改变了语法的行为,使得声明的变量一定要在声明之后使用,否则就会报错。
console.log(a); // var 声明的会变量提升 结果为undefined
var a = 10;
console.log(b); // 报错:ncaught ReferenceError: b is not defined
let b = 20;
暂时性死区
只要在 {} 内存在let
命令 他所在的变量 就 “绑定” 在这个区域了,不再受外部的影响
var temp = 123;
{
temp = 'abc';
let temp;
}
// 报错:temp is not defined
上面的代码中虽然存在全局变量,但是在 {} 内使用了 let
然后就这个区域就是一个块级作用域。在这个作用域内 temp
变量还没有声明就使用了就会报错:temp 没有定义
let 的出现就是为了替代var会出现的一些怪异现象 所以以后声明变量使用 let 就对了
const 关键字用来声明常量,const 声明有以下特点
// 声明常量
const STUDY = "好好学习,天天向上";
1.声明必须赋初始值
const A;
// 报错:Uncaught SyntaxError: Missing initializer in const declaration
2.一般常量使用大写(行业默认)
const NAME = '张三';
const AGE = 20;
3.常量的值不能修改
const HELLO = '你好';
HELLO = '大家好';
// 报错:Uncaught TypeError: Assignment to constant variable.
4.和 let 一样可以形成块级作用域
{
const PLAPER = 'LWX';
}
console.log(PLAPER);
// 外部访问不到:Uncaught ReferenceError: PLAPER is not defined
5.对于数组和对象的元素修改,不算做对常量的修改,不会报错
const TEAM = ['UZI','MLXG','Ming','Letme'];
TEAM.push('Meiko');
虽然值修改了,但是常量指向的地址没有发生改变,所以不会报错。
但是如果我们把TEAM的值修改了,那么就会报错,如:
const TEAM = ['UZI','MLXG','Ming','Letme'];
TEAM = 100;
// 报错:Uncaught SyntaxError:
// Identifier 'TEAM' has already been declared
总结: 以后声明数组或者对象使用const声明更加的稳妥,避免误操作,修改了数据值。避免潜在的问题
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称 为解构赋值。本质上,解构赋值就是模式匹配。解构赋值在实际开发中可以大量减少我们的代码量,并且让程序结构更清晰
数组解构赋值允许我们按照 一 一 对应的关系从数组中提取值,然后在将值赋值给变量
举例:
// 以前的取值
let a = 10;
let b = 20;
let c = 30;
现在我们可以通过数组解构的方式进行赋值:
let [a, b, c] = [10,20,30];
取值也是一样
var arr = [10,20,30];
var [a,b,c] = arr;
解构例子:
let [,,third] = [1,2,3];
console.log(third); //3
let [x, ,z] = [1,2,3];
console.log(x,z); // 1 3
let [head, ...tail] = [1, 2, 3, 4];
console.log(head,tail); // 1 [2,3,4]
let [x, y, ...z] = ['a'];
console.log(x,y,z); // x undefined []
1.如果解构不成功 那么变量的值就等于undefined
let [a] = [];
let [bar,foo] = [1];
console.log(a); // undefined 未赋值
console.log(bar,foo); //bar = 1,foo = undefined
2.不完全解构情况下 也可以进行解构赋值 即等号左右两边模式一样 但是只匹配到右边的一部分数据
let[x,y] = [1,2,3]
console.log(x,y); // 1,2
let [x, y,z,e,f] = [1, 2, 3];
console.log(x,y,z,e,f); // 1 2 3 undefined undefined
3.如果等号右边不是数组(不是可遍历的结构) 那么就会报错
let [m] = 1;
let [m] = false;
let [m] = null;
let [m] = {
};
console.log(m);
// 统统不是数组 报错:Uncaught TypeError: false/null... is not iterable
4.在解构赋值时,是允许使用默认值的,但是要启动默认值 这个变量的值就必须严格等于 undefined。举例如下:
let[a,b = "abc"] = [1,2]
console.log(a,b); // 1,2
let[a,b = "abc"] = [1]
console.log(a,b); // 1,"abc"
let [a, b = "abc"] = [1,null];
console.log(a,b); // 1 "null"
let [a, b = "abc"] = [1,undefined];
console.log(a,b); // 1 "abc"
let [a, b = "abc"] = [1,""];
console.log(a,b); // 1 ""
5.默认值可以使用其他变量 但是前提是赋值的这个变量必须是提前声明过的
let d = 3;
let [x = d] = [];
console.log(x); // 3
let [x = 2, y = x] = [];
console.log(x,y); // 2,2
let [x = y, y = 2] = [];
console.log(x,y); // 报错 因为x = y时 y还没有声明
// ||
let x = y;
let y = 2;
let [x = 2,y = x] = [8];
console.log(x,y); // 8,8
对象的解构允许我们使用变量的名字匹配对象的属性,匹配成功将对象属性的值赋值给变量。
举例:
let person = {
name:'张三',age:30,sex:'男'};
let {
name,age,sex} = person;
console.log(name);
console.log(age);
console.log(sex);
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
let {
bar, foo } = {
foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
let {
baz } = {
foo: 'aaa', bar: 'bbb' };
baz // undefined
注意
如果解构失败,那么变量的值就是undefuned
let {
foo} = {
bar:'baz'};
console.log(foo); // undefined
默认值
对象的解构也可以指定默认值。和 数组默认值解构同样,对象的属性值也要严格等于undefined
var {
x = 3} = {
};
console.log(x); // 3
var {
x, y = 5} = {
x: 1};
console.log(x); // 1
var {
x = 3} = {
x: undefined};
console.log(x); // 3
var {
x = 3} = {
x: null};
console.log(x); // null
注意点
(1)如果要将一个已经声明的变量用于解构赋值,必须非常小心。
// 错误的写法
let x;
{
x} = {
x: 1};
// SyntaxError: syntax error
因为JavaScript引擎会将{x}
理解成一个代码块,从而发生语法错误。只有不将大括号写在行首,避免 JavaScript 将其解释为代码块,才能解决这个问题。
// 正确的写法
let x;
({
x} = {
x: 1});
注意点
(2)由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构。
let arr = [1, 2, 3];
let {
0 : first, [arr.length - 1] : last} = arr;
first // 1
last // 3
上面代码对数组进行对象解构。数组arr
的0
键对应的值是1
,[arr.length - 1]
就是2
键,对应的值是3
。
字符串也可以解构,这是因为,此时字符串被转换成了一个类似数组的对象。举例如下:
const [a, b, c, d] = 'smyhvae';
console.log(a);
console.log(b);
console.log(c);
console.log(d);
console.log(typeof a); //输出结果:string
使用扩展运算符 就是将一个数组转为用逗号分隔的参数序列。
let arr = ["a","b","c"];
console.log(...arr); // 相当于 => console.log("a","b","c");
合并数组
扩展运算符提供了数组合并的新写法。
const arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3 = ['d', 'e'];
// ES5 的合并数组
arr1.concat(arr2, arr3);
// [ 'a', 'b', 'c', 'd', 'e' ]
// ES6 的合并数组
[...arr1, ...arr2, ...arr3]
// [ 'a', 'b', 'c', 'd', 'e' ]
转换数组
利用扩展运算符将伪数组转换为真数组
var divs = document.querySelectorAll('div');
var arr = [...divs];
console.log(arr);
对象属性的简单赋值方式 可以将变量名直接放进对象中, 解析的时候 将变量名解析为属性名 变量值解析为属性值
1.属性的简单赋值
function fn(num1,num2) {
return {
num1,num2};
}
// 等同于
function fn(x,y) {
return {
num1:x,num2:y};
}
fn(1,2);
除了属性的简写,对象方法的也可以简单赋值 可以省略function关键字
var obj = {
name :'lucy',
age :18,
study() {
console.log("学习");
}
}
// 等同于
var obj = {
name :'lucy',
age :18,
study: function(){
console.log("学习");
}
}
我们知道,this
关键字总是指向函数所在的当前对象,ES6 又新增了另一个类似的关键字super
,指向 当前对象的原型对象 。
const proto = {
foo: 'hello'
};
const obj = {
foo: 'world',
find() {
return super.foo;
}
};
Object.setPrototypeOf(obj, proto);
obj.find() // "hello"
使用注意点 :
var stu = {
name : "张三",
age : 16,
// study : function() {
// console.log("我是" +super.type + ", 我的任务是" + super.tesk); // 报错
// }
// 正确用法 :
study() {
console.log("我是" +super.type + ", 我的任务是" + super.tesk);
}
}
stu.__proto__ = {
type : "student",
tecsk : "学习"
}
Object.is()
ES5 比较两个值是否相等,只有两个运算符:相等运算符(==
)和严格相等运算符(===
)。它们都有缺点,前者会自动转换数据类型,后者的NaN
不等于自身,以及+0
等于-0
。JavaScript 缺乏一种运算,在所有环境中,只要两个值是一样的,它们就应该相等。
Object.is() 用来比较两个值否严格相等,只是在-0 和+0 还有NaN的判断上面不一样
console.log(-0 === +0); // true
console.log(NaN === NaN); // false
console.log(Object.is(-0, +0)); // false
console.log(Object.is(NaN, NaN)); // true
Object.assign()
Object.assign()
方法用于对象的合并
参数1 : 目标对象 参数2 : 需要被合并的对象
var obj1 = {
name: "hello",
age: 18
};
var obj2 = {
sex: "男"
}
var newObj = Object.assign(obj1, obj2);
console.log(newObj);
//{name: "hello", age: 18, sex: "男"}
注意点
1.返回值是传入的第一个目标对象,会把所有的对象合并上去,再返回
var xm = {
"name" : "张学友"};
var age = {
"age" : 58};
var gender = {
"gender" : "男"};
var objAssign = Object.assign(age,xm,gender);
console.log(objAssign); //{age: 58, name: "张学友", gender: "男"}
console.log(objAssign === age); // true 说明返回值是传入的第一个参数
console.log(objAssign === xm); // false
console.log(objAssign === gender); //false
注意点
2.第一个参数必须是对象 如果不是对象,就会把它转换成对象
var xm = {
"name" : "张学友"};
var age = {
"age" : 58};
var gender = {
"gender" : "男"};
var objAssign = Object.assign(18, xm, gender);
console.log(objAssign);
// Number {18, name: "张学友", gender: "男"}
注意点
3.如果第一个参数是undefined 或者null 因为他们无法转换成对象 那么就会报错
var xm = {
"name" : "张学友"};
var age = {
"age" : 58};
var gender = {
"gender" : "男"};
var objAssign = Object.assign(undefined, xm, gender);
console.log(objAssign); // 报错 Cannot convert undefined or null to object
var xm = {
"name" : "张学友"};
var age = {
"age" : 58};
var gender = {
"gender" : "男"};
var objAssign = Object.assign(null, xm, gender);
console.log(objAssign); // 报错 Cannot convert undefined or null to object
注意点
4.如果在需要合并的多个对象里面 有同名的属性 那么后面的属性就会对前面的进行覆盖
var xm = {
"name" : "张学友","gender" : "男"};
var age = {
"age" : 58};
var gender = {
"gender" : "女","name": "lili"};
var objAssign = Object.assign(null, xm, gender);
console.log(objAssign); // {age: 58, name: "lili", gender: "女"}
注意点
5.如果undefined 和 null 不是第一个参数就不会报错 而是把第一个参数返回
var xm = {
"name" : "张学友"};
var age = {
"age" : 58};
var gender = {
"gender" : "男"};
var objAssign = Object.assign(age, xm, gender, undefined, null);
console.log(objAssign); // {age: 58, name: "张学友", gender: "男"}
注意点
6.Object.assign() 可以浅拷贝
Object.setPrototypeOf()
设置一个对象的原型对象
参数1 : 目标对象 参数2 : 新设置原型的对象
let proto = {
sayHi() {
console.log("hello");
}
}
let obj = {
n1: 15,
n2: 18
}
Object.setPrototypeOf(obj, proto);
obj.sayHi(); // hello
Object.getPrototypeOf()
获取一个对象的原型对象。参数 : 需要获取对象的原型的对象名
let proto = {
sayHi() {
console.log("hello");
}
}
let obj = {
n1: 15,
n2: 18
}
Object.setPrototypeOf(obj, proto);
obj.sayHi();
console.log(Object.getPrototypeOf(obj)); // {sayHi: ƒ}
Object.keys()
Object.keys() 是将对象所有的属性名获取到 添加到数组 并返回 返回的是一个数组
参数 : 需要遍历的对象名
var stu = {
name: "张三",
age: 18,
work: "无业游民"
}
console.log(Object.keys(stu));
// ["name", "age", "work"]
Object.values()
Object.values() 是将对象所有的属性值获取到 添加到数组 并返回 返回的是一个数组
var stu = {
name: "张三",
age: 18,
work: "无业游民"
}
console.log(Object.keys(stu));
// ["name", "age", "work"]
console.log(Object.values(stu));
// ["张三", 18, "无业游民"]
其实class就是一颗语法糖 他的本质还是函数 而且是ES5里面的函数封装而成的
class Cat{
constructor(name, type) {
this.name = name; // this指向于类创建的实例化对象
this.type = type;
}
work() {
concole.log("抓老鼠");
}
}
console.log(typeof Cat); // function
在类里面定义方法 可以省略function关键字
每创建一个类 都会默认有一个构造方法constructor() 相当于构造函数
传统的 JavaScript 语言,输出模板通常是这样写的:
var stu = {
name: "lisa",
age: 12,
place: "湖北武汉",
grade: 6
}
document.querySelector("p").innerHTML = "我叫" + stu.name + ",我几年" + stu.age + ",我住在" + stu.place + ",今年上" + stu.grade;
let str = `我是一个模板字符串哦!!`;
特点:
内容可以直接出现换行符
// 之前我们写的字符串想要换行,必须通过 + 和''连接
let str = ''
+
'沈腾 ' +
'艾伦 '
'';
// 现在使用 `` 的方式
let str = `
- 沈腾
- 艾伦
`;
变量拼接
// 之前的写法
let lovest = '沈腾';
let out = lovest + '是搞笑的演员';
// 现在写法
let lovest = '沈腾';
let out = `${
lovest}是搞笑的演员`;
console.log(out); // 沈腾是搞笑的演员
var stu = {
name: "lisa",
age: 12,
place: "湖北武汉",
grade: 6
}
document.querySelector("p").innerHTML = `我叫${
stu.name}, 我今年 ${
stu.age}岁了, 我住在${
stu.place}`;
document.querySelector("p").innerHTML = `我叫${
stu.name}, 我今年 ${
stu.age - 1}岁了, 我住在${
stu.place}`;
引入模板引擎的目的:
页面渲染使用字符串拼接 会存在问题: 字符串的恒定性 字符串拼接的时候容易出错
模板引擎不用拼接字符串 直接使用 静态页面里面的HTML里面的结构生成模板 需要渲染的时候直接调用
模板引擎的实现方式有很多 最简单的 ‘置换型’
模板引擎 这类模板引擎只是将指定模板内容( 字符串) 中的特定标记( 子字符串) 替换一下便生成了最终需要的业务数据( 网页)
模板引擎的使用步骤:
导入模板引擎 template-web.js 下载地址-选择lib
准备一个模板
<script type="text/html" id="tpl">
<h1>自我介绍</h1>
<p>大家好, 我叫
<%= name %>, 我今年
<%= age %> 岁</p>
</script>
使用模板引擎注意点:
<%= name %>
// 那么下面的对象属性名是name才可以和模板字符串对应上
type的值只要不是text / javascript 但是建议使用text / html 因为其它的无法识别html标签
<%= %> 必须是一个整体 不能加空格 或者其它的符号
<% = %> // xxx 错误写法
模板引擎使用示例:
<script src="./template-web.js"></script>
<script type="text/html" id="tpl">
<h1>自我介绍</h1>
<p>大家好, 我叫
<%= name %>, 我今年
<%= age %> 岁</p>
</script>
<script>
var stu = {
name: "丽萨",
age: 18
}
var html = template("tpl", stu);
var stu2 = {
xingming: "黑龙江",
age: 16
}
html += template("tpl", stu2);
document.body.innerHTML = html;
var arr = [{
name: "丽萨",
age: 18
}, {
name: "安迪",
age: 18
}, {
name: "艾玛",
age: 18
}, {
name: "路痴",
age: 18
}]
</script>
显示效果:
<script type="text/html" id="tpl">
<%for(var i = 0; i < list.length;i++) {
%>
<h1>自我介绍</h1>
<p>大家好, 我叫
<%= list[i].name %>, 我今年
<% if(list[i].age >20) {
%>
<u>成年</u>
<% } else {
%>
<u>未成年</u>
<% } %>
</p>
<% } %>
</script>
Demo
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
input {
width: 120px;
height: 70px;
margin: 50px auto;
display: block;
}
table {
margin: 0px auto;
/* table的样式,其他地方不能用 */
/* 合并单元格的边框 */
border-collapse: collapse;
}
th,
td {
border: 1px solid #000;
width: 120px;
height: 40px;
text-align: center;
}
</style>
</head>
<body>
<!-- 1.导入模板引擎 -->
<script src="./template-web.js"></script>
<input type="button" value="加载数据" id="load">
<table>
<thead>
<tr>
<th>序号</th>
<th>姓名</th>
<th>年龄</th>
<th>成绩</th>
</tr>
</thead>
<tbody id="tb">
<!-- 2.准备模板 -->
<script type="text/html" id="tpl">
<!-- 遍历 -->
<% for(var i = 0;i < list.length; i++) {
%>
<tr>
<td>
<%= list[i].Id %>
</td>
<td>
<%= list[i].name %>
</td>
<td>
<%= list[i].age %>
</td>
<td>
<%= list[i].score %>
</td>
</tr>
<% }%>
</script>
</tbody>
</table>
</body>
</html>
<script>
var arr = [{
Id: 0,
name: "张三",
age: 18,
score: 68
}, {
Id: 1,
name: "王亚",
age: 28,
score: 55
}, {
Id: 2,
name: "李辉",
age: 16,
score: 70
}, {
Id: 3,
name: "秦虹",
age: 18,
score: 50
}, {
Id: 4,
name: "海庆",
age: 19,
score: 76
}, {
Id: 5,
name: "蛋娃",
age: 14,
score: 27
}, ];
var html = "";
html += template("tpl", {
list: arr
});
document.getElementById("load").onclick = function() {
document.getElementById("tb").innerHTML = html;
}
</script>
上面的模板引擎全都是 <%= %>
<% %>
不便于查看维护,下面改进一下,使用简洁一点的方法
// 模板引擎改进
<script type="text/html" id="tpl">
<!-- 遍历 -->
{
{
each list value }}
<tr>
<td> {
{
value.Id }} </td>
<td> {
{
value.name }} </td>
{
{
if(value.age >= 18) }}
<td>成年</td>
{
{
else }}
<td>未成年</td>
{
{
/if }}
<td>
{
{
value.score }}
</td>
</tr>
{
{
/each }}
</script>
标签模板 他本质上不是模板 而是函数的另外一种调用形式 其实我们所说的标签 就是我们事先封装好的函数 他的参数就是后面拼接的字符串 以${} 作为分隔符 固定的字符串 放进数组里面 然后 ${} 里面的变量依次作为后面的参数
标记模板 (函数)的参数:
示例:
var name = "jack";
var boy = "男孩";
var girl = "女朋友";
var str = `我的名字叫${
name},我是一个穷穷的${
boy},我有一个很漂亮的${
girl}`;
console.log(str);
function intro(parts) {
console.log(parts);
// ["我的名字叫",",我是一个",","穷穷的",",我有一个漂亮的","]
console.log(arguments);
// 伪数组转换真数组
var res = Array.prototype.slice.call(arguments, 1);
console.log(res);
var s = "";
for (let i = 0; i < res.length; i++) {
s += `${
parts[i]}${
res[i]}`;
}
s += `${
parts[parts.length-1]}`;
return s;
}
var feature = "很绅士的";
var hi = "大家好";
var str = intro `${
hi},我的名字叫${
name},我是一个${
feature}穷穷的${
boy},我有一个很漂亮的${
girl}`;
console.log(str);
Demo
<!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>
<textarea name="" cols="30" rows="10" id="txt"></textarea>
<button id="btn">提交</button>
<div id="container"></div>
<script>
var txt = document.getElementById("txt");
var container = document.getElementById("container");
document.getElementById("btn").onclick = function() {
console.log(txt.value);
container.innerHTML = protect `${
txt.value}${
txt.value}`;
}
function protect(parts) {
console.log(parts);
console.log(arguments);
var res = Array.prototype.slice.call(arguments, 1);
console.log(res);
var str = "";
for (let i = 0; i < res.length; i++) {
var s = res[i].replace(/, "<").replace(/>/g, ">");
console.log(s);
str += parts[i] + s;
if (i === res.length - 1) {
str += parts[i + 1];
}
}
return str;
}
</script>
</body>
</html>
ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法
function fn(x,y) {
y = y || "world";
console.log(x,y);
}
fn("hello"); // hello world
fn("hello",""); // hello world
上面的代码会检查 函数 fn
的 y
有没有赋值,如果没有,就指定默认值为 world
。这种写法缺点在于,如果参数 y
赋值了,但是赋的值是 false
那么该赋值就起不到作用,就像上面代码的最后一行,参数y
等于空字符,结果被改为默认值。
所以ES6 允许为函数的参数设置默认值,即 直接写在参数定义的后面。
function add(a,b,c = 10) {
return a + b + c;
}
let res = add(10,20);
console.log(res); // 40
与解构赋值结合
// 不使用解构赋值的情况
function content(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);
}
// 使用解构赋值
function content({
host = '127.0.0.1',username,password,port }) {
console.log(host);
console.log(username);
console.log(password);
console.log(port);
};
content({
host: 'qq.com',
username: 'root',
password: 'root',
port: 8080
});
我们可以看到不使用解构赋值的时候,每次都需要写 options.
但是使用了对象的解构赋值就简化了很多操作,而且还可以赋默认值。
ES6 允许使用“箭头”(=>
)定义函数。
var fn = n => v;
// 等同于
var fn = function(n) {
return v;
}
1.只有一个参数 并且函数体只有一句话 那么参数可以省略() 返回值可以省略 return 函数体省略了{}
var fn = n => 123;
var r = fn();
console.log(r);
2.如果函数体没有函数 或者有多个参数 那么参数中的()就不能省略
var fn = (x,y) => x + y;
3.如果箭头函数的函数体有多条代码 那么就不能省略 {} 以及返回值也不能省略return
var study = () => {
var lesson = "语文";
return "好好学习,天天向上";
}
4.如果函数体只有一句话 并且 返回值是对象 那么 这个返回值必须使用()包起来
函数体使用的是 {} 对象也是 {}, {}大括号会被解释为代码块
// 报错
var info = (name, age) => {
name: name,
age: age
};
// 不报错
var info = (name, age) => ({
name: name,
age: age
});
5.如果对象只有一个键值对 那么不会报错 但是也没有正确的值
因为js引擎在解析的时候 {} 默认解析为函数体结构 函数体代码 name : name;
let foo = name=> {
name:name };
foo("张三") // undefined
在ES6里面 箭头函数的this指向 是继承自父级执行上下文中的this
箭头函数没有自己的this, 他的this是从父级继承而来的 箭头函数的this在定义的时候就已经确定了
var name = "我在window里面";
var obj = {
name: "我是obj里面的",
fn: function() {
console.log(this);
var that = this;
setTimeout(function() {
console.log(this.name); // "我在window里面"
console.log(that.name); // "我是obj里面的"
}, 1000)
}
}
obj.fn();
setTimeout
默认的this是指向 window
的,这里我们使用 that
保存了 了 obj
的this,所以this.naem是指向window的,而that.name 是obj。下面我们来看看箭头函数里面的this
var name = "我在window里面";
var obj = {
name: "我是obj里面的",
fn: function() {
console.log(this);
var that = this;
setTimeout(() => {
console.log(this.name); // "我是obj里面的"
console.log(that.name); // "我是obj里面的"
}, 1000)
}
}
obj.fn();
that.name
是指向 obj
的,上边已经测试过没什么问题,那 this.name
怎么也变成了 “我是obj里面的” 呢?再看以下代码:把 fn
也变成箭头函数
var name = "我在window里面";
var obj = {
name: "我是obj里面的",
fn: () => {
console.log(this);
var that = this;
setTimeout(() => {
console.log(this.name); // "我在window里面"
console.log(that.name); // "我在window里面"
}, 1000)
}
}
obj.fn();
嗯?怎么都变成 “我在window里面” ? 想想开头的概述:“箭头函数没有自己的this, 他的this是从父级继承而来的” 。所以,我们得知,箭头函数的父级this指向哪里,那么箭头函数的this就指向哪里
1.不能作为构造函数实例化对象
let Person = (name,age) => {
this.name = name;
this.age = age;
}
let me = new Person('xiao',30);
console.log(me);
// 报错:Person is not a constructor
2.不能使用arguments 变量
let fn = () => {
console.log(arguments);
}
fn(1,2,3,4);
// 报错:Identifier 'fn' has already been declared
应用一:点击div 2s 后变成【pink 色】
// 原来的做法
<div id="ad"></div>
<script>
// 点击div 2s 后变成【pink 色】
let ad = document.getElementById("ad");
ad.addEventListener("click", function() {
// 保存this的值,用于定时器内(因为定时器的this指向window)
let _this = this;
setTimeout(function() {
_this.style.backgroundColor = "pink";
}, 2000);
});
</script>
现在可以直接使用箭头·函数实现
// 点击div 2s 后变成【pink 色】
let ad = document.getElementById("ad");
ad.addEventListener("click", function() {
setTimeout( () => {
this.style.backgroundColor = "pink";
}, 2000);
});
应用二:从数组中返回偶数的元素
let arr =[1,6,9,10,500,25];
let res = arr.filter(function(item) {
return item % 2 === 0;
});
console.log(res); // 6 10 500
// 使用箭头函数
let arr =[1,6,9,10,500,25];
let res = arr.filter((item) => item % 2 === 0 );
console.log(res); // 6 10 500
1.普通函数的this : this就是谁调用 this就指向谁 this是在调用的时候确定的
function f1() {
console.log(this);
}
f1(); //window
window.f1(); //window
2.对象里面的方法 它里面的this指向当前这个对象
var obj = {
a: "111",
b: "222",
print: function() {
console.log(this);
}
}
obj.print();
var fn = obj.print;
var fn = obj.print();
fn = function() {
console.log(this);
}
window.fn(); // this指向window
obj.f2 = fn;
obj.f2(); // this指向obj
3.定时器里面的this, 如果没有特殊的指向 那么 setInterval和setTimeout里面的回调函数的this一定是指向window
Symbol 是ES6新引入的一种基本数据类型 表示的是 独一无二的值 他是js里面 第六种基本数据类型