一个常见的问题是,ECMAScript 和 JavaScript 到底是什么关系?
要讲清楚这个问题,需要回顾历史。1996 年 11 月,JavaScript 的创造者 Netscape 公司,决定将 JavaScript 提交给标准化组织 ECMA,希望这种语言能够成为国际标准。次年,ECMA 发布 262 号标准文件(ECMA-262)的第一版,规定了浏览器脚本语言的标准,并将这种语言称为 ECMAScript,这个版本就是 1.0 版。
该标准从一开始就是针对 JavaScript 语言制定的,但是之所以不叫 JavaScript,有两个原因。一是商标,Java 是 Sun 公司的商标,根据授权协议,只有 Netscape 公司可以合法地使用 JavaScript 这个名字,且 JavaScript 本身也已经被 Netscape 公司注册为商标。二是想体现这门语言的制定者是 ECMA,不是 Netscape,这样有利于保证这门语言的开放性和中立性。
因此,ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现(另外的 ECMAScript 方言还有 JScript 和 ActionScript)。日常场合,这两个词是可以互换的。
ECMAScript 2015(简称 ES2015)这个词,也是经常可以看到的。它与 ES6 是什么关系呢?
ES6是ECMA
为JavaScript
制定的第6个标准版本。
标准委员会最终决定,标准在每年6月正式发布并作为当年的正式版本,接下来的时间里就在此版本的基础上进行改动,直到下一年6月草案就自然变成新一年的版本,这样一来就无需以前的版本号,只要用年份标记即可。ECMAscript 2015
是在2015年6月
发布ES6的第一个版本。以此类推,ECMAscript 2016
是ES6的第二个版本、 ECMAscript 2017
是ES6的第三个版本。ES6既是一个历史名词也是一个泛指,含义是5.1版本
以后的JavaScript下一代标准
,目前涵盖了ES2015
、ES2016
、ES2017
、ES2018
、ES2019
、ES2020
。
所以有些文章上提到的ES7
(实质上是ES2016
)、ES8
(实质上是ES2017
)、ES9
(实质上是ES2018
)、ES10
(实质上是ES2019
)、ES11
(实质上是ES2020
),实质上都是一些不规范的概念。从ES1到ES6,每个标准都是花了好几年甚至十多年才制定下来,你一个ES6到ES7,ES7到ES8,才用了一年,按照这样的定义下去,那不是很快就ES20了。用正确的概念来说ES6目前涵盖了ES2015、ES2016、ES2017、ES2018、ES2019、ES2020。
什么是变量提升?
JavaScript 中,函数及变量的声明都将被提升到函数的最顶部。
JavaScript 中,变量可以在使用后声明,也就是变量可以先使用再声明。
var存在变量提升
function fn() {
//var a
if (true) {
console.log(a + ' now')
}
else {
var a = 1
console.log(2)
}
}
fn() // a -> undefined
var a = [];
for(var i = 0; i < 10;i++){
var q = i;
a[i] = function(){
console.log(q)
}
}
a[0]()
其中,由于for循环并不是一个函数体,所以for循环中定义的变量q和i是作用域for循环所在的函数体,和a同级,
i++ 和 q=i 并不是重新定义变量,只是重复赋值,最终循环结束,i = 10,q=9;
由于function(){console.log(q)} 并不是立即执行,所以这里的q一直是存储的内存引用,最终所有的a[i]()都是输出 9
不过,在es6中新增了let命令声明变量,用法和var类似,不过let所声明的变量,只在let命令所在的代码块有效果,for循环的计数器中就很适合let命令
var a=[];
for(let i = 0; i < 10; i++){
let q = i;
a[i] = function(){
console.log(q)
}
}
a[6]() //这里会输出 6 let声明的变量仅在块级作用域有效,所以这里的i只在本轮循环有效果,每次循环的i其实都是一个新的变量
字符串解构:const [a, b, c, d, e] = "hello"
数值解构:const { toString: s } = 123
布尔解构:const { toString: b } = true
对象解构
const { x, y } = { x: 1, y: 2 }
const { x, y = 2 } = { x: 1 }
const { x, y: z } = { x: 1, y: 2 }
数组解构
Iterator接口
可采用数组形式的解构赋值const [x, y] = [1, 2]
const [x, y = 2] = [1]
函数参数解构
function Func([x = 0, y = 1]) {}
function Func({ x = 0, y = 1 } = {}) {}
应用场景
[x, y] = [y, x]
const [x, y, z] = Func()
Func([1, 2])
const { name, version } = packageJson
function Func({ x = 1, y = 2 } = {}) {}
for (let [k, v] of Map) {}
const { readFile, writeFile } = require("fs")
Tips 1: 从对象中取值
const obj = {
a:1,
b:2,
c:3,
d:4,
e:5,
}
1️⃣ 属性名是想要的
const {a,b,c,d,e} = obj;
const f = a + d;
const g = c + e;
2️⃣ 属性名不是想要的
const {a:a1} = obj;
console.log(a1);// 1
3️⃣ 补充
ES6的解构赋值虽然好用。但是要注意**解构的对象不能为undefined
、null
。**否则会报错,故要给被解构的对象一个默认值。
const {a,b,c,d,e} = obj || {};
const arr1 = [1,2,3];
const arr2 = [...arr1];
console.log(arr2);
// [ 1, 2, 3 ]
const a = [1,2,3];
const b = [1,5,6];
const c = [...new Set([...a,...b])];//[1,2,3,5,6] 利用set去重
arr.push(...arr1)
const obj1 = {
a:1,
}
const obj2 = {
b:1,
}
const obj = {...obj1,...obj2};//{a:1,b:1}
let arr1 = ['this', 'is', 'an'];
arr1 = [...arr1, 'array'];
console.log(arr1);
// [ 'this', 'is', 'an', 'array' ]
const user = {
firstname: 'Chris',
lastname: 'Bongers'
};
const output = {...user, age: 31};
const arr1 = [1, -1, 0, 5, 3];
const max1 = Math.max(arr1);
console.log(max1);
// NaN
const max2 = Math.max(...arr1);
console.log(max2);
// 5
const myFunc = (x1, x2, x3) => {
console.log(x1);
console.log(x2);
console.log(x3);
};
const arr1 = [1, 2, 3];
myFunc(...arr1);
// 1
// 2
// 3
//接受无数个参数的函数
const myFunc = (...args) => {
console.log(args);
};
const str = 'Hello';
const arr = [...str];
console.log(arr);
// [ 'H', 'e', 'l', 'l', 'o' ]
const user = {
firstname: 'Chris',
lastname: 'Bongers',
age: 31
};
const {firstname, ...rest} = user;
console.log(firstname);
console.log(rest);
// 'Chris'
// { lastname: 'Bongers', age: 31 }
const name = '小明';
const score = 59;
const result = `${name}${score > 60?'的考试成绩及格':'的考试成绩不及格'}`;
A callback is a function that is passed as an argument to another function and is executed after its parent function has completed.
function f1(callback){
callback();
console.log('f1');
}
function f2(){
console.log('f2');
}
f1(f2);// f2 f1
之前
hello = function() {
return "Hello World!";
}
用了箭头函数之后:
hello = () => {
return "Hello World!";
}
如果函数只有一个语句,并且该语句返回一个值,则可以去掉括号和 return 关键字:
箭头函数默认返回值:
hello = () => "Hello World!";
传参
hello = (val) => "Hello " + val;
事实上,如果只有一个参数,您也可以略过括号:
不带括号的箭头函数:
hello = val => "Hello " + val;
new Set()
会把重复的数据过滤到,得到一个类数组的对象
Array.from()
可以把类数组的对象转换为真正的数组对象
let a = [1,2,2,3,3,4,5];
let b = Array.from(new Set(a))
console.log(b) // [1,2,3,4,5]
let a = [
{
name: 'kele',
title: '可口可乐'
},
{
name: 'kele',
title: '芬达'
},
{
name: 'wlg',
title: '王老吉'
}
]
let b = a.filter(item => item.name === 'kele');
console.log(b) //[{name: 'kele', title: '可口可乐'},{name: 'kele', title: '芬达'}]
是否所有元素满足条件
let a = [1,2,3,4,5];
let b = a.every(item => item > 2);
console.log(b) // false
是否存在元素满足条件
let a = [1,2,3,4,5];
let b = a.some(item => item > 2);
console.log(b) // true
是否包含某个元素
let a = [1,2,3,4,5];
let b = a.includes(2);
console.log(b) // true
let a = [1,2,3,4,5];
let b = a.find(item => item > 2);
console.log(b) // 3
let a = [1,2,3,4,5];
let b = a.findIndex(item => item > 2);
console.log(b) // 2 符合条件的为元素3 它的索引为2
for in 语句循环遍历对象的属性:
const person = {fname:"John", lname:"Doe", age:25};
let text = "";
for (let x in person) {
text += person[x];
}
for in 语句也可以遍历数组的属性:
const numbers = [45, 4, 9, 16, 25];
let txt = "";
for (let x in numbers) {
txt += numbers[x];
}
如果索引顺序很重要,请不要在数组上使用 for in。
索引顺序依赖于实现,可能不会按照您期望的顺序访问数组值。
当顺序很重要时,最好使用 for 循环、for of 循环或 Array.forEach()。
forEach()
方法为每个数组元素调用一次函数(回调函数)。
const numbers = [45, 4, 9, 16, 25];
let txt = "";
numbers.forEach(myFunction);
//注意这里的三个参数
// 项目值
// 项目索引
// 数组本身
// 可以省略未使用的
function myFunction(value, index, array) {
txt += value;
}
const cars = ["BMW", "Volvo", "Mini"];
let text = "";
for (let x of cars) {
text += x;
}
**map()**
方法创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。
const array1 = [1, 4, 9, 16];
// pass a function to map
const map1 = array1.map(x => x * 2);
console.log(map1);
// expected output: Array [2, 8, 18, 32]
需求:多维数组=>一维数组
let ary = [1, [2, [3, [4, 5]]], 6];
arr_flat = arr.flat(Infinity);
1、Object.values():返回以值组成的数组
const deps = {
'采购部':[1,2,3],
'人事部':[5,8,12],
'行政部':[5,14,79],
'运输部':[3,64,105],
}
let member = Object.values(deps).flat(Infinity);
2、Object.entries():返回以键和值组成的数组
const obj = { foo: 'bar', baz: 42 };
console.log(Object.entries(obj)); // [ ['foo', 'bar'], ['baz', 42] ]
// array like object
const obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.entries(obj)); // [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ]
// array like object with random key ordering
const anObj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.entries(anObj)); // [ ['2', 'b'], ['7', 'c'], ['100', 'a'] ]
// getFoo is property which isn't enumerable
const myObj = Object.create({}, { getFoo: { value() { return this.foo; } } });
myObj.foo = 'bar';
console.log(Object.entries(myObj)); // [ ['foo', 'bar'] ]
// non-object argument will be coerced to an object
console.log(Object.entries('foo')); // [ ['0', 'f'], ['1', 'o'], ['2', 'o'] ]
// iterate through key-value gracefully
const obj = { a: 5, b: 7, c: 9 };
for (const [key, value] of Object.entries(obj)) {
console.log(`${key} ${value}`); // "a 5", "b 7", "c 9"
}
// Or, using array extras
Object.entries(obj).forEach(([key, value]) => {
console.log(`${key} ${value}`); // "a 5", "b 7", "c 9"
const nullValue = null;
const emptyText = ""; // 空字符串,是一个假值,Boolean("") === false
const someNumber = 42;
const valA = nullValue ?? "valA 的默认值";
const valB = emptyText ?? "valB 的默认值";
const valC = someNumber ?? 0;
console.log(valA); // "valA 的默认值"
console.log(valB); // ""(空字符串虽然是假值,但不是 null 或者 undefined)
console.log(valC); // 42
可以用来优化判断
if((value??'') !== ''){
//...
}