? ES6 既是一个历史名词(特指2015年发布的标准ES2015),也是一个泛指,含义是 5.1 版以后的 JavaScript 的下一代标准,涵盖了ES2015、ES2016、ES2017等。
? Node.js是JavaScript语言的服务器运行环境,对ES6的支持度比浏览器更高。
? 默认安装
? 本地安装
? npm install <模块名> 使用:require(‘模块名‘)
? 全局安装
安装模块 npm install <模块名> -g
卸载模块 npm uninstall <模块名>
更新模块 npm update <模块名>
搜索模块 npm search <模块名>
? ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构赋值。
? 如果解构不成功,变量的值就等于undefined。
? 如果等号的右边不是数组(不是可遍历的结构),将会报错。
? 解构赋值不仅适用于var命令,也适用于let和const命令。
? (1)设置默认值
? 解构赋值允许指定默认值。当等式右边没值或是undefined时,使用默认值。
语法 let [变量=默认值]=[赋值]
? (2)对象的解构赋值
? 解构不仅可以用于数组,还可以用于对象。
var { name, age } = { name: "sss", bar: "12" }; // name:sss ,age:12
? 注意变量必须与属性同名,才能取到正确的值,位置顺序没关系。
? 如果变量名与属性名不一致,必须写成下面这样:
var { foo: baz } = { foo: 'aaa', bar: 'bbb' }; // baz :"aaa"
? 对象的解构赋值的内部机制是:先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。
? (3)字符串的解构赋值
? 此时的字符串被转换成了一个类似数组的对象。
let [a, b, c, d] = 'nice'; // a为n,b为i,c为c,d为e
? (4)函数参数的解构赋值
function add([x, y]){
return x + y;
}
add([1, 2]); // 3
//设置默认值
function move({x = 1, y = 2} = {}) { // 默认参数 x=1,y=2
return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 2]
move({}); // [1, 2]
move(); // [1, 2]
? (5)不能使用圆括号的情况
? 建议只要有可能,就不要在解构中放置圆括号。
? <1>变量声明语句中,不能带有圆括号。
? <2>函数参数中,模式不能带有圆括号。
? <3>赋值语句中,不能将整个模式或嵌套模式中的一层,放在圆括号之中。
总结:解构赋值的使用场景
交换变量的值
[x, y] = [y, x];
方便从函数取值
function func() {
return [1, 2, 3];
}
var [a, b, c] = func();
函数参数的定义
// 参数是一组无次序的值
function f({x, y, z}) { ... }
f({z: 1, y: 2, x: 3});
提取JSON数据
var jsonData = {
id: 12,
name: "sss",
data: [867, 5309]
};
let { id, name, data: number } = jsonData;
function move({x = 1, y = 2} = {}) { // 默认值
return [x, y];
}
move({x: 3}); // y使用默认值,x:3, y:2
只在let命令所在的代码块有效
let不存在变量提升
变量的提升:javascript并不是严格的自上而下执行的语言,它会将当前作用域的所有变量的声明提升到程序的顶部。
console.log(aa) // 输出undefined
var aa=2222; // 实际上是分两步执行 var aa;aa=2222;
let暂时性死区
ES6明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量就会报错。
总之,在代码块内使用let命令声明变量之前,该变量都是不可用的。
let不能重复声明同个变量
? let不允许在相同作用域内,重复声明同一个变量。
let num=111;
let num=222; // 报错内容:Identifier 'num' has already been declared
? const 声明一个只读的常量。一旦声明,常量的值就不能改变。
? const一旦声明变量就必须立即初始化,不能留到以后赋值。
? 只声明不赋值同样会报错。
? const声明的常量不能重新赋值。
const aa= [];
aa.push('firstNumber'); // 可执行
aa.length = 0; // 可执行
aa= ['secondNumber']; // 报错
JavaScript的7中数据类型:Undefined、Null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)、Symbol
Symbol
是一种基础数据类型
功能类似于一种标识唯一性的ID
对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的Symbol类型。
? 通常情况下,我们可以通过调用Symbol()
函数来创建一个Symbol实例
let s1 = Symbol()
传入一个可选的字符串参数,相当于给你创建的Symbol实例一个描述信息:
let s2 = Symbol('another symbol')
由于每一个Symbol值都是不相等的,这意味着Symbol值可以作为标识符用于对象的属性名,这就能保证不会出现同名的属性。
var name= Symbol();
// 第一种写法
var student= {};
student[name] = 'jack!'; // Symbol值name作为student的属性
// 第二种写法
var student= {
[name]: 'jack!' //注意要使用方括号包裹,不放在方括号中,该属性的键名就是字符串
};
注意,Symbol值作为对象属性名时,不能用点运算符,要使用方括号。
取值对象的Symbol类型的属性也要用方括号。普通属性是直接用点运算符。
因为点运算符后面总是字符串
- 用于定义一组常量,保证这组常量的值都是不相等的。
- 常量使用Symbol值最大的好处,就是其他任何值**都不可能有相同的值**了。
levels = {
DEBUG: Symbol('debug'),
INFO: Symbol('info'),
WARN: Symbol('warn')
};
console.log(levels.DEBUG) // Symbol(debug)
console.log(levels.INFO) // Symbol(info)
console.log(levels.WARN) // Symbol(warn)
? Symbol 作为属性名,该属性不会出现在for...in、for...of循环中,也不会被Object.keys()、JSON.stringify()等返回。
var obj = {};
var name = Symbol('name');
var age = Symbol('age');
obj[name] = 'huang';
obj[age] = '23';
var objectSymbols = Object.getOwnPropertySymbols(obj); // 是个数组,可以用for循环遍历
console.log(objectSymbols) // [ Symbol(name), Symbol(age) ]
console.log(obj[name]) // huang 取出Symbol类型的属性要用方括号
var name = Symbol('name');
var age = Symbol('age');
var obj = {
[name] : 'huang', // Symbol类型属性
[age] : '23', // Symbol类型属性
num : 1 // 普通属性
};
console.log( Reflect.ownKeys(obj)) // [ 'num', Symbol(name), Symbol(age) ]返回值是数组
Symbol.for方法可以重新使用同一个Symbol值。
Symbol.for("foo") === Symbol.for("foo") // true
Symbol("foo") === Symbol("foo") // false
Symbol.keyFor方法返回一个已登记的 Symbol 类型值的key
var s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo" 使用Symbol.for有登记名字
var s2 = Symbol("foo");
Symbol.keyFor(s2) // undefined 使用Symbol未登记名字
const FOO_KEY = Symbol.for('foo'); // 使FOO_KEY变成Symbol类型
function A() {
this.foo = 'hello';
}
if (!global[FOO_KEY]) { // 如果不存这个实例创建新的,存在就跳过
global[FOO_KEY] = new A(); // 创建一个新的实例,把实例放到顶层对象global
}
module.exports = global[FOO_KEY]; // 暴露这个实例,可以被其他文件引用
console.log("\u20BB7"); // JavaScript会理解成 \u20BB+7 ,所以只会显示一个空格,后面跟着一个7
console.log("\u{20BB7}"); //输出:告
for of与for in
for in遍历数组的毛病
1.index索引为字符串型数字,不能直接进行几何运算
2.遍历顺序有可能不是按照实际数组的内部顺序
3.使用for in会遍历数组所有的可枚举属性,包括原型。
结论:for in更适合遍历对象,不要使用for in遍历数组
for of
for of遍历的只是数组内的元素,而不包括数组的原型属性method和索引name
结论:for in遍历的是数组的索引(即键名),而for of遍历的是数组元素值。
ES6为字符串添加了遍历器接口,使得字符串可以被for...of循环遍历
for (let i of 'abcd') {
console.log(i)
}
//a
//b
//c
//d
'a'.repeat(3) // "aaa"
'ok'.repeat(2) // "okok"
'ok'.repeat(0) // ""
'ok'.repeat(3.9) // "okokok" 参数为小数只取整数部分,参数为负数会报错,参数为NaN就是0
传统方式:使用加号拼接字符串和变量
$('.op').append(
"I am '+info.age+'years old"
)
ES6使用:模板字符串(template string)是增强版的字符串,用反引号(`)标识
变量用 ${变量} 来表示。大括号内部可以放入任意的JavaScript表达式,可以进行运算,引用对象属性,调用函数,甚至还能嵌套。
如果在模板字符串中需要使用反引号,则前面要用反斜杠转义。
模板字符串中所有的空格和缩进都会被保留在输出之中,可以用来定义多行字符串。
$('.op').append(`
I am ${info.age} years old
`)
alert`123` // 字符串123跟在 alert函数后面
// 等同于
alert(123)
var a = 5;
var b = 10;
tag`Hello ${ a + b } world ${ a * b }`; // tag表示一个函数
// 等同于
tag(['Hello ', ' world ', ''], 15, 50);
ES5中,RegExp构造函数参数
var regex = new RegExp('xyz', 'i');
// 等价于
var regex = /xyz/i;
var regex = new RegExp(/xyz/i);
// 等价于
var regex = /xyz/i;
//★ES5不允许此时使用第二个参数,添加修饰符,否则会报错。
var regex = new RegExp(/xyz/, 'i');
// 报错 Uncaught TypeError: Cannot supply flags when constructing one RegExp from another
ES6
如果RegExp构造函数第一个参数是一个正则对象,那么可以使用第二个参数指定修饰符。
而且,返回的正则表达式会忽略原有的正则表达式的修饰符,只使用新指定的修饰符。
new RegExp(/xyz/ig, 'i').flags // 返回值 i flags属性,会返回正则表达式的修饰符。
原有正则对象的修饰符是ig,它会被第二个参数 i 覆盖
? ES6对正则表达式添加了u修饰符,含义为“Unicode模式”,用来正确处理大于\uFFFF的Unicode字符。也就是说,会正确处理四个字节的UTF-16编码。
/^\uD83D/u.test('\uD83D\uDC2A') // 打印值为false
//★结果说明:加了u修饰符以后,ES6就会识别其为一个字符,所以第一行代码结果为false。
Unicode字符表示法
S6新增了使用大括号表示Unicode字符,这种表示法在正则表达式中必须加上u修饰符,才能识别。
/\u{61}/.test('a') // 打印结果 false
/\u{61}/u.test('a') // 打印结果true
//★如果不加u修饰符,正则表达式无法识别\u{61}这种表示法,只会认为这匹配61个连续的u。
? y修饰符,叫做“粘连”(sticky)修饰符
y修饰符与g修饰符的区别
var s = 'aaa_aa_a'; // 字符串
// 正则声明,此处是字面量方式,还有一种是构造函数方式
var r1 = /a+/g; // 带g修饰符
var r2 = /a+/y; // 带y修饰符
//第一次匹配
r1.exec(s) // 匹配结果 ["aaa"] , 剩余字符串_aa_a
r2.exec(s) // ["aaa"] 剩余字符串_aa_a
//第二次匹配
r1.exec(s) // ["aa"]
r2.exec(s) // null 剩余字符串_aa_a头部 是 _ 不符合,所以返回null
sticky属性
var r = /abc\d/y;
r.sticky // true
ES6为正则表达式新增了flags属性,会返回正则表达式的修饰符。
// ES5的source属性,返回正则表达式的正文
/abc/ig.source // "abc"
// ES6的flags属性,返回正则表达式的修饰符
/abc/ig.flags // 'gi'
二进制和八进制数值的新的写法,分别用前缀0b(或0B)和0o(或0O)表示
// 十进制456转换为二进制111001000 ,二进制使用0b开头
0b111001000 === 456 // true
// 十进制456转换为二进制710,八进制使用0o开头
0o710 === 456 // true
将0b和0o前缀的字符串数值转为十进制,要使用Number方法
Number('0b110') // 6 二进制
Number('0o11') // 9 八进制
? ES6新提供了Number.isFinite()和Number.isNaN()两个方法,只对数值有效,非数值一律返回false。
用来检查一个数值是否为有限的(finite)。有限返回值为true,不是有限的返回false。
用来检查一个值是否为NaN。是NaN返回值为true,不是NaN返回值为false。
用来判断一个值是否为整数。需要注意的是,在JavaScript内部,整数和浮点数是同样的储存方法,所以3和3.0被视为同一个值。
Number.isFinite(0.545877); // true
Number.isFinite('aaaa'); // false 非数值返回false
Number.isFinite(NaN); // false
Number.isFinite(Infinity); // false Infinity 属性用于存放表示正无穷大的数值。
Number.isNaN(NaN) // true
Number.isNaN(15) // false
Number.isNaN('15') // false
Number.isNaN(true) // false
Number.isNaN(9/NaN) // true
Number.isNaN('true'/0) // true
Number.isNaN('true'/'true') // true
Number.isInteger(10) // true
Number.isInteger(10.0) // true
Number.isInteger(10.1) // false
Number.isInteger("10") // false
Number.isInteger(true) // false
浮点数计算是不精确的。
ES6在Number对象上面,作为一个误差范围。
当这个误差能够小于Number.EPSILON,我们就可以认为得到了正确结果。
// ES5的写法
parseInt('12.34') // 12
parseFloat('12.34#') // 12.34
// ES6的写法
Number.parseInt('12.34') // 12
Number.parseFloat('12.34abcd这里是字符串') // 12.34
var num=0.1+0.2-0.3 // 5.551115123125783e-17
num< Number.EPSILON //true 表示小于误差范围
var num=Math.pow(2, 53) // 9007199254740992
var num2=num+1 // num2超过 -2^53到2^53这个范围,打印出来和num值相等,这是错误的
num==num2 // true ☆结果分析:超出2的53次方之后,一个数就不精确了。num和num2本来应该是不相等,但是返回值是相等。
Number.isSafeInteger(num2) // false
Math.trunc 用于去除一个数的小数部分,返回整数部分
? 返回五种值:
Math.cbrt 计算一个数的立方根
Math.trunc(4.1) // 4
Math.trunc('123.456') //123 对于非数值,Math.trunc内部使用Number方法将其先转为数值
Math.trunc(NaN); // NaN 对于空值和无法截取整数的值,返回NaN
Math.cbrt('8') // 2 2的三次方为8
Math.expm1(x) 返回ex - 1,即Math.exp(x) - 1。
Math.log1p(x) 方法返回1 + x的自然对数,即Math.log(1 + x)。如果x小于-1,返回NaN。
Math.log10(x) 返回以10为底的x的对数。如果x小于0,则返回NaN。
Math.log2(x) 返回以2为底的x的对数。如果x小于0,则返回NaN。
Math.atanh(x) 返回x的反双曲正切
? 用于将两类对象转为真正的数组:类似数组的对象和可遍历(iterable)的对象(包括ES6新增的数据结构Set和Map)。
// 类似数组的对象
var arr= {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
// ES6的写法
var arr2 = Array.from(arr); // ['a', 'b', 'c']
Array.from('hello') // ['h', 'e', 'l', 'l', 'o']
Array.from([1, 2, 3], (x) => x * x) // [1, 4, 9] =>是箭头函数
Array.of(7, 8, 9) // [7,8,9]
Array.of(13) // [13]
Array.of() // []
Array.of(13).length // 1
var num = Array(2); // 使用Array 方法
var num2 = Array.of(2); // 使用Array.of 方法
num.length // 2
num2.length // 1
? 在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组。
语法 Array.prototype.copyWithin(target, start = 0, end = this.length)
其中有三个参数(应该是数值类型,不是的话会自动转为数值):
//将从3号位直到数组结束的成员(4和5),复制到从0号位开始的位置,结果覆盖了原来的1和2。
[1, 2, 3, 4, 5].copyWithin(0, 3) // 替换后的结果 [4, 5, 3, 4, 5]
//将下标为3号和4号位复制到下标为0号位和1号位
[1, 2, 3, 4, 5].copyWithin(0, 3, 5) // [4, 5, 3, 4, 5]
// -2相当于3号位,-1相当于4号位
[1, 2, 3, 4, 5].copyWithin(0, -2, -1) // [4, 2, 3, 4, 5]
/从下标为2开始替换(也就是数值9),后两个参数没写,替换的内容默认从下标为0开始(数值8,5,9)
[8,5,9,4,3].copyWithin(2) // [ 8, 5, 8, 5, 9 ]
数组实例的find方法,用于找出第一个符合条件的数组成员。
参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。
find方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组
数组实例的findIndex方法,返回第一个符合条件的数组成员的位置(下标),如果所有成员都不符合条件,则返回-1。
// 查找数组中小于0的数
[1, 2, -3, 4].find( (n) => n < 0 ) // -3 => 是箭头函数,等于 function(n) { return n<0; }
[1, 2, 3, 4].find(function(value, index, arr) { return value > 2; }) // 返回值为3
[1, 2, 3, 4].findIndex(function(value, index, arr) {
return value > 2; // 返回值为2
}
使用给定值,填充一个数组
还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置
['a', 'b', 'c'].fill(7, 1, 2) // ['a', 7, 'c']
//fill方法从1号位开始,向原数组填充7,到2号位之前结束。
['a', 'b', 'c'].fill(7, 1, 3) // ['a', 7, 7]
console.log("----------keys方法---------")
for (let index of ['hello', 'world'].keys()) {
console.log("输出结果:", index);
}
console.log("----------values方法发---------")
for (let elem of ['hello', 'world'].values()) {
console.log("输出结果:",elem);
}
console.log("----------entries方法发---------")
for (let [index, elem] of ['hello', 'world'].entries()) {
console.log("输出结果:",index, elem);
}
includes()
返回一个布尔值,表示某个数组是否包含给定的值
[1, 2, 3].includes(2); // true
[1, 2, 3].includes(4); // false
? ES6允许使用“箭头”(=>
)定义函数。箭头函数使得表达更加简洁。
var f = (参数)=>{ 代码块}
var ff = (x,y)=>{ return x+y }
// 相当于
var f = function(参数){ 代码块 }
var ff = function(x,y){ return x+y }
const full = ({ first, last }) => first + ' ' + last;
// 等同于
var person={ first, last };
function full(person) {
return person.first + ' ' + person.last;
}
function foo() {
return () => {
return () => {
return () => {
console.log('id:', this.id);
};
};
};
}
var f = foo.call({id: 1}); // 设置foo的id为1
var t1 = f.call({id: 2})()(); // id: 1
var t2 = f().call({id: 3})(); // id: 1
var t3 = f()().call({id: 4}); // id: 1
//★结果分析:因为所有的内层函数都是箭头函数,都没有自己的this,它们的this其实都是最外层foo函数的this。所以箭头函数的this指向是创建它所在的对象,不会改变。
箭头函数有几个使用注意点:
(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用Rest参数代替。
(4)不可以使用yield命令,因此箭头函数不能用作Generator函数。
//ES6之前
function log(x, y) {
y = y || 'aaa'; // 设置y的默认值为aaa
console.log(x, y);
}
//ES6
function log(x, y = 'aaa') {
console.log(x, y);
}
//对象的解构赋值
function foo({x, y = 5}) {
console.log(x, y);
}
foo({}) // undefined, 5
foo({x: 1}) // 1, 5
foo({x: 1, y: 2}) // 1, 2
foo() // 报错内容:TypeError: Cannot read property 'x' of undefined
(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2
(function (a = 0, b, c) { }).length // 0
(function (a, b = 1, c) { }).length // 1
var x=1; // 全局变量x
function f(x, y = x) { console.log(y); }
f(5); //5
//★:结果说明:调用时,由于函数作用域内部的变量x已经生成,所以y等于参数x,而不是全局变量x。
let x = 1; // 全局变量 x
function f(y = x) {
let x = 2; // 局部变量 x
console.log(y);
}
f() // 1
//★结果说明:函数调用时,y的默认值变量x尚未在函数内部生成,所以x指向全局变量。
function add(...values) {
let sum = 0;
for (var num of values) {
sum += num;
}
return sum;
}
add(4, 5, 6,7) // 22
console.log( ...[1, 2, 3] ) // 1 2 3
console.log( ...[1, 2, ...[1,2,5],3] ) // 1 2 1 2 5 3
console.log( ...[1, 2, [1,2,5],3] ) // 1 2 [ 1, 2, 5 ] 3
function add(x, y) {
return x + y;
}
var numbers = [4, 38];
add(...numbers) // 42
//**尾调用**
function f(x){
return g(x);
}
//递归**
function foo(n) {
if (n === 1) return 1;
return n * foo(n - 1);
}
foo(5) // 120
原文:https://www.cnblogs.com/xuzhengguo/p/12036959.html