-
let 和 const
let
作为javascript
的变量没有块级作用域而补充的,解决了一部分由变量提升而导致的问题,比如,for循环变量泄露,变量覆盖问题。
const
是声明一个只读的常量。一旦声明,必须立刻初始化。
let
和const
注意事项:- 都不存在变量提升问题。声明
let
变量之前,这个变量是不存在的。 - 都存在暂时性死区。块级作用域存在
let
命令,它声明的变量就绑定了这个区域。在区块里,存在let
,const
,在声明变量之前,该变量不可用。 - 都不允许重复声明。
-
let
,const
,class
命令声明的全局变量,不属于顶层属性。
var a = 1; // 如果在 Node 的 REPL 环境,可以写成 global.a // 或者采用通用方法,写成 this.a window.a // 1 let b = 1; window.b // undefined
- 都不存在变量提升问题。声明
-
变量解构赋值
解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined
和null
无法转为对象,所以对它们解构赋值,都会报错。-
数组的解构赋值
a. 只要模式匹配,等号两边的模式相同,左边的变量就会赋予对应的值;let [a, b, c] = [1, 2, 3];
b. 不匹配的话,值就为undefined;
let [bar, foo] = [1]; // foo 值为undefined
c. 部分匹配就得到部分值;
let [a, [b], d] = [1, [2, 3, 4]]; // a = 1, b = 2, d = 4;
d. 解构赋值允许指定默认值;
let [foo = true] = []; // foo = true
e. 如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值;
function f() { console.log('aaa'); } let [x = f()] = [1];
只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值。
-
对象的解构赋值
a. 正常解构let { foo, bar } = { foo: 'aaa', bar: 'bbb' }; foo // "aaa" bar // "bbb"
b. 如果变量名跟属性名不一样,需要这样写
let { foo: baz } = { foo: 'aaa', bar: 'bbb' }; baz // "aaa"
c. 对象赋值解构是内部机制,是先找到同名的属性,再赋给对应的变量。真正被赋值的是后者,不是前者;
let { foo: baz } = { foo: 'aaa', bar: 'bbb' }; baz // "aaa" foo // error: foo is not defined
d. 这个p是模式,不是变量,所以不会被赋值。
let obj = { p: [ 'Hello', { y: 'World' } ] }; let { p: [x, { y }] } = obj; x // "Hello" y // "World"
e. p作为变量赋值
let { p, p: [x, { y }] } = obj; x // "Hello" y // "World" p // ["Hello", {y: "World"}]
f. 对象解构赋值可以取到继承的属性
const obj1 = {}; const obj2 = { foo: 'bar' }; Object.setPrototypeOf(obj1, obj2); const { foo } = obj1; foo // "bar"
-
字符串的解构,数值和布尔值的解构赋值
字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象。
如果等号右边是数值和布尔值,则会先转为对象。const [a, b, c, d, e] = 'hello'; a // "h" b // "e" c // "l" d // "l" e // "o" let {toString: s} = 123; s === Number.prototype.toString // true let {toString: s} = true; s === Boolean.prototype.toString // true
-
解构的用途
a. 交换变量的值let x = 1; let y = 2; [x, y] = [y, x];
b. 从函数返回多个值
// 返回一个数组 function example() { return [1, 2, 3]; } let [a, b, c] = example(); // 返回一个对象 function example() { return { foo: 1, bar: 2 }; } let { foo, bar } = example();
c. 函数参数的定义
// 参数是一组有次序的值 function f([x, y, z]) { ... } f([1, 2, 3]); // 参数是一组无次序的值 function f({x, y, z}) { ... } f({z: 3, y: 2, x: 1});
d. 提取JSON数据
let jsonData = { id: 42, status: "OK", data: [867, 5309] }; let { id, status, data: number } = jsonData; console.log(id, status, number); // 42, "OK", [867, 5309]
e. 函数默认值
jQuery.ajax = function (url, { async = true, beforeSend = function () {}, cache = true, complete = function () {}, crossDomain = false, global = true, // ... more config } = {}) { // ... do stuff };
f. 遍历Map结构
const map = new Map(); map.set('first', 'hello'); map.set('second', 'world'); for (let [key, value] of map) { console.log(key + " is " + value); } // first is hello // second is world
g. 输入模块指定方法
const { SourceMapConsumer, SourceNode } = require("source-map");
-
-
字符串
模板字符串
-
新增方法
includes(),是否找到参数字符串,返回布尔值, 第二参数表示开始搜索位置
startsWith(),参数字符串是否在原字符串头部,返回布尔值,第二参数表示开始搜索位置
endsWith(),参数字符串是否在原字符串的尾部,返回布尔值,第二参数表示开始搜索位置
let s = 'Hello world!'; s.startsWith('Hello') // true s.endsWith('!') // true s.includes('o') // true
padStart(),如果某个字符串不够长,头部补足长度
padEnd(),如果某个字符串不够长,尾部补足长度
'x'.padStart(5, 'ab') // 'ababx' 'x'.padStart(4, 'ab') // 'abax' 'x'.padEnd(5, 'ab') // 'xabab' 'x'.padEnd(4, 'ab') // 'xaba'
trimStart(),消除字符串头部的空格
trimEnd(),消除字符串头部的空格
const s = ' abc '; s.trim() // "abc" s.trimStart() // "abc " s.trimEnd() // " abc"
-
数值
ES6在Number原型上新增了isFinite(), isNaN()方法,用来取代传统的全局isFinite(), isNaN()方法检测数值是否有限、是否是NaN。ES5的isFinite(), isNaN()方法都会先将非数值类型的参数转化为Number类型再做判断,这其实是不合理的,最造成
isNaN('NaN') === true
的奇怪行为--'NaN'是一个字符串,但是isNaN却说这就是NaN。而Number.isFinite()和Number.isNaN()则不会有此类问题(Number.isNaN('NaN') === false
)。(isFinite()同上) -
函数
箭头函数没有自己的this
a. 箭头函数的this指向的是函数定义时所在的对象(它内部没有this, this总是指向上一层的this,直到得到非箭头函数的this),而不是函数执行是的对象,ES5函数里面的this总是指向函数执行时的对象,所以它不适合做事件监听函数;
b. 箭头函数不能做构造函数,没有this,无法实例化;
c. 没有自己的this,函数内也不存在arguments对象,可用扩展运算符代替。
ES6新增了双冒号运算符,用来取代以往的bind,call,和apply。
foo::bar; // 等同于 bar.bind(foo); foo::bar(...arguments); // 等同于 bar.apply(foo, arguments);
-
数组
扩展运算符(...)
可以轻松的实现数组和松散序列的相互转化,可以取代arguments对象和apply方法,轻松获取未知参数个数情况下的参数集合。(尤其是在ES5中,arguments并不是一个真正的数组,而是一个类数组的对象,但是扩展运算符的逆运算却可以返回一个真正的数组)
ES6在Array原型上新增了find()方法,用于取代传统的只能用indexOf查找包含数组项目的方法,且修复了indexOf查找不到NaN的bug(
[NaN].indexOf(NaN) === -1
).此外还新增了copyWithin(), includes(), fill(),flat()等方法,可方便的用于字符串的查找,补全,转换等 -
对象
a. 对象属性变量式声明。ES6可以直接以变量形式声明对象属性或者方法。比传统的键值对形式声明更加简洁,更加方便,语义更加清晰。
let [apple, orange] = ['red appe', 'yellow orange']; let myFruits = {apple, orange}; // let myFruits = {apple: 'red appe', orange: 'yellow orange'};
b. 对象的扩展运算符(...)。 ES6对象的扩展运算符和数组扩展运算符用法本质上差别不大,毕竟数组也就是特殊的对象。对象的扩展运算符一个最最常用也最好用的用处就在于可以轻松的取出一个目标对象内部全部或者部分的可遍历属性,从而进行对象的合并和分解。
let {apple, orange, ...otherFruits} = {apple: 'red apple', orange: 'yellow orange', grape: 'purple grape', peach: 'sweet peach'}; // otherFruits {grape: 'purple grape', peach: 'sweet peach'} // 注意: 对象的扩展运算符用在解构赋值时,扩展运算符只能用在最有一个参数(otherFruits后面不能再跟其他参数)
c. super 关键字。ES6在Class类里新增了类似this的关键字super。同this总是指向当前函数所在的对象不同,super关键字总是指向当前函数所在对象的原型对象。
d. ES6在Object原型上新增了is()方法,做两个目标对象的相等比较,用来完善
===
方法。===
方法中NaN === NaN //false
其实是不合理的,Object.is修复了这个小bug。(Object.is(NaN, NaN) // true
)e. ES6在Object原型上新增了assign()方法,用于对象新增属性或者多个对象合并。注意: assign合并的对象target只能合并source1、source2中的自身属性,并不会合并source1、source2中的继承属性,也不会合并不可枚举的属性,且无法正确复制get和set属性(会直接执行get/set函数,取return的值)。
f. ES6在Object原型上新增了getOwnPropertyDescriptors()方法,此方法增强了ES5中getOwnPropertyDescriptor()方法,可以获取指定对象所有自身属性的描述对象。结合defineProperties()方法,可以完美复制对象,包括复制get和set属性。
g. ES6在Object原型上新增了getPrototypeOf()和setPrototypeOf()方法,用来获取或设置当前对象的prototype对象。这个方法存在的意义在于,ES5中获取设置prototype对像是通过proto属性来实现的,然而proto属性并不是ES规范中的明文规定的属性,只是浏览器各大产商“私自”加上去的属性,只不过因为适用范围广而被默认使用了,再非浏览器环境中并不一定就可以使用,所以为了稳妥起见,获取或设置当前对象的prototype对象时,都应该采用ES6新增的标准用法。
h. ES6在Object原型上还新增了Object.keys(),Object.values(),Object.entries()方法,用来获取对象的所有键、所有值和所有键值对数组。
-
Symbol
Symbol是ES6引入的第七种数据类型,所有Symbol()生成的值都是独一无二的,可以从根本上解决对象属性太多导致属性名冲突覆盖的问题。对象中Symbol()属性不能被for...in遍历,但是也不是私有属性。
-
Set和Map
Set是ES6引入的一种类似Array的新的数据结构,Set实例的成员类似于数组item成员,区别是Set实例的成员都是唯一,不重复的。
Map是ES6引入的一种类似Object的新的数据结构,Map可以理解为是Object的超集,打破了以传统键值对形式定义对象,对象的key不再局限于字符串,也可以是Object。可以更加全面的描述对象的属性。
-
Proxy
Proxy是ES6新增的一个构造函数,可以理解为JS语言的一个代理,用来改变JS默认的一些语言行为,包括拦截默认的get/set等底层方法,使得JS的使用自由度更高,可以最大限度的满足开发者的需求。比如通过拦截对象的get/set方法,可以轻松地定制自己想要的key或者value。下面的例子可以看到,随便定义一个myOwnObj的key,都可以变成自己想要的函数。
function createMyOwnObj() { //想把所有的key都变成函数,或者Promise,或者anything return new Proxy({}, { get(target, propKey, receiver) { return new Promise((resolve, reject) => { setTimeout(() => { let randomBoolean = Math.random() > 0.5; let Message; if (randomBoolean) { Message = `你的${propKey}运气不错,成功了`; resolve(Message); } else { Message = `你的${propKey}运气不行,失败了`; reject(Message); } }, 1000); }); } }); } let myOwnObj = createMyOwnObj(); myOwnObj.hahaha.then(result => { console.log(result) //你的hahaha运气不错,成功了 }).catch(error => { console.log(error) //你的hahaha运气不行,失败了 }) myOwnObj.wuwuwu.then(result => { console.log(result) //你的wuwuwu运气不错,成功了 }).catch(error => { console.log(error) //你的wuwuwu运气不行,失败了 })
-
Reflect
Reflect是ES6引入的一个新的对象,他的主要作用有两点,一是将原生的一些零散分布在Object、Function或者全局函数里的方法(如apply、delete、get、set等等),统一整合到Reflect上,这样可以更加方便更加统一的管理一些原生API。其次就是因为Proxy可以改写默认的原生API,如果一旦原生API别改写可能就找不到了,所以Reflect也可以起到备份原生API的作用,使得即使原生API被改写了之后,也可以在被改写之后的API用上默认的API。
-
Promise
Promise是ES6引入的一个新的对象,他的主要作用是用来解决JS异步机制里,回调机制产生的“回调地狱”。它并不是什么突破性的API,只是封装了异步回调形式,使得异步回调可以写的更加优雅,可读性更高,而且可以链式调用
-
Class
ES6 的class可以看作只是一个ES5生成实例对象的构造函数的语法糖。它参考了java语言,定义了一个类的概念,让对象原型写法更加清晰,对象实例化更像是一种面向对象编程。Class类可以通过extends实现继承。它和ES5构造函数的不同点:
a. 类的内部定义的所有方法,都是不可枚举的。
// ES5 function ES5Fun (x, y) { this.x = x; this.y = y; } ES5Fun.prototype.toString = function () { return '(' + this.x + ', ' + this.y + ')'; } var p = new ES5Fun(1, 3); p.toString(); Object.keys(ES5Fun.prototype); //['toString'] // ES6 class ES6Fun { constructor (x, y) { this.x = x; this.y = y; } toString () { return '(' + this.x + ', ' + this.y + ')'; } } Object.keys(ES6Fun.prototype); //[]
b. ES6的class类必须用new命令操作,而ES5的构造函数不用new也可以执行。
c. ES6的class类不存在变量提升,必须先定义class之后才能实例化,不像ES5中可以将构造函数写在实例化之后。
d. ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。
es6,一些总结
©著作权归作者所有,转载或内容合作请联系作者
- 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
- 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
- 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
推荐阅读更多精彩内容
- JavaScript的相关语法知识:1、函数(important)基本上所有的高级语言(C、OC、JavaScri...