2017-08-21-day07
ES6:
1. let和块作用域
- 问题1: hoist,打乱了程序的正常执行顺序
解决: 今后用let代替var声明变量
原理: let通过禁止在当前作用域内提前使用未声明的变量,来避免声明提前
强调: 如果已经用let声明的变量,不能再用var重复声明
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>//hoist
console.log(a);//报错!
let a=10;//不提前,且之前不允许出现a
console.log(a);//10
var b=10;
function fun(){//hoist
console.log(b);//报错!
let b=100;//不提前,且在当前作用域内的前半部分不允许提前出现b
console.log(b);
}
fun();//报错
console.log(b);
</script>
</body>
</html>
- 问题2: js没有块级作用域, 块内的变量即使不执行,也会影响块外的变量:
块级作用域: 程序中的一对儿{}之间,称为代码块
js vs java: Java 三级作用域: 全局,函数,块
js 两级作用域: 全局,函数,没有块级作用
解决: 用let代替var
规定: let声明的变量仅在当前块内有效!
原理: 自动在块作用域位置创建匿名函数自调,划分临时作用域。
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
var t=0;
function conn(){
t+=0.3; console.log("建立连接,耗时0.3s");
}
function query(){//hoist
t+=0.8; console.log("查询数据,耗时0.8s");
var err=false;
if(err){//不执行!
//(function(){
let t=new Date();
console.log("出错啦! at:"+t);
//})();
}
}
conn(); query();
console.log("共耗时:"+t+"s");//1.1
</script>
</body>
</html>
- 问题3: 原来在for/while/if...内声明的变量,出了结构,就不能使用
解决: 今后,凡是出了结构,还要使用的变量,必须在结构外提前声明。
总结: 今后所有变量都要用let声明,而不var
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<button>click me!</button>
<button>click me!</button>
<button>click me!</button>
<script>
var btns=
document.getElementsByTagName("button");
for(let i=0;i<btns.length;i++){
//(function(i){
btns[i].onclick=function(){
alert(i);
}
//})(i);
// btns[i].onclick=(function(){
// var index=i;
// return function(){
// alert(index);
// }
// })();
//btns[0].onclick=function(){alert(index=0);}
//btns[1].onclick=function(){alert(index=1);}
//btns[2].onclick=function(){alert(index=2);}
}//i=3
//单击 3
//单击 3
//单击 3
</script>
</body>
</html>
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
let sum=0;
for(let i=1/*,sum=0*/;i<=100;sum+=i++);
console.log(sum);//报错! 5050
</script>
</body>
</html>
2. 箭头函数: 简化回调函数
何时: 今后所有回调函数都可用箭头函数简化!
如何:
- 去function改箭头
更简化: - 如果只有一个参数变量,可省略()
- 如果函数体只有一句话,可省略{}
如果仅有一句return,则可省略return
箭头函数特点: 内容this通用!
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
var arr=[23,2,12,3,123,1];
arr.sort(//去function,改=>
(a,b)=>a-b//如果只有一句话,可省略{}
); //如果只有一句return,可省略return
console.log(String(arr));
var arr=[1,2,3,4,5];
arr.forEach(
(val,i,arr)=>arr[i]=val*2
);
console.log(String(arr));
var arr=[1,2,3,4,5];
var evens=arr.map(
val=>val*2//如果只有一个参数变量,可省略()
);
console.log(String(arr));
console.log(String(evens));
</script>
</body>
</html>
箭头函数this
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
var bob={
sname:"Bob",
friends:["jack","rose","tom","jerry","lucy"],
intr(){
this.friends.forEach(
val=>console.log(this.sname+" 认识 "+val)
// function(val){
// console.log(this.sname+" 认识 "+val);
// }.bind(this)
);
}
}
bob.intr();//intr中的this->bob
</script>
</body>
</html>
3.参数增强:
Default:(默认值)
function fun(参数1,参数2,参数3=默认值,参数4=默认值){}
调用时,如果传给参数的值有效,就用传入的值
如果没有传入参数,或传入的参数无效,则用提前指定的默认值
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
function indexOf(arr,val,fromi=0){
//fromi=fromi||0;
for(var i=fromi;i<arr.length;i++){
if(arr[i]===val) return i;
}
return -1;
}
var arr=[1,2,3,4,3,2,1];
// 0 1 2 3 4 5 6
console.log(
indexOf(arr,2), //1
indexOf(arr,2,3),//5
indexOf(arr,5), //-1
indexOf(arr,4,4) //-1
);
</script>
</body>
</html>
REST:(剩余参数列表) 为了代替arguments
arguments的问题:
1. 不是真正的数组类型,无法使用数组类型的API
2. 默认只能获得所有参数值,无法仅选取部分参数值
解决: REST
何时: 今后,都是用REST语法代替arguments
如何:
定义函数: function fun(参数1,参数2,...剩余参数的统称)
调用时: ...剩余参数的统称 会获得一个数组
SPREAD:(散播) 为了代替apply打散数组类型的参数
apply的问题:
1. 必须传入第一个对象参数,代替this
2. 必须将所有参数都放入数组中
解决: spread
何时: 今后只要打散数组类型参数时,首选spread
如何: 定义函数时,参数变量是分开定义的
调用函数时,可用fun(值1,...arr),打散arr数组
以下是一个REST和spread的完整例子
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
//REST: 代替arguments,聚拢多个参数值
function calc1(ename,...sals){
//sals:[10000,500,200,300]
console.log(ename+"的总工资是"+
sals.reduce((prev,val)=>prev+val)
);
}
calc1("Li Lei",10000,500,200,300);
calc1("Han Meimei",3000,4000,5000);
function calc2(base,b1,b2,b3){//要求参数分别传入
console.log(base+b1+b2+b3);
}
//SPREAD:代替apply
//将数组打散为单个参数值,再分发给每个参数变量
var bonus=[1000,2000,3000];//参数值在一个数组中
calc2(10000,...bonus);
var arr=[3,5,2];
//Math.max.apply(null,arr)
console.log(Math.max(...arr));
</script>
</body>
</html>
4. 模板字符串: 简化复杂格式的字符串拼接
何时: 今后所有字符串拼接,都用模板字符串代替+
如何:
- 用反引号`包裹整个字符串
- 在模板字符串内,自动支持换行
- 在模板字符串内,支持动态生成表达式的值
要执行的表达式,必须用${}包裹
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
var price=10,count=5;
console.log(
`单价:¥${price.toFixed(2)}
数量:${count}
============
总价:¥${(price*count).toFixed(2)}`
);
console.log(
String.raw `c:\windows\temp\new folder\index.html`
);
</script>
</body>
</html>
5. 解构: 简化复杂对象的打散和批量赋值
- 数组解构: 等号左右两边都是数组格式; 解构时,用下标对应值和变量
- 对象解构: 等号左右两边都是对象格式; 解构时,用属性名匹配值和变量
- 参数解构: 在调用函数时,如果传入的参数值是数组或对象,也可被解构为单个参数值
//数组解构
var scores = [81, 59, 89];
var [math, chs, eng] = scores;
console.log(math, chs, eng);//81 58 89
//对象解构
var fbb = {
math: 81,
chs: 59,
eng: 89
};
var {
math: a,
chs: b,
eng: c
} = fbb;
console.log(a, b, c);//81 59 89
//数组参数解构
function sum1([math, chs, eng]) {
return math + chs + eng
};
var scores = [81, 59, 89];
console.log(sum1(scores));//229
//对象参数解构
function sum2({
math,//math:math,
chs,//chs:chs,
eng,//eng:eng
}) {
return math + chs + eng
}
var fbb = {
math: 81,
chs: 59,
eng: 89
};
console.log(sum2(fbb)) //229
6: class: 为了简化面向对象:
封装,继承,多态,访问器属性,静态方法
class: 集中定义构造函数和原型对象方法的程序结构
为什么: 更像封装
何时: 只要创建一种新类型,必须先定义class
如何:
class 类型名{
constructor(...){
this.xxx=xxx;
}
原型对象方法(){//类型名.prototype.xxx()
... this.xxx ...
}
}
继承: 2步:
1. 子类型构造函数中借用父类型构造函数:
super(参数值)
super自动指代父类型的构造函数
2. 让子类型继承父类型
class 子类型 extends 父类型
不用再Object.setPrototypeOf
访问器属性:
class 类型名{
constructor(...){
this._age=age
}
get age(){return this._age}
set age(val){
... ...
}
}
以下是一个例子
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
class Flyer{
constructor(fname,speed){
//this->window->call->new
this.fname=fname;
this.speed=speed;
}
fly(){//Flyer.prototype.fly
console.log(
this.fname+"以时速"+this.speed+"飞行");
}
}
class Plane extends Flyer{
constructor(fname,speed,score){
super(fname,speed);
this.score=score;
}
getScore(){
console.log(
"击落"+this.fname+"得"+this.score+"分");
}
}
var f16=new Plane("F16",1000,10);
console.dir(f16);
f16.fly(); f16.getScore();
</script>
</body>
</html>
getter setter例子
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
class Emp{
constructor(id,ename,age){
this.id=id;
this.ename=ename;
Object.defineProperty(this,"_age",{
value:null,
writable:true
})
this.age=age;
Object.seal(this);
}
get age(){return this._age}
set age(val){
if(val>=18&&val<=65)
this._age=val;
else
throw new RangeError(
"年龄必须介于18~65之间")
}
}
var eric=new Emp(1001,"Eric",25);
console.dir(eric);
eric.age=26;
console.log(eric.age)
eric.age=-2;
</script>
</body>
</html>