ES6的名称为ESMAScript2015(es2015),是2015年6月份发行的,它是最新ECMAScript的代表版本,一是因为相对与es5变化比较大,二是因为它的发行让标准命名规则发生了变化,ES6更准确的缩写名称应该叫ES2015,
ES6的出现最主要的解决了以下几个问题:
//let
console.log(c) //Uncaught ReferenceError: Cannot access 'c' before initialization
let c = 100
//var
console.log(c) //underfined
var c =100
因为 let 不存在变量提升,所以上面 let 定义的变量会报错,报错的意思是我们想要打印 c 的值,必须先要初始化,然后再去使用它,必须遵循 先声明,后使用 的使用规则。
var 定义的变量结果为 underfined 代码相当于:
var c;//变量提升,但是值不能提升,所以是underfined
console.log(c) //underfined
c = 100
//let
let c = 100
let c = 200 //报错 Uncaught SyntaxError: Unexpected identifier
//var
var c = 100
var c = 200
console.log(c) //200 正常运行,会覆盖前一个值
ES5 中作用域有:全局作用域、函数作用域,没有块作用域的概念。
ES6 中新增了块级作用域,块作用域由**{ }**包括,if 语句和 for 语句里面的 { } 也属于块作用域。
//let
function fun(){
let n = 10
if(true){
let n = 20 //与上面的 n 不属于同一个作用域,所以不受影响
}
console.log(n) //10
}
fun()
//var
function fun(){
var n = 10
if(true){
var n = 20 //会覆盖上面的 n 的值
}
console.log(n)//20
}
fun()
const a =2;
a=3;//错误 Uncaught TypeError: Assignment to constant variable.
const a; // 错误 Uncaught SyntaxError: Missing initializer in const declaration
//(const声明中缺少初始化程序)
{
const a = 3
}
console.log(a) // Uncaught ReferenceError: a is not defined
但是用 const 声明的对象的属性是可以更改的,const 实质上保证的并不是变量的值不得改动,而是变量指向的 内存地址 的值不得改动,对于简单类型数据,值就保存在变量指向的内存地址中,相当于常量。而对于复合型的数据,变量指向的是内存地址保存的是一个指针。const 只能保证指针是不可以被更改,但指针指向的数据结构是可以被改变的。
//以下代码正常执行
const obj = {
}
obj.name="xiaoke"
console.log(obj) // {name: "xiaoke"}
const arr = []
arr.push("xiaoke")
console.log(arr) // ["xiaoke"]
const arr = ['a','b','c']
const [a1,a2,a3] = arr
console.log(a1,a2,a3)//a b c
const arr = ['a', 'b', 'c']
const [, , a3] = arr //用逗号进行占位
console.log(a3)//c
const arr = ['a', 'b', 'c']
const [, ...arr2] = arr
console.log(arr2) //['b','c']
存在
const arr = ['a', 'b', 'c', 'd']
const [a1, a2, a3, a4 = 'dd'] = arr
console.log(a1, a2, a3, a4)
console.log(arr)//a b c d
不存在
const arr = ['a', 'b', 'c']
const [a1, a2, a3, a4 = 'dd'] = arr
console.log(a1, a2, a3, a4)
console.log(arr)// a b c dd
const obj = {
name: "xuke", age: 22 }
const {
age } = obj
console.log(age) //22
const obj = {
name: "xuke", age: 22 }
const {
hobby = "eat" } = obj
console.log(hobby) // eat
3.如果我们代码中已经定义了一个与obj中相同的属性名,我们可以使用下面的方式给属性名一个别名,然后再取出
const obj = {
name: "xuke", age: 22 }
const name = 'keke'
const {
name } = obj
console.log(newname) //会报错:SyntaxError: Identifier 'name' has already been declared
改正
const obj = {
name: "xuke", age: 22 }
const name = 'keke'
const {
name : newname } = obj
console.log(newname) //xuke
const name = 'xuke'
const str = `my name is ${
name}`
console.log(str) // my name is xuke
const name = 'xuke'
const str = `my name
is ${
name}`
console.log(str)
//my name
//is xuke
const str = `my age is ${
true ? 22 : 10}`
console.log(str) //my age is 22
const message = 'welcome to beijing.'
console.log(message.startsWith('welcome'))//true
console.log(message.startsWith('wel'))//true
console.log(message.endsWith('beijing'))//false
console.log(message.endsWith('.'))//true
console.log(message.includes('bei'))//true
function num(x = 20) {
return x += x
}
//x不传为undefined时 ,会使用我们的默认值 20
console.log(num()) //40
//传值的时候会使用我们传的值
console.log(num(10)) //20
注意:我们对参数设置默认值的时候,要把带有默认值的参数放到最后面
//错误的写法
function num(a = 20, x) {
return a + x
}
//正确的写法
function num(a , x = 20) {
return a + x
}
let a = [1,2,3];
let b = [4,5,6];
let c = [...a,...b]; // [1,2,3,4,5,6]
function f(a,b,c){
console.log(a,b,c)
}
let args = [1,2,3];
// 以下三种方法结果相同
f.apply(null,args)
f(...args)
f(1,2,3)
//===========
function f2(...args){
console.log(args)
}
f2(1,2,3) // [1,2,3]
function f3(){
console.log(Array.from(arguments))
}
f3(1,2,3) // [1,2,3]
//数组
var a = [1,2,4]
var b = [...a]
a.push(6)
console.log(b) // [1,2,4]
//对象
var a = {
a:1}
var b = {
...a}
a.a = 5
console.log(b.a) // 1
使用
const fun = function () {
return 'hello'
}
const fun2 = () => 'hello'
console.log(fun()) // hello
console.log(fun2())// hello
//===
const nums = [1, 2.3, 2, 46, 6, 4, 3, 2]
const result = nums.filter(num => num % 2 === 0)
console.log(result)//[ 2, 46, 6, 4, 2]
箭头函数不会改变this的指向
const Person = {
name:'xuke',
say1:function(){
console.log(this.name) //xuke 指向Person
},
say2:()=>{
console.log(this.name) //undefined 指向window
}
}
Person.say1()
Person.say2()
setTimeOut情况:
const Person = {
name: 'xuke',
say1: function () {
setTimeout(function () {
//setTimeOut函数体里面的函数题被放到全局作用域去调用
console.log(this.name) //undefined
}, 1000)
setTimeout(() => {
//setTimeOut函数体里面的箭头函数始终指向当前作用域中的this
console.log(this.name) //xuke
}, 1000)
}
}
Person.say1()
属性名
和值
相等的时候,我们可以把值
省略:const name = 'xuke'
//普通用法
const obj1 = {
name:name,
age:12
}
//字面量用法
const obj2 = {
name,
age:12
}
console.log(obj1) //{ name: 'xuke', age: 12 }
console.log(obj2) //{ name: 'xuke', age: 12 }
[]
中写入我们的表达式//es2015之前
const name = 'xuke'
const obj = {
name,
age:12
}
const obj2 = {
}
obj2[obj.name] = 'keke'
console.log(obj2) //{ xuke: 'keke' }
//es2015
const name = 'xuke'
const obj = {
name,
age:12
}
const obj2 = {
[obj.name]:'keke'
}
console.log(obj2) //{ xuke: 'keke' }
Object.assign方法用于对象的合并,将源对象(source)
的所有可枚举属性,复制到目标对象(target
),如果目标对象与源对象有同名属性
,或多个源对象有同名属性,则后面的属性覆盖前面的属性
const source = {
a:123,
b:123
}
const target ={
a:456,
c:111
}
console.log(Object.assign(target,source)) //{a:123,c:111,b:123}
还可用于对象的浅拷贝,
const source = {
a:123,
b:123,
name:{
cc:'ccc'
}
}
const target = Object.assign({
},source)
target.a = 333
console.log(target) //{ a: 333, b: 123 }
console.log(source) //{ a: 123, b: 123 }
proxy真的用处很大,可是我项目中很少用到,总结一下。
proxy在目标对象的外层搭建了一层拦截
,外界对目标对象的某些操作,必须通过这层拦截.
var proxy = new Proxy(target, handler);
new Proxy()表示生成一个Proxy实例
,target参数表示所要拦截的目标对象
,handler参数也是一个对象,用来定制拦截行为
var target = {
name: 'poetries'
};
var logHandler = {
get: function(target, key) {
console.log(`${
key} 被读取`);
return target[key];
},
set: function(target, key, value) {
console.log(`${
key} 被设置为 ${
value}`);
target[key] = value;
}
}
var targetWithLog = new Proxy(target, logHandler);
targetWithLog.name; // 控制台输出:name 被读取
targetWithLog.name = 'others'; // 控制台输出:name 被设置为 others
console.log(target.name); // 控制台输出: others
targetWithLog 读取属性
的值时,实际上执行的是 logHandler.get
:在控制台输出信息,并且读取被代理对象 target 的属性。
在 targetWithLog 设置属性
值时,实际上执行的是 logHandler.set
:在控制台输出信息,并且设置被代理对象 target 的属性的值
// 由于拦截函数总是返回35,所以访问任何属性都得到35
var proxy = new Proxy({
}, {
get: function(target, property) {
return 35;
}
});
proxy.time // 35
proxy.name // 35
proxy.title // 35
var proxy = new Proxy({
}, {
get: function(target, property) {
return 35;
}
});
let obj = Object.create(proxy);
obj.time // 35
proxy对象是obj对象的原型,obj对象本身并没有time属性,所以根据原型链,会在proxy对象上读取该属性,导致被拦截
var target = {
name: 'poetries',
_age: 22
}
var logHandler = {
get: function(target,key){
if(key.startsWith('_')){
console.log('私有变量age不能被访问')
return false
}
return target[key];
},
set: function(target, key, value) {
if(key.startsWith('_')){
console.log('私有变量age不能被修改')
return false
}
target[key] = value;
}
}
var targetWithLog = new Proxy(target, logHandler);
// 私有变量age不能被访问
targetWithLog.name;
// 私有变量age不能被修改
targetWithLog.name = 'others';
例1:在下面的代码中,我们声明了一个私有的 apiKey,便于 api 这个对象内部的方法调用,但不希望从外部也能够访问 api._apiKey
var api = {
_apiKey: '123abc456def',
/* mock methods that use this._apiKey */
getUsers: function(){
},
getUser: function(userId){
},
setUser: function(userId, config){
}
};
// logs '123abc456def';
console.log("An apiKey we want to keep private", api._apiKey);
// get and mutate _apiKeys as desired
var apiKey = api._apiKey;
api._apiKey = '987654321';
很显然,约定俗成是没有束缚力的。使用 ES6 Proxy 我们就可以实现真实的私有变量了,下面针对不同的读取方式演示两个不同的私有化方法。第一种方法是使用 set / get 拦截读写请求并返回 undefined:
let api = {
_apiKey: '123abc456def',
getUsers: function(){
},
getUser: function(userId){
},
setUser: function(userId, config){
}
};
const RESTRICTED = ['_apiKey'];
api = new Proxy(api, {
get(target, key, proxy) {
if(RESTRICTED.indexOf(key) > -1) {
throw Error(`${
key} is restricted. Please see api documentation for further info.`);
}
return Reflect.get(target, key, proxy);
},
set(target, key, value, proxy) {
if(RESTRICTED.indexOf(key) > -1) {
throw Error(`${
key} is restricted. Please see api documentation for further info.`);
}
return Reflect.get(target, key, value, proxy);
}
});
// 以下操作都会抛出错误
console.log(api._apiKey);
api._apiKey = '987654321';
例2:让我们从一个简单的类型校验开始做起,这个示例演示了如何使用 Proxy 保障数据类型的准确性
let numericDataStore = {
count: 0,
amount: 1234,
total: 14
};
numericDataStore = new Proxy(numericDataStore, {
set(target, key, value, proxy) {
if (typeof value !== 'number') {
throw Error("Properties in numericDataStore can only be numbers");
}
return Reflect.set(target, key, value, proxy);
}
});
// 抛出错误,因为 "foo" 不是数值
numericDataStore.count = "foo";
// 赋值成功
numericDataStore.count = 333;
例3:对于那些调用频繁、运行缓慢或占用执行环境资源较多的属性或接口,开发者会希望记录它们的使用情况或性能表现,这个时候就可以使用 Proxy 充当中间件的角色,轻而易举实现日志功能
let api = {
_apiKey: '123abc456def',
getUsers: function() {
/* ... */ },
getUser: function(userId) {
/* ... */ },
setUser: function(userId, config) {
/* ... */ }
};
function logMethodAsync(timestamp, method) {
setTimeout(function() {
console.log(`${
timestamp} - Logging ${
method} request asynchronously.`);
}, 0)
}
api = new Proxy(api, {
get: function(target, key, proxy) {
var value = target[key];
return function(...arguments) {
logMethodAsync(new Date(), key);
return Reflect.apply(value, target, arguments);
};
}
});
api.getUsers();
Reflect是为操作对象而提供的新API,那么我们为什么要去使用它呢?将Object对象的属于语言内部的方法放到Reflect对象上,即从Reflect对象上拿Object对象内部方法,比较方便,可读性更强。
const object = {
name:'xuke',
age:22
}
console.log(Reflect.has(object,'name'))//true
const object = {
name:'xuke',
age:22
}
console.log(Reflect.set(object,'ww','dd'))//true object中不存在会直接添加
console.log(Reflect.set(object,'name','dd'))//true object中存在会覆盖
console.log(object) //{ name: 'dd', age: 22, ww: 'dd' }
const object = {
name:'xuke',
age:22
}
console.log(Reflect.get(object,'name'))//xuke 属性名存在直接返回
console.log(Reflect.get(object,'ww'))//undefined 属性名不存在返回undefined
const object = {
name:'xuke',
age:22
}
Reflect.deleteProperty(object,'name')
console.log(object) //{age:22}
const object = {
name:'xuke',
age:22
}
console.log(Reflect.ownKeys(object)) //[ 'name', 'age' ]
const object = {
name:'xuke',
age:22
}
const proxy = new Proxy(object,{
get:(target,property)=>{
return Reflect.get(target,property) //获取被访问的属性
},
set:(target,property,value)=>{
Reflect.set(target,property,value) //设置被访问的属性与新值
}
})
console.log(proxy.name) //xuke
proxy.name = 'xiaohaha'
proxy.hobby = 'eat'
console.log(object) //{ name: 'xiaohaha', age: 22, hobby: 'eat' }