day12-ES6新增语法
- ECMAScript6在保证向下兼容的前提下,提供大量新特性 ,大多数只允许在严格模式下使用。
- ES6 版本的开发版 => babel => ES5 的发布版;
1. 严格模式
- 严格模式通过抛出错误来消除了一些原有静默错误。
- 严格模式修复了一些导致 JavaScript引擎难以执行优化的缺陷:有时候,相同的代码,严格模式可以比非严格模式下运行得更快。
- 严格模式禁用了在ECMAScript的未来版本中可能会定义的一些语法。
范围:用字符串“use strict”声明的script标签或函数内;
注意:从use strict声明以下才有效,上面的不会起作用。
-
变量的严格
在严格模式之中,变量未经声明不可以赋值;
a = 20; //在严格模式下,报错
var a = 010; //所有的浏览器都支持这种以零(0)开头的八进制语法 console.log(a) //输出8 var a = 0o10; // ES6中只能以0o开头来表示八进制 console.log(a);
"use strict"; var x; delete x; // !!! 语法错误 eval("var y; delete y;"); // !!! 语法错误
-
只读属性不能修改
"use strict"; var str = "abcde"; str.length = 10; //报错 var obj = {}; Object.defineProperty(obj,"a",{ writable:false; value:10 }); obj.a = 20; //报错
-
参数的严格
1.不允许同名参数存在;
2.arguments 和形参彼此分离并不会互相影响;
(function(){ })() //匿名函数后面没有加“;”,在严格模式下会报错,多行连读 (function(){ })()
(function(){ function foo(a,a,b){ console.log(a,b); } foo(1 , 2 , 3); //结果是:2 3 })() (function(){ "use strict" function foo(a,a,b){ console.log(a,b); } foo(1 , 2 , 3); //调用结果会报错 })()
(function() { function foo(a, b) { a = 20; b = 30; console.log(a, b, "arguments", arguments[0], arguments[1]); } foo(1, 2) })(); (function() { "use strict" function foo(a, b) { a = 20; b = 30; console.log(a, b, "arguments", arguments[0], arguments[1]); } foo(1, 2) })() //变量和伪数组冲突 Uncaught TypeError: "use strict" is not a function
function fn(a,b,...arg){ console.log(a,b,arg); } fn(3,5,7,8,9,10); //3 5 (4) [7, 8, 9, 10]
-
指针的严格
如果你指向了window, 那么就把指针指向抛空;
"use strict"; var obj1 = {}; Object.defineProperty(obj1, "x", { value: 42, writable: false }); obj1.x = 9; //给不可写属性赋值, 抛出TypeError错误 var obj2 = { get x() { return 17; } }; obj2.x = 5; // 给只读属性赋值, 抛出TypeError错误 var fixed = {}; Object.preventExtensions(fixed); fixed.newProp = "ohai"; // 给不可扩展对象的新属性赋值, 抛出TypeError错误 delete Object.prototype; // 抛出TypeError错误
-
反射 字符串反射对象
反射机制指的是程序在运行时能够获取自身的信息。例如一个对象能够在运行时知道自己有哪些方法和属性。
var obj0 = {}; var obj1 = {}; var obj2 = {}; var obj3 = {}; for (var i = 0; i < 4; i++) { eval("obj" + i).a = i; }
-
JSON
JSON 规则 key值必须用双引号 !
" : 转义字符
var json = '{"a" : "10"}' console.log(JSON.parse(json)); //{a: "10"} var obj = { a : 10} console.log(JSON.stringify(obj)) //{"a":10}
2.ES6新增属性
-
let关键字
- let 语句声明一个块级作用域的本地变量 。
- let命令不存在变量提升,所以在声明前调用变量,都会报错,这在语法上,称为“暂时性死区”(temporal dead zone,简称TDZ)。
- 比较var: var 定义的变量是
window
的属性,而let a不是window
的属性,无法通过this.a
调用。
for(var i = 0; i < 5; i ++){ setTimeout(function(){ alert(i); },1000); } //输出5 5 5 5 5 for(let i = 0; i < 5; i ++){ setTimeout(function(){ alert(i); },1000); } //输出0 1 2 3 4 //var i = 0,定义的不是局部变量,而是全局变量,这里不会形成闭包。
- 使用未声明的变量
var varTest; let letTest; console.log(varTest); //输出undefined(注意要注释掉下面一行才能运行) console.log(letTest); //直接报错:ReferenceError: letTest is not defined
- 重复声明同一个变量
var varTest = 'test var OK.'; let letTest = 'test let OK.'; var varTest = 'varTest changed.'; let letTest = 'letTest changed.'; //直接报错:SyntaxError: Identifier 'letTest' has already been declared console.log(varTest); //输出varTest changed.(注意要注释掉上面letTest变量的重复声明才能运行) console.log(letTest);
- 变量作用域
var varTest = 'test var OK.'; let letTest = 'test let OK.'; { var varTest = 'varTest changed.'; let letTest = 'letTest changed.'; } console.log(varTest); // 输出"varTest changed." // 内部"{}"中声明的varTest变量覆盖外部的letTest声明 console.log(letTest); // 输出"test let OK." // 内部"{}"中声明的letTest和外部的letTest不是同一个变量
- 块级作用域
// eg : function m(){ console.log("111111111") } if( flag > 5) { function m(){ console.log("2222222222") } } m(); //在ES5中,函数m会在第二次定义时被覆盖。 结果: 222222222222 //在ES6中,则会出现 111111111111的结果
-
const常量
常量创建的是一个只读的引用,它的值不能通过重新赋值来改变,并且不能重新声明。
const obj = {}; obj.a =10; //报错 obj = {}; //一些既定的,不需要改变的数据都可以放在常量中
-
箭头函数
这个我个人不推荐使用,待会再说原因,先讲优点!
- 先看区别吧,原来的写法
var test = function(x){ return x+2; }
- 使用箭头函数
var test = x=>x+2; //看起很简单吧? 省略了function、return关键字和大括号。 //使用方法跟以前一样没区别test(5); 结果: 7
- 还有好处就是,自动绑定this,或者说this指向不发生改变
var obj = { left : 200, move : function(){ setTimeout(function(){ //this.left = 100; //以前这里不能写this },1000); } }
- 使用了箭头函数
var obj = { left : 200, move : function(){ setTimeout( ()=>{ this.left = 100; },1000); } }
- 当然也有一些缺陷
箭头函数是不能new的,它的设计初衷就跟构造函数不太一样
-
箭头函数如果要返回一个JSON对象,必须用小括号包起来
var test = ()=>({id:3, val=20})
箭头函数现在非常流行,但我个人并不觉得它有那么美好。
主要是因为,这样的设计对代码的可读性伤害太大了
引用国际著名OO专家、敏捷开发创始人 马丁.福勒的一句名言:任何一个傻瓜都能写出计算器可以理解的代码。惟有写出人类容易理解的代码,才是优秀的程序员。
我们读代码的时间和写代码的时间比率是10:1。这意味着我们大部分时间都在阅读老代码,以便于之后新代码的编写。因为读代码占得比重太大了,因此我们希望在阅读代码的时候能够更加轻松,即便在编写代码的时候需要费点劲。这一段好像也是他老人家说的,我觉得很有道理。
省略掉一个function单词,并不能给开发效率提高多少,但牺牲的却是最基本的代码可读性除了数学运算,我们几乎从来不用符号表示一些复杂的含义。
甚至以前有人质疑过JQ关于each方法的设计思想,认为它屏蔽了程序最基本的逻辑,那就是循环代码的可读性收到了严重伤害。不过我们仔细看看forEach、map、filter这些函数,尽管它们屏蔽了for循环,但分析这些单词的含义:each 每个map 映射(一一对应)filter 过滤它们其实全部都隐含的表达了遍历的意思。
回过头来我们再看,从function到 =>,这东西连个象形符号都算不上这就是为什么数学有那么多的符号可以用来交流,但我们却从不把它称为语言。
- 吐槽归吐槽,还是要学的
-
(参数1, 参数2, …, 参数N) => { 函数声明 }
相当于:(参数1, 参数2, …, 参数N) =>{ return 表达式; }
-
(参数1, 参数2, …, 参数N) => 表达式(单一)
当只有一个参数时,圆括号是可选的:
(单一参数) => {函数声明}
单一参数 => {函数声明} -
没有参数的函数应该写成一对圆括号。
() => {函数声明}
箭头函数中不支持arguments,用剩余运算符代替了。
箭头函数的this一旦确定,不可改变
var add = (1, 2) => a + b; console.log(add(1, 2)); //3 var arr = [1,2,3,4,5]; var res = arr.map(item => item * 2); console.log(res); //[2,4,6,8,10] var res = arr.filter(item => item > 3); console.log(res); //[4,5] var arr = [1, 5, 2, 3, 8, 4, 5, 9, 0, 6]; arr.sort(function(a, b) { return a - b; }) arr.sort((a, b) => a - b);//[0, 1, 2, 3, 4, 5, 5, 6, 8, 9]
- 一点小缺陷
- 箭头函数是不能new的,它的设计初衷就跟构造函数不太一样
- 箭头函数如果要返回一个JSON对象,必须用小括号包起来
var test = ()=>({id:3, val=20})
- 箭头函数的指向,是看它的外围this
var obj = { a: 1, init: function() { var div = document.createElement("div"); Object.assign(div.style, { width: '50px', height: "50px", backgroundColor: "red" }); document.body.appendChild(div); div.addEventListener("click", e => { // console.log(this);//因为有箭头函数,指向obj,不再指向div this.clickHandler(e); }); }, clickHandler: function(e) { // console.log(this);this指向obj this.a++; e.currentTarget.style.backgroundColor = "blue"; console.log(this); } } obj.init();
-
解构赋值
解构赋值主要分为对象的解构和数组的解构 。
- 数组赋值(中括号赋值)
// 以前我们定义多个变量,可以这样写: var x = 10, y = 20, z = 30; // 现在我们可以这样写: let [x, y, z] = [10, 20, 30]; let [x, y, z] = [10, 20]; //z为undefined //数组赋值允许嵌套: let [x, [a,b], y] = [10, [15,18], 20]; //这并不是在定义数组,而是通过等号两边的结构匹配,进行赋值。 //如果写的不够规范,浏览器并不会报错,依然赋值成功 let [x,y] = [10,20,30]; //结果x=10,y=20 let [x,[a,b],y] = [10,[5],20]; //结果x=10,a=5,y=20,b=undefined
- 交换变量
var a = 3, b = 4; [a, b] = [b, a]; console.log(a, b);//4,3
- 对象赋值(大括号赋值)
// 声明的变量和属性是一致的时候可以直接内容并赋值给声明的变量 let obj = { hello : "hello world" } let { hello } = obj.hello; console.log(hello); //hello world // 将hello 更名为 hillo; let obj = { hello : "hello world" } let { hello : hillo } = obj; //取出hello的值,并替换成hillo console.log(hillo); //hello world console.log(obj.hello); //hello world var obj={a : 1, b : 2, c : {a : 3, b : 4}}; var {a, b, c : {a : a1, b : b1}} = obj; console.log(a, b, a1, b1); //1 2 3 4 console.log(c); //报错,c is not defined
let {a = 10, b = 20 } = {a : 30}; console.log(a, b); // 30 20 let [a, b, c = 0] = [1, 2];//c的默认值是0
- 解构 JSON
var str = '{"a":1,"b":2}'; var {a, b} = JSON.parse(str); console.log(a, b); //1 2 var str = "[1,2,3,4,5]"; var [a, b, c, d, e] = JSON.parse(str); console.log(a, b); //1 2
- 解构字符串
var str = "abcdef"; var [a, b, c, d, e] = str; console.log(a, b, c, d, e); //a b c d e var { length: a1 } = str; console.log(a1, str); //6 "abcdef"
- 参数传值
function fn([a, b, c = 0]) { console.log(a + b + c); //4 } fn([1, 3]); let { a = 10 , b = 20} = { a : 20 , b : 30} //相当于: 实参 = > 形参 function foo( { a = 20, b = 30, opt = "+"} = {}){ console.log(a , b , opt) //40 30 + } foo({a:40}); function foo( { a = 20, b, opt = "+"} = {}){ console.log(a , b , opt) //40 undefined + } foo({a:40});
-
扩展运算符
let obj = { a : 1, b : 2, c : 3 } let targetObj = { ...obj, d : 4 , e : 5 } console.log(targetObj); //{a: 1, b: 2, c: 3, d: 4, e: 5} // 两个对象合并的方法; function newObject(obj){ return{ ····obj } } var obj = { a : 1, b : 2, c : 3 } console.log(newObject(obj)); //{a: 1, b: 2, c: 3, d: {…}} console.log(newObject(obj) === obj); //false obj.a = 100; console.log(newObject(obj).a); //100 //通过函数进行浅克隆:此时输出100是因为输出的时候,又重新克隆了一边
-
剩余操作符
将一个不定数量的参数表示为一个数组。
let [a,...b] = [1,2,3,4,5,6,7,8,9,0]; console.log(a,b); //1 (9) [2, 3, 4, 5, 6, 7, 8, 9, 0]
-
字符串扩展方法
- includes()
判断字符串中是否有该字符,返回布尔值,可以从第几位开始判断
console.log("asbdwsa".includes("b",3));//false
- startWith()
判断字符串中从某个位置开始第一个元素是不是这个字符,如果没有指定位置就是从头开始
console.log("abcedsjks".startsWith("a",0));//true
- endsWith()
判断字符串中某个字符是否是最后一个
console.log("abcedsjks".endsWith("s"));//true
- repeat()
让某个字符串重复若干次
console.log("abc".repeat(2));//abcabc
- trim()
去除字符串两端的前后空格
console.log(" abc de ".trim()); //abc de
- padStart(参数1,参数2)
在前面增加指定字符,不足参数1的个数时,就补几个参数2
console.log("aaa".padStart(5,"$"));
- padEnd()
与padStart()同理,在后面增加指定字符,补足要求的长度。
- 模板字符串
- 通过${}替换字符串
- ` 与“最大的区别是,``它支持换行
var sss = 123 var templateString = `hello ${sss} world`; function fn(a){ console.log(a);//["4", raw: Array(1)] } fn `4`;
- EJS是一个JavaScript模板库,用来从JSON数据中生成HTML字符串。
- js语句直接写在<% %>中
- 绑定的数据写在<%= %>中
<script src="./libs/ejs.js"></script> var arr = [1, 2, 3, 4, 5, 6]; // 模板 => 只包裹JS , 不包裹HTML; var template = ` <% for(var i = 0 ; i < arr.length ; i ++){ %> <li> <a href="#"><%= arr[i] %> </a></li> <% } %> ` var res = ejs.render(template, { arr: arr }) // console.log(res); document.getElementById("list").innerHTML = res;
-
字符串模版封装
<div id="list"></div> <script type="text/html" id="template"> <% for(var i = 0 ; i < arr.length ; i ++){ %> <li> <a href="javascript:void(0)"> <%= arr[i] %> </a> </li> <% } %> </script> var renderData = {} var tem = document.getElementById("template"); var list = document.getElementById("list"); Object.defineProperty(renderData, "data", { set: function(val) { var html = render(tem.innerHTML, val); list.innerHTML = html; this.$data = val; }, get: function() { return this.$data; } }) function render(template, data) { var html = ""; var normalReg = /<%([^=].*?)%>/g; var outputReg = /<%=(.*?)%>/g; template = template.replace(normalReg, `\`); $1 print(\``); template = template.replace(outputReg, `\`); print($1); print(\``); template = `print(\` ${ template } \`)`; // console.log(template); eval(template); function print(str) { html += str; } } renderData.data = [1, 2, 3, 4, 5, 6, 7, 8];
-
Symbol
唯一,不参与运算
let a = Symbol(5); let b = Symbol(5); console.log(a == b); //false
- 取出Symbol中的唯一值
var a = Symbol(5); console.log(a.toString().match(/^Symbol\((.*?)\)$/)[1]);
var obj = {}; var s = "b"; obj["a"] = 5; obj[s] = 10; obj[{a:1}] = 20; console.log(obj[{a:2}]); //20 obj[Symbol()] = 20; //Symbol类型不会转换为字符串 console.log(obj[Symbol()]);//undefined
- 消除魔术字符串
var state = ""; const RUN = Symbol(); const WALK = Symbol(); const JUMP = Symbol(); const STAND = Symbol(); function setState(state){ switch(state){ case RUN: console.log("跑"); break; case WALK: console.log("走"); break; case JUMP: console.log("跳") break; case STAND: console.log("站立"); break; } }
-
set
- set不允许重复的列表,
- 删除、添加和查找的速度高于数组,但是无法寻找到关系数据
- set中只有size ,没有length
let a = new Set(); a.add(2); a.add(3); a.add(5); a.add(3); console.log(a.has(3)); //true 判断列表中是否含有该值 a.delete(); console.log(a); //{2, 3, 5}
- for of遍历
let a = new Set([2,3,4,5,6,3,2,4]); for(let value of a){ console.log(value); }
- 数组去重
var arr = [2, 3, 5, 6, 7, 3, 2, 4, 2, 1, 2, 3, 4, 5]; arr = Array.from(new Set(arr)); console.log(arr);
-
Map hasMap
对象保存键值对
let map = new Map(); map.set("name","zhangsan"); map.set("age", 30); map.set("sex", "男"); console.log(map.get("age")); //30; console.log(map.size); // 3 console.log(map.has("age")) //true 判断是否有当前属性 map.delete("age"); console.log(map);//{"name" => "zhangsan", "sex" => "男"} map.clear(); //清空map console.log(map); // {}
- 遍历键
for(let value of map.keys()){ console.log(); }
- 遍历值
for(let value of map.value()){ console.log(value); }
- 遍历键和值
for(let value of map){ console.log(value); } map.forEach(value, key) =>{ console.log(value, key); }
-
生成器函数
- function* 这种声明方式(
function
关键字后跟一个星号)会定义一个生成器函数 (generator function),它返回一个 [Generator
] 对象。 - 生成器函数在执行时能暂停,后面又能从暂停处继续执行。
- yield用来在生成器函数中定义暂停位置和进行数值的返回,还有进行执行权的移交(yield *);
function* getSum(a,b){ yield a; yield b; let sum = a + b; return sum; } var sum = getSum(3, 5); console.log(sum.next());//{value: 3, done: false} console.log(sum.next());//{value: 5, done: false}
function* getSum(a, b) { a++; yield a; b--; yield b; let sum = a + b; yield sum; return sum; } var sum = getSum(3, 5); var first = sum.next(); while (!first.done) { console.log(first.value);//4 4 8 first = sum.next(); } var a = sum.next(); console.log(a.done);//true
异步过程强制变成同步阻塞
function* setNums() { yield setTimeout(fn1, 2000); yield setTimeout(fn2, 2000); } function fn1() { console.log("aaaa"); a.next(); } function fn2() { console.log("bbb"); } var a = setNums(); a.next();
避免了下面这种函数的嵌套
setTimeout(function() { setTimeout(function() { setTimeout(function() { }, 2000); }, 2000) }, 2000);
- function* 这种声明方式(