ES6入门篇 - 由浅入深对比ES5
简介
最近在学习前端,以前接触的都是ES5,所以趁这个机会来补一补ES6的知识,了解一些ES6的新特性,顺便记一笔,fighting!
常量
ES5 常量定义
Object.defineProperty(window, "PI2", {
value: 3.1415926,
writable: false, //不能写,即为只读
})
console.log(window.PI2);
ES6 常量定义
const PI = 3.1415926;
console.log(PI);
块作用域
ES5中作用域概念
const callbacks = [];
for (var i = 0; i <= 2; i++) {
callbacks[i] = function() {
return i * 2;
}
}
console.table([
callbacks[0](),
callbacks[1](),
callbacks[2](),
])
结果为:
大家可能会疑惑为什么都是6呢?我起初也疑惑,原因是这样的:
最开始的var i = 0
变量处在for循环外,第一个循环话callbacks[i]
中的i取值为0,这都没问题,但
function() {
return i * 2;
}
中的i是对变量的引用,而不是对i变量值的引用。函数体中是一个表达式,而不是一个值,所以这里有一个闭包。即callbacks[0]
在执行的时候,其实i已经变成3了,也就是循环结束了,所以不管执行哪个,结果i的结果都为3。
再来看一个
const callbacks2 = [];
for (let j = 0; j <= 2; j++) {
callbacks2[j] = function() {
return j * 2;
}
}
console.table([
callbacks2[0](),
callbacks2[1](),
callbacks2[2](),
])
结果为:
因为我们使用了let声明了变量j,let声明的变量有一个块作用域的概念,这个时候的闭包完全取决于当前的块作用域,块作用域就是for循环的花括号,即每循环一次生成一个新的作用域。
ES5中块作用域
块作用域,就是用块给代码做一个隔离,比如声明一个函数,只能在这块代码中运行。
我们在ES5中通常使用“立即执行函数”来实现块作用域。
((function() {
const foo = function() {
return 1;
}
console.log("foo()===1", foo() === 1);
((function() {
const foo = function() {
return 2;
}
console.log("foo()===2", foo() === 2);
})())
})())
结果:
这样即实现了块作用域,互不干扰。而在ES6中的块作用域呢?很简单,接着看。
ES6中块作用域
在ES6中指定一个块作用域,直接使用“花括号”即可。
{
function foo() {
return 1;
}
console.log("foo()===1", foo() === 1);
{
function foo() {
return 2;
}
console.log("foo()===2", foo() === 2);
}
console.log("foo()===1", foo() === 1);
}
结果:
是不是就相当清晰和明了了,互不干扰。
箭头函数
箭头函数其实就是函数新的语法,在ES5及之前,声明函数都是使用function:
function name(){
}
但在ES6中,可以这样声明函数:
()=>{
}
那这样做有什么好处呢?一是代码减少了,不用再用function来声明,第二个就是this
的指向有了新的意义,下面用遍历操作来举例说明。
ES5声明函数
var evens = [1, 2, 3, 4, 5];
var odds = evens.map(function(v) {
return v + 1;
});
console.log(evens, odds);
结果:
ES6箭头函数
{
let evens = [1, 2, 3, 4, 5];
let odds = evens.map(v => v + 1);
console.log(evens, odds);
}
结果:
这里的v => v + 1
箭头函数和上面的箭头函数有点不一样啊?其实这样的写法是很正确的。
箭头函数是在小括号()
中声明参数,如果参数只有一个,小括号()
是可以省略的;花括号{}
中是写表达式的,如果花括号{}
中的表达式直接作为返回值的话,是可以省略花括号{}
的。
ES5 this指向
var factory = function() {
this.a = 'a';
this.b = 'b';
this.c = {
a: 'a+',
b: function() {
return this.a;
}
}
}
console.log(new factory().c.b());
结果:
a+
this指向是该函数被调用的对象,函数执行时,this指向的是调用者的function,这里的b是c调用的,this.a
就是c中的a,即为a+。
ES6 this指向
var factory = function() {
this.a = 'a';
this.b = 'b';
this.c = {
a: 'a+',
b: () => {
return this.a;
}
}
}
console.log(new factory().c.b());
结果为:
a
这里this指向结果就和上面的不一样了,箭头函数的this指向是定义时this指向,箭头函数中的thisthis.a
指向的就是
var factory = function() {
}
函数体中的this,函数体中的this指向的就是构造函数中的实例factory
。
所以
b: () => {
return this.a;
}
中的this.a指向的是就是new factory()
的a了。
总而言之就是: obj.f()通常函数f里面的this指向的是调用者obj,但是在箭头函数里面this指向的是定义的对象。
默认参数
ES5中默认参数
需要判断是否传值,未传值则使用默认值,有点麻烦。
function f(x, y, z) {
if (y === undefined) {
y = 7;
}
z = z || 42;
return x + y + z;
}
console.log(f(1));
结果:
46
ES6中默认参数
function f(x, y = 7, z = 42) {
return x + y + z;
}
console.log(f(1, 3));
结果:
46
必选参数校验
function checkParameter() {
throw new Error('can\'t be empty');
}
function f(x = checkParameter(), y = 7, z = 42) {
return x + y + z;
}
console.log(f(1));
try {
f();
} catch (e) {
console.log(e);
}
结果:
ES5可变参数的处理
不确定参数的个数,就需要可变参数,下例为求和方法。
function f() {
var a = Array.prototype.slice.call(arguments);
var sum = 0;
a.forEach(function(item) {
sum += item * 1;
})
return sum;
}
console.log(f(1, 2, 3, 6));
结果:
12
ES6可变参数的处理
function f(...a) {
var sum = 0;
a.forEach(item => {
sum += item * 1;
});
return sum;
}
console.log(f(1, 2, 3, 6));
结果:
12
传入参数中的...
是扩展运算符 可变参数,即为一个数组。
ES5合并数组
合并两个数组内容
var params = ['hello', true, 7];
var other = [1, 2].concat(params);
console.log(other);
结果:
ES6合并数组
var params = ['hello', true, 7];
var other = [
1, 2, ...params
];
console.log(other);
结果:
对象代理
ES3/5数据保护
通过set方法来限制修改。
var Person = function() {
var data = {
name: 'es3',
sex: 'male',
age: 15
}
this.get = function(key) {
return data[key]
}
this.set = function(key, value) {
if (key !== 'sex') {
data[key] = value
}
}
}
// 声明一个实例
var person = new Person();
// 读取
console.table({name: person.get('name'), sex: person.get('sex'), age: person.get('age')});
// 修改
person.set('name', 'es3-cname');
person.set('sex', 'female');
console.table({
name: person.get('name'),
sex: person.get('sex'),
age: person.get('age')
});
结果:
setSex并没有起作用。
ES5数据保护
利用Object.defineProperty API来操作只读,就不能写了。
var Person = {
name: 'es5',
age: 15
};
Object.defineProperty(Person, 'sex', {
writable: false,
value: 'male'
});
console.table({name: Person.name, age: Person.age, sex: Person.sex});
try {
Person.name = 'es5-cname';
Person.sex = 'female';
console.table({name: Person.name, age: Person.age, sex: Person.sex});
} catch (e) {
console.log(e);
}Z
结果:
ES6数据保护
使用Proxy代理对象来限制读写。
let Person = {
name: 'es6',
sex: 'male',
age: 15
};
let person = new Proxy(Person, {
get(target, key) {
return target[key];
},
set(target,key,value){
if(key!=='sex'){
target[key]=value;
}
}
});
console.table({
name:person.name,
sex:person.sex,
age:person.age
});
try {
person.sex='female';
} catch (e) {
console.log(e);
}
结果: