目录
includes(), startsWith(), endsWith()
Number.isFinite(), Number.isNaN()
Number.parseInt(), Number.parseFloat()
ECMAScript6新特性
命令行工具
- CMD 命令行工具
- PowerShell 命令行工具
- 1 在开始位置搜索 PowerShell 打开
- 2 在对应目录按住 shift +右键,打开
Nodejs环境安装

Babel转码器
浏览器支持性查看https://caniuse.com/
// 转码前
input.map(item => item + 1);
// 转码后
input.map(function (item) {
return item + 1;
});
npm install --save-dev @babel/core
{
"presets": [],
"plugins": []
}
npm install --save-dev @babel/preset-env
{
"presets": [
"@babel/env"
],
"plugins": []
}

最后一步
npm install --save-dev @babel/cli

基本用法如下
# 转码结果输出到控制台输出
npx babel example.js
# 转码结果写入一个文件
# --out-file 或 -o 参数指定输出文件
npx babel example.js --out-file compiled.js
# 或者
$ npx babel example.js -o compiled.js
# 整个目录转码
# --out-dir 或 -d 参数指定输出目录
$ npx babel src --out-dir lib
# 或者
$ npx babel src -d lib
Let命令
ES6 新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。
{
let a = 10;
var b = 1;
}
a // ReferenceError: a is not defined.
b // 1
上面代码在代码块之中,分别用let和var声明了两个变量。然后在代码块之外调用这两个变量,结果let声明的变量报错,var声明的变量返回了正确的值。这表明,let声明的变量只在它所在的代码块有效。
for循环的计数器,就很合适使用let命令。
for (let i = 0; i < 10; i++) {
// ...
}
console.log(i);
// ReferenceError: i is not defined
上面代码中,计数器i只在for循环体内有效,在循环体外引用就会报错。
下面的代码如果使用var,最后输出的是10。
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 10
上面代码中,变量i是var命令声明的,在全局范围内都有效,所以全局只有一个变量i。每一次循环,变量i的值都会发生改变,而循环内被赋给数组a的函数内部的console.log(i),里面的i指向的就是全局的i。也就是说,所有数组a的成员里面的i,指向的都是同一个i,导致运行时输出的是最后一轮的i的值,也就是 10。
如果使用let,声明的变量仅在块级作用域内有效,最后输出的是 6。
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 6
上面代码中,变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6。你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。
另外,for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
// abc
// abc
// abc
上面代码正确运行,输出了 3 次abc。这表明函数内部的变量i与循环变量i不在同一个作用域,有各自单独的作用域
let不存在变量提升
var命令会发生“变量提升”现象,即变量可以在声明之前使用,值为undefined。这种现象多多少少是有些奇怪的,按照一般的逻辑,变量应该在声明语句之后才可以使用。
为了纠正这种现象,let命令改变了语法行为,它所声明的变量一定要在声明后使用,否则报错。
// var 的情况
console.log(foo); // 输出undefined
var foo = 2;
// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;
let不允许重复声明
let不允许在相同作用域内,重复声明同一个变量。
// 报错
function func() {
let a = 10;
var a = 1;
}
// 报错
function func() {
let a = 10;
let a = 1;
}
<script>
/**
* 1. var关键字的作用域:var关键字是函数级别作用域
* 2. let关键字是块级作用域,理解为{}级别的作用域
* 3. let不存在变量提升
* 4. let不允许重复声明
*/
// var name = "itbaizhan";
// console.log(name);
// if(true){
// var age = 20;
// }
// console.log(age); // 20
// function fn(){
// var sex = "男";
// }
// fn();
// console.log(sex); // 报错
// let name = "itbaizhan";
// console.log(name);
// if(true){
// let age = 20;
// }
// console.log(age); // 报错
// for(let i = 0;i<5;i++){
// console.log(i);
// }
// var arr = [];
// for(var i = 0;i<10;i++){ // 并不是创建了10个i,而是只创建了一个i,i一直在被重新赋值
// arr[i] = function(){
// console.log(i);
// }
// }
// // 相当于每一个arr= [10个数据],每个数据都是一个函数
// arr[6](); // 10
// var arr = [];
// for(let i = 0;i<10;i++){ // 每次循环独立的创建一个i,有10个i
// arr[i] = function(){
// console.log(i);
// }
// }
// arr[3](); // 6
// console.log(names);
// let names = "itbaizhan";
// let age = 20;
// let age = 30;
// console.log(age);
</script>
const 命令
const声明一个只读的常量。一旦声明,常量的值就不能改变。
const PI = 3.1415;
PI // 3.1415
PI = 3;
// TypeError: Assignment to constant variable.
上面代码表明改变常量的值会报错。
const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。
const foo;
// SyntaxError: Missing initializer in const declaration
上面代码表示,对于const来说,只声明不赋值,就会报错。
const的作用域与let命令相同:只在声明所在的块级作用域内有效。
if (true) {
const MAX = 5;
}
MAX // Uncaught ReferenceError: MAX is not defined
const命令声明的常量也是不提升
if (true) {
console.log(MAX); // ReferenceError
const MAX = 5;
}
const声明的常量,也与let一样不可重复声明。
var message = "Hello!";
let age = 25;
// 以下两行都会报错
const message = "Goodbye!";
const age = 30;
变量的解构赋值
数组的解构赋值
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
以前,为变量赋值,只能直接指定值。
let a = 1;
let b = 2;
let c = 3;
ES6 允许写成下面这样。
let [a, b, c] = [1, 2, 3];
上面代码表示,可以从数组中提取值,按照对应位置,对变量赋值。
本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。下面是一些使用嵌套数组进行解构的例子。
let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3
let [ , , third] = ["foo", "bar", "baz"];
third // "baz"
let [x, , y] = [1, 2, 3];
x // 1
y // 3
let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]
let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []
如果解构不成功,变量的值就等于undefined。
解构赋值允许指定默认值。
let [foo = true] = [];
foo // true
let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
注意,ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,只有当一个数组成员严格等于undefined,默认值才会生效。
对象的解构赋值
解构不仅可以用于数组,还可以用于对象。
let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
let { bar, foo } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
let { baz } = { foo: 'aaa', bar: 'bbb' };
baz // undefined
上面代码的第一个例子,等号左边的两个变量的次序,与等号右边两个同名属性的次序不一致,但是对取值完全没有影响。第二个例子的变量没有对应的同名属性,导致取不到值,最后等于undefined。
let { random,floor } = Math;
let { log } = console;

var {x = 3} = {};
let hello = "Hello";
let { hello } = {hello:"hello"}; // 报错
let hello = "Hello";
({ hello } = {hello:"hello"}); // 正确
字符串的解构赋值
字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值。

变量的解构赋值_用途
交换变量的值
let x = 1;
let y = 2;
[x, y] = [y, x];
从函数返回多个值
// 返回一个数组
function example() {
return [1, 2, 3];
}
let [a, b, c] = example();
// 返回一个对象
function example() {
return {
foo: 1,
bar: 2
};
}
let { foo, bar } = example();
提取 JSON 数据
let jsonData = {
id: 42,
status: "OK",
data: ["iwen", "itbaizhan"]
};
let { id, status, data } = jsonData;
字符串扩展
"\u0061"
// "a"
字符串遍历器接口
for (let i of 'itbaizhan') {
console.log(i);//打印每个字符
}
for (let codePoint of 'foo') {
console.log(codePoint)
}
// "f"
// "o"
// "o"
模板字符串
let url = "www.itbaizhan.com"
let h1 = "<a href='"+ url +"'>itbaizhan</a>"
let h2 = `<a href='${url}'>itbaizhan</a>`
字符串新增方法
includes(), startsWith(), endsWith()
传统上,JavaScript 只有indexOf方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6 又提供了三种新方法。
- includes():返回布尔值,表示是否找到了参数字符串。
- startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
- endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
let s = 'Hello world!';
s.startsWith('Hello') // true
s.endsWith('!') // true
s.includes('o') // true
这三个方法都支持第二个参数,表示开始搜索的位置。
let s = 'Hello world!';
s.startsWith('world', 6) // true
s.endsWith('Hello', 5) // 注意 true
s.includes('Hello', 6) // false
实例方法:repeat()
repeat方法返回一个新字符串,表示将原字符串重复n次。(包含自身)
'x'.repeat(3) // "xxx"
'hello'.repeat(2) // "hellohello"
'na'.repeat(0) // ""
padStart(),padEnd()
ES2017 引入了字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补全。padStart()用于头部补全,padEnd()用于尾部补全。
'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'
'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'
trimStart(),trimEnd()
ES2019 对字符串实例新增了trimStart()和trimEnd()这两个方法。它们的行为与trim()一致,trimStart()消除字符串头部的空格,trimEnd()消除尾部的空格。它们返回的都是新字符串,不会修改原始字符串。
const s = ' abc ';
s.trim() // "abc"
s.trimStart() // "abc "
s.trimEnd() // " abc"
at()
const str = 'hello';
str.at(1) // "e"
str.at(-1) // "o"
如果参数位置超出了字符串范围, at() 返回 undefined
数值的扩展
Number.isFinite(), Number.isNaN()
ES6 在Number对象上,新提供了Number.isFinite()和Number.isNaN()两个方法。
Number.isFinite()用来检查一个数值是否为有限的(finite),即不是Infinity。
Number.isFinite(15); // true
Number.isFinite(0.8); // true
Number.isFinite(NaN); // false
Number.isFinite(Infinity); // false
Number.isFinite(-Infinity); // false
Number.isFinite('foo'); // false
Number.isFinite('15'); // false
Number.isFinite(true); // false
注意,如果参数类型不是数值,Number.isFinite一律返回false。
Number.isNaN()用来检查一个值是否为NaN。
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
如果参数类型不是NaN,Number.isNaN一律返回false
Number.parseInt(), Number.parseFloat()
ES6 将全局方法parseInt()和parseFloat(),移植到Number对象上面,行为完全保持不变。
// ES5的写法
parseInt('12.34') // 12
parseFloat('123.45#') // 123.45
// ES6的写法
Number.parseInt('12.34') // 12
Number.parseFloat('123.45#') // 123.45 自动去调非数值
这样做的目的,是逐步减少全局性方法,使得语言逐步模块化。
Number.parseInt === parseInt // true
Number.parseFloat === parseFloat // true
Number.isInteger()
Number.isInteger()用来判断一个数值是否为整数。
Number.isInteger(25) // true
Number.isInteger(25.1) // false
Math函数扩展
Math.trunc()
Math.trunc(4.1) // 4
Math.trunc(4.9) // 4
Math.trunc(-4.1) // -4
Math.trunc(-4.9) // -4
Math.sign()
- 参数为正数,返回 +1
- 参数为负数,返回 -1
- 参数为 0,返回 0
- 参数为-0,返回 -0
- 其他值,返回 NaN
Math.sign(-5) // -1
Math.sign(5) // +1
Math.sign(0) // +0
Math.sign(-0) // -0
Math.sign(NaN) // NaN
Math.sign('itbaizhan') // NaN
Math.sign(undefined) // NaN
Math.cbrt()
如果一个数的立方等于 a ,那么这个数叫 a 的立方根,也称为三次方根。也就是说,如果 x³=a ,那么 x 叫做 a 的立方根
Math.cbrt('8') // 2
Math.cbrt('hello') // NaN
对数方法
- 1 Math.expm1()
- 2 Math.log1p()
- 3 Math.log10()
- 4 Math.log2()
双曲函数方法
- Math.sinh(x) 返回 x 的双曲正弦(hyperbolic sine)
- Math.cosh(x) 返回 x 的双曲余弦(hyperbolic cosine)
- Math.tanh(x) 返回 x 的双曲正切(hyperbolic tangent)
- Math.asinh(x) 返回 x 的反双曲正弦(inverse hyperbolic sine)
- Math.acosh(x) 返回 x 的反双曲余弦(inverse hyperbolic cosine)
- Math.atanh(x) 返回 x 的反双曲正切(inverse hyperbolic tangent)
数组扩展_扩展运算符
console.log(...[1, 2, 3])
// 1 2 3
console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5
[...document.querySelectorAll('div')]
// [<div>, <div>, <div>]
function push(array, items) {
array.push(...items);
}
function add(x, y) {
return x + y; }
const numbers = [4, 38];
add(...numbers) // 42
替代函数的 apply 方法 由于扩展运算符可以展开数组,所以不再需要apply方法,将数组转为函数的参数了。
// ES5 的写法
function f(x, y, z) {
// ...
}
var args = [0, 1, 2];
f.apply(null, args);
// ES6的写法
function f(x, y, z) {
// ...
}
let args = [0, 1, 2];
f(...args);
下面是扩展运算符取代apply方法的一个实际的例子,应用Math.max方法,简化求出一个数组最大元素的写法。
// ES5 的写法
Math.max.apply(null, [14, 3, 77])
// ES6 的写法
Math.max(...[14, 3, 77])
// 等同于
Math.max(14, 3, 77);
扩展运算符的应用-合并数组 扩展运算符提供了数组合并的新写法。
const arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3 = ['d', 'e'];
// ES5 的合并数组
arr1.concat(arr2, arr3);
// [ 'a', 'b', 'c', 'd', 'e' ]
// ES6 的合并数组
[...arr1, ...arr2, ...arr3]
// [ 'a', 'b', 'c', 'd', 'e' ]
Array.from()
Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。
下面是一个类似数组的对象,Array.from将它转为真正的数组。
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
// ES5的写法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']
// ES6的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
实际应用中,常见的类似数组的对象是 DOM 操作返回的 NodeList 集合,以及函数内部的arguments对象。Array.from都可以将它们转为真正的数组
<div></div>
<div></div>
<div></div>
<script>
// Array.from()把伪数组变为真的数组
function add(){
arguments = Array.from(arguments)
arguments.push(50)
console.log(arguments);
}
add(10,20,30,40);
var divs = document.querySelectorAll("div");
console.log(Array.from(divs));
let arrayLike = {
0:"iwen",
1:"ime",
2:"demo",
length:3
}
console.log(Array.from(arrayLike));
// Array.of():把一组数变为数组
console.log(Array.of(10,20,30,40));
var arr1 = new Array(3);
console.log(arr1); // [,,]
var arr2 = new Array(10,20,30);
console.log(arr2); // [10,20,30]
console.log(Array.of(3)); // [3]
console.log(Array.of(3,3,4,5)); // [3]
</script>
Array.of()
Array.of方法用于将一组值,转换为数组。
Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1
这个方法的主要目的,是弥补数组构造函数Array()的不足。因为参数个数的不同,会导致Array()的行为有差异。
Array() // []
Array(3) // [, , ,]
Array(3, 11, 8) // [3, 11, 8]
数组实例的 copyWithin()
数组实例的copyWithin()方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组
Array.prototype.copyWithin(target, start = 0, end = this.length)
它接受三个参数。
target(必需):从该位置开始替换数据。如果为负值,表示倒数。
start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示从末尾开始计算。
end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示从末尾开始计算。
这三个参数都应该是数值,如果不是,会自动转为数值。
[1, 2, 3, 4, 5].copyWithin(0, 3)
// [4, 5, 3, 4, 5]
数组实例的 find() 和 findIndex()
数组实例的find方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。
[1, 4, -5, 10].find((n) => n < 0)
// -5
上面代码找出数组中第一个小于 0 的成员。
[1, 5, 10, 15].find(function(value, index, arr) {
return value > 9;
}) // 10
上面代码中,find方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组
数组实例的 fill()
fill方法使用给定值,填充一个数组。
['a', 'b', 'c'].fill(7)
// [7, 7, 7]
new Array(3).fill(7)
// [7, 7, 7]
数组实例的 includes()
Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似。ES2016 引入了该方法。
[1, 2, 3].includes(2) // true
[1, 2, 3].includes(4) // false
[1, 2, NaN].includes(NaN) // true
数组其他方法
<script>
// indexOf
// var arr = [10,20,30];
// if(arr.indexOf(20) > -1){
// console.log("存在");
// }else{
// console.log("不存在");
// }
// console.log(arr.includes(20)); // true
// // NaN的问题 indexOf是无法验证NaN
// var arr1 = [10,20,30,NaN]
// console.log(arr1.indexOf(NaN)); // -1
// console.log(arr1.includes(NaN)); // true
// var arr = [10,20,[30,40]];
// console.log(arr.flat()); // [10,20,30,40]
// console.log(arr); // [10,20,[30,40]]
// var arr1 = [10,20,[30,40,[50,60]]];
// // 可以传递参数,参数的数字代表拉平的维度
// console.log(arr1.flat(2)); // [10,20,30,40,50,60]
var arr = [10,20,30,40];
console.log(arr[2]);
console.log(arr.at(-3)); // 20
</script>
对象的扩展
属性的简洁表示法
ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。
const foo = 'bar';
const baz = {foo};
baz // {foo: "bar"}
// 等同于
const baz = {foo: foo};
上面代码中,变量foo直接写在大括号里面。这时,属性名就是变量名, 属性值就是变量值。下面是另一个例子。
function f(x, y) {
return {x, y};
}
// 等同于
function f(x, y) {
return {x: x, y: y};
}
f(1, 2) // Object {x: 1, y: 2}
下面是一个实际的例子。
let birth = '2000/01/01';
const Person = {
name: '张三',
//等同于birth: birth
birth,
// 等同于hello: function ()...
hello() { console.log('我的名字是', this.name); }
};
属性名表达式
JavaScript 定义对象的属性,有两种方法。
// 方法一
obj.foo = true;
// 方法二
obj['a' + 'bc'] = 123;
上面代码的方法一是直接用标识符作为属性名,方法二是用表达式作为属性名,这时要将表达式放在方括号之内。 ES6 允许字面量定义对象时,用方法二(表达式)作为对象的属性名,即把表达式放在方括号内。
let propKey = 'foo';
let obj = {
[propKey]: true,
['a' + 'bc']: 123
};
方法的 name 属性
函数的name属性,返回函数名。对象方法也是函数,因此也有name属性。
const person = {
sayName() {
console.log('hello!');
},
};
person.sayName.name // "sayName"
上面代码中,方法的name属性返回函数名(即方法名)
对象的扩展运算符
《数组的扩展》一章中,已经介绍过扩展运算符(...)。ES2018 将这个运算符引入了对象。 对象的解构赋值用于从一个对象取值,相当于将目标对象自身的所有可遍历的(enumerable)、但尚未被读取的属性,分配到指定的对象上面。所有的键和它们的值,都会拷贝到新对象上面。
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x // 1
y // 2
z // { a: 3, b: 4 }
<script>
var job = ["itbaizhan","sxt"]
var user = {
name:"iwen",
age:20,
sex:"男",
job,
sayHello(){
console.log("Hello");
}
}
user.sayHello()
function getPosition(){
var x = 100;
var y = 100;
return{
x,
y
}
}
var result = getPosition()
console.log(result.x);
console.log(result.y);
let propKey = 'itbaizhan';
var info = {
address:"地址",
[propKey]:"haha" // 属性名表达式
}
console.log(info[propKey]);
var hello = {a:"a",b:"b"};
console.log({...hello});
console.log({...{b:1}, a: 1});
</script>
对象的新增方法-Object.is()
ES5 比较两个值是否相等,只有两个运算符:相等运算符(==)和严格相等运算符(===)。它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0。JavaScript 缺乏一种运算,在所有环境中,只要两个值是一样的,它们就应该相等。 ES6 提出“Same-value equality”(同值相等)算法,用来解决这个问题。Object.is就是部署这个算法的新方法。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。
Object.is('foo', 'foo')
// true
Object.is({}, {})
// false
不同之处只有两个:一是+0不等于-0,二是NaN等于自身。
+0 === -0 //true
NaN === NaN // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
对象的新增方法-Object.assign()
Object.assign()方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}
const target = { a: "hello" }
const source = { a: "world" }
console.log(Object.assign(target, source));
Object.assign([1, 2, 3], [4, 5])
// [4, 5, 3]
<script>
console.log(Object.is("hello","hello")); // true
console.log(Object.is("hello","world")); // false
console.log(NaN === NaN); // false
console.log(+0 === -0); // true
console.log(Object.is(NaN,NaN)); // true
console.log(Object.is(+0,-0)); // false
var obj1 = {
name:"iwen",
age:10
}
var obj2 = {
age:20
}
var obj3 = {
sex:"男"
}
console.log(Object.assign(obj1,obj2,obj3));
var arr1 = [1,2,3] //
var arr2 = [4,5]
console.log(Object.assign(arr1,arr2));// [4,5,3]
let {keys,values,entries} = Object; // 对象的解构赋值
var user = {
username:"iwen",
password:"123"
}
for(let item of keys(user)){
console.log(item); // username password
}
for(let item of values(user)){
console.log(item); // iwen 123
}
for(let item of entries(user)){
console.log(item); // ['username', 'iwen'] ['password', '123']
}
</script>
运算符的扩展
指数运算符
2 ** 2 // 4
2 ** 3 // 8
// 相当于 2 ** (3 ** 2)
2 ** 3 ** 2
// 512
let a = 1.5; a **= 2;
// 等同于 a = a * a;
let b = 4; b **= 3;
// 等同于 b = b * b * b;
链判断运算符
// 错误的写法
const firstName =
message.body.user.firstName || 'default';
// 正确的写法
const firstName = (message
&& message.body
&& message.body.user
&& message.body.user.firstName) ||
'default';
const firstName = message?.body?.user?.firstName || 'default';
Null 判断运算符
function add(x,y){
x = x || 100;
y = y || 100;
console.log(x+y);
}
add(0,0); // 200
function add(x,y){
x = x ?? 100;
y = y ?? 100;
console.log(x+y);
}
add(0,0);
// 或赋值运算符
x ||= y
// 等同于
x || (x = y)
// 与赋值运算符
x &&= y
// 等同于
x && (x = y)
// Null 赋值运算符
x ??= y
// 等同于
x ?? (x = y)
// 老的写法
user.id = user.id || 1;
// 新的写法
user.id ||= 1;
函数的扩展
函数参数的默认值
ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法。
function log(x, y) {
y = y || 'World';
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello World
上面代码检查函数log的参数y有没有赋值,如果没有,则指定默认值为World。这种写法的缺点在于,如果参数y赋值了,但是对应的布尔值为false,则该赋值不起作用。就像上面代码的最后一行,参数y等于空字符,结果被改为默认值。
为了避免这个问题,通常需要先判断一下参数y是否被赋值,如果没有,再等于默认值。
if (typeof y === 'undefined') {
y = 'World';
}
ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。
function log(x, y = 'World') {
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello
可以看到,ES6 的写法比 ES5 简洁许多,而且非常自然。下面是另一个例子
function Point(x = 0, y = 0) {
this.x = x;
this.y = y;
}
const p = new Point();
p // { x: 0, y: 0 }
rest 参数
ES6 引入 rest 参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(2, 5, 3) // 10
上面代码的add函数是一个求和函数,利用 rest 参数,可以向该函数传入任意数目的参数
function add(a, ...b,c) {} // 报错
function add(a, ...b) {}
console.log(add.length); // 1
function doSomething(a, b) {
'use strict';
// code
}
// 报错
function doSomething(a, b = a) {
'use strict';
// code
}
name 属性
函数的name属性,返回该函数的函数名。
function foo() {}
foo.name // "foo"
参数默认值的位置
function add(x = 1, y) {
console.log(x+y);
}
add(10) // NaN
函数的 length 属性
function fn1(x){}
console.log(fn1.length); // 1
function fn2(x=1){}
console.log(fn2.length); // 0
function missingParameter() {
throw new Error('Missing parameter');
}
function add(x = missingParameter()) {
console.log(x);
}
add() // Missing parameter
箭头函数
ES6 允许使用“箭头”(=>)定义函数
var f = v => v;
// 等同于
var f = function (v) {
return v;
};
如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分
var f = () => 5;
// 等同于
var f = function () { return 5 };
var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
return num1 + num2;
};
如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回
var sum = (num1, num2) => { return num1 + num2; }
由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错
// 报错
let getTempItem = id => { id: id, name: "Temp" };
// 不报错
let getTempItem = id => ({ id: id, name: "Temp" });
箭头函数的一个用处是简化回调函数。
// 正常函数写法
[1,2,3].map(function (x) {
return x * x;
});
// 箭头函数写法
[1,2,3].map(x => x * x);
使用注意点 箭头函数有几个使用注意点。
(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
上面四点中,第一点尤其值得注意。this对象的指向是可变的,但是在箭头函数中,它是固定的
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
var id = 21;
foo.call({ id: 42 });
// id: 42
箭头函数里面根本没有自己的 this ,而是引用外层的 this
var name = "itbaizhan"
var user = {
name:"iwen",
getName(){
setTimeout(() =>{
console.log(this.name); // iwen
})
}
}
user.getName()
Symbol
let s = Symbol();
typeof s
// "symbol"
注意, Symbol 函数前不能使用 new 命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象。也就是说,由于 Symbol 值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。
let s1 = Symbol('itbaizhan');
let s2 = Symbol('sxt');
s1 // Symbol(itbaizhan)
s2 // Symbol(sxt)
注意, Symbol 函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的 Symbol 函数的返回值是不相等的。
// 没有参数的情况
let s1 = Symbol();
let s2 = Symbol();
s1 === s2 // false
// 有参数的情况
let s1 = Symbol('itbaizhan');
let s2 = Symbol('itbaizhan');
s1 === s2 // false
实例属性description
const sym = Symbol('itbaizhan');
sym.description // "itbaizhan"
let mySymbol = Symbol("name");
var user = {
name:"iwen"
}
var info = {
[mySymbol]:"itbaizhan", }
var newUser = Object.assign(user,info)
console.log(newUser[mySymbol]);
属性名的遍历
let mySymbol = Symbol("age");
var user = {
name:"iwen",
[mySymbol]:20
}
for(let item in user){
console.log(item); // name
}
const objectSymbols = Object.getOwnPropertySymbols(user);
console.log(objectSymbols); // [Symbol(age)]
Symbol.for()
let s1 = Symbol.for('itbaizhan');
let s2 = Symbol.for('itbaizhan');
console.log(s1 === s2); // true
<script>
var user = {
name:"iwen"
}
/*
Symbol:
1. 新的原始数据类型:字符串和数字
2. 他不能使用new关键字去生成
3. 他是独一无二的
4. Symbol可以接受一个参数,参数的目的是为了方便识别不同的Symbol
5. Symbol的参数只是描述,哪怕描述内容相同,两个值也是不同的
6. 可以使用description获取一个Symbol的描述
7. Symbol作为对象的属性名,可以保证不重复
8. Symbol作为对象的属性名,读取的时候要用数组的方式
9. Symbol作为对象的属性名,读取的时候只能使用getOwnPropertySymbols()
10. 如果要创建重复的Symbol的值,需要使用Symbol.for()
*/
var s = Symbol();
console.log(typeof s); // symbol // number string object
var s1 = Symbol("itbaizhan");
var s2 = Symbol("sxt");
console.log(s1,s2);
var s3 = Symbol("iwen");
var s4 = Symbol("iwen");
console.log(s3 === s4); // false
console.log(s1.description); // itbaizhan
var info2Symbol = Symbol("name")
var info1 = {
name:"iwen"
}
var info2 = {
[info2Symbol]:"ime"
}
var newInfo = Object.assign(info1,info2); // {name:'ime', [info2Symbol]:"ime"}
console.log(newInfo.name); // iwen
// 用数组的方式读取Symbol属性
console.log(newInfo[info2Symbol]); // ime
var info3Symbol = Symbol("job")
var info3 = {
name:"iwen",
age:20,
[info3Symbol]:"itbaizhan"
}
for(let key in info3){
console.log(key);
}
console.log(Object.getOwnPropertySymbols(info3)); // Symbol("job")
var itSymbol1 = Symbol.for("itbaizhan")
var itSymbol2 = Symbol.for("itbaizhan")
console.log(itSymbol1 === itSymbol2); // true
</script>
1035

被折叠的 条评论
为什么被折叠?



