文章目录
一、ES6模块化
1、什么是模块化?
模块化是指将一个大的程序文件,拆分为许多小的文件,然后再将小文件组合起来。
2、为什么要编写模块化代码?
- 防止命名冲突
- 代码复用
- 高维护性
3、JS的模块化规范有哪些?
- commonJS
- AMD
- CMD
- ES6模块化
4、ES6模块化
4.1、export命令用于规定模块的对外接口
暴露语法:
- 分别暴露:
export xxx
export function foo () {
console.log("foo() module1.js")
}
export function bar () {
console.log("bar() module1.js")
}
export let arr = [1, 2, 3, 4, 5, 6];
- 统一暴露:
export {xxx,yyy}
function fun1 () {
console.log("fun() module2.js");
}
function fun2 () {
console.log("fun() module2.js");
}
export { fun1, fun2 }
- 默认暴露:
export default{}
只能写一次
export default {//default只能写一次
msg: "我是默认暴露",
foo: function () {
console.log(this.msg);
}
}
4.2、import命令用于输入其他模块提供的功能
- 语法:
import xxx from ‘路径’
//引入其他的模块
//引入第三方库写在最前面
import $ from 'jquery';
import { foo, bar } from './module1';
import { fun1, fun2 } from './module2';
import module3 from './module3';
$('body').css('background', 'green')
foo()
bar()
fun1()
fun2()
module3.foo();
注意点:
若是引入多个模块是=时,有同名的需要用as
起别名
5、commonJS模块化与ES6模块化的区别?
- commonJS适用于node.js服务器端,而ES6浏览器端和服务器端都适用
- 语法差异:CommonJS 模块使用require()和module.exports,ES6 模块使用import和export
- commonJS是在运行时加载,而ES6模块化是在编译时就已经加载完毕了
- CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
二、let和const
let的特性
- 具有块级作用域
- 不能重复声明
- 不存在变量提升
//1、不能重复声明:
let obj = "spylent";
let obj = "疯兔"; //Uncaught SyntaxError: Identifier 'obj' has already been declared
//2、块级作用域:
{
let obj1= "王天雄";
}
console.log(obj1); //Uncaught ReferenceError: obj1 is not defined
//3、不存在变量提升
console.log(a);
let a = 1; //Uncaught ReferenceError: Cannot access 'a' before initialization
什么是块级作用域?
在ES6中被{}包裹起来的代码就是一个块,而{}中就形成了一个块级作用域,比如if(){}、for(……){}、……
const的特性
- 声明时必须赋值
- const声明的是常量,一旦赋值就不能再修改,但对于引用数据类型,只要不是修改引用地址就可以修改这个数据里的数据
- 具有块级作用域
- 不能重复声明
- 不存在变量提升
//1、一定要赋初始值
const A;//Missing initializer in const declaration
//2、一般常量使用大写:
const CYM = "cym";
//3、常量的值不能修改:
CYM = 1;//Assignment to constant variable.
//4、对于引用型数据类型内部数据的修改不算是对常量的修改,不会报错:
const OBJ = {
cym: "cym"
}
OBJ.cym = "wtx";
for和var一起使用与for和let一起使用的区别?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>let实例</title>
<style>
.box {
width: 100px;
height: 50px;
border: 1px solid black;
margin: 5px 0;
}
</style>
</head>
<body>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<script>
let boxes = document.getElementsByClassName("box");
for (let i = 0; i < boxes.length; i++) {
boxes[i].onclick = function () {
boxes[i].style.background = "red";
console.log(i);// 0 1 2
}
}
// for (var i = 0; i < boxes.length; i++) {
// boxes[i].onclick = function () {
// boxes[i].style.background = "red";//报错 此时i = 3
// }
// }
</script>
</body>
</html>
为什么会发生上述情况?
用var声明的i
会成为全局变量,每次循环中使用的i都是同一个i,等到循环结束之后i就变为3了;
而用let声明的i
,因为块级作用域的关系,每一次循环中使用的i都是新的变量;
问?如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?
这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。
三、解构赋值
什么是解构赋值?
解构赋值就是可以从对象或者数组中按照一定模式提取值,赋值给变量;
对象解构赋值
对象的解构是先找到同名属性,然后再赋给对应的变量
let { bar, foo } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
let { baz } = { foo: 'aaa', bar: 'bbb' };
baz // undefined
数组解构赋值
数组的解构变量的取值是根据它的位置决定的;
// 1、数组的元素是按次序排列的,变量的取值由它的位置决定;
const arr = [2, "wtx", true, 1];
const [a, b, c, d] = arr;
console.log(a);//2
console.log(b);//"wtx"
console.log(c);//true
console.log(d);//1
// 2、解构嵌套数组,就是模式匹配,只要等号两边的层级结构相同,就可以拿到对应位置的值
const arr = [2, [3, 4, [5, 6, 7, [8]]]];
const [a, [b, c, [d, e, f, [g]]]] = arr;
// 3、相同“模式”的不完全解构
const [a, b, c] = [1, 2];//1,2,undefined
const [a, b] = [1, 2, 3];//1,2
const [a, b, ...c] = [1, 2, 3, 4, 5, 6];
console.log(a);//1
console.log(b);//2
console.log(c);//[3,4,5,6]
// 4、解构的默认值
const [a = 1] = [];
console.log(a);//1
可用于变量进行值的交换:
let x = 1;
let y = 2;
[x,y] = [y,x]
从函数中返回多个数:
function example() {
return [1, 2, 3];
}
let [a, b, c] = example();
四、模板字符串
ES6引入新的声明字符串的方式:``(键盘tab键上面的反引号)
使用该方式可以在字符串中使用变量,语法形式为:${变量名}
let name = "spylent";
let str = `我的名字为${name}`
console.log(str)//我的名字为spylent
可以在字符串中直接换行:
cosnt str =
`<ul>
<li></li>
</ul>
`
五、简化了对象写法
- 在对象中可以直接写入变量和函数:
let name = "spylent";
let setName = function (name) {
return this.name = name;
}
let obj = {
name,
setName,
setAge (age) {
return this.age = age;
}
}
obj.setAge(3);
console.log(obj.age);//3
六、允许函数参数赋初始值
具有默认值的参数,一般位置要靠后
//1、函数参数默认值
function fun (a, b, c = 2) {
console.log(a + b + c);//5
}
fun(1, 2);
//2、与解构赋值结合
function fun1 ({ a, b, c = 4 }) {
console.log(a);//2
console.log(b);//hahah
console.log(c);//1
}
fun1({
a: 2,
b: "hahah",
c: 1
})
七、函数扩展之rest参数
ES6 引入 rest 参数(形式为...变量名
),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
rest 参数就是一个真正的数组,数组特有的方法都可以使用;
rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。
function fun (a, b, ...args) {
console.log(a);
console.log(b);
console.log(args);
}
fun(1, 2, 3, 4, 5, 6, 7, 8, 9);
八、箭头函数
ES6 允许使用“箭头”(=>)定义函数。
1、箭头函数声明
let fun = ()=>{
console.log("我是箭头函数")
}
fun()
2、箭头函数的简写形式
2.1、若是参数只有一个时,可以省略小括号
let fun = item=>{
console.log("我是"+item)
}
fun("spylent")
2.1、当代码体只有一条语句时,且此时若是省略花括号return必须省略
let fun = ()=>{
return "spylent"
}
//可简写为
let fun = ()=>"spylent"
如果箭头函数直接返回一个对象,需要为对象套上括号
// 报错
let getTempItem = id => { id: id, name: "Temp" };
// 不报错
let getTempItem = id => ({ id: id, name: "Temp" });
3、箭头函数的特点
3.1、箭头函数没有this,若是在箭头函数中使用了this,则该this指向声明箭头函数时的作用域中的this,是静态的,不会随着调用者的改变而改变
let obj = {
name: "spylent",
age: 13
}
let fun = () => {
console.log(this.name);
}
window.name = "wtx";
fun();//wtx
fun.call(obj);//wtx
3.2、箭头函数不能作为构造函数创建实例对象
let Fun = (name, age) => {
this.name = name;
this.age = age;
}
let fun = new Fun("cym", 13);
console.log(fun);//Uncaught TypeError: Fun is not a constructor
3.3、箭头函数中没有arguments,但是可以通过rest参数获取参数
let fun = () => {
console.log(arguments);
}
fun(1, 2, 3);//Uncaught ReferenceError: arguments is not defined
箭头函数和普通函数的区别?
- 表现形式不同
- 普通函数有arguments,箭头函数没有,但可以通过rest参数获取参数
- 普通函数有this,且会随着调用者不同而改变,箭头函数没有this,在其中使用this,则是指向声明箭头函数的当前作用域的this,且this值是静态的,不会再改变
- 普通函数可以作为构造函数创建实例,箭头函数不能作为构造函数创建实例
- 箭头函数不能用作生成器函数,也不能在其中使用yield关键字,而普通函数可以
九、扩展运算符
表现形式:...
扩展运算符的应用
数组的合并
let arr1 = [1, 2, 3];
let arr2 = ["cym", "wtx"];
let arr3 = [...arr1, ...arr2];
console.log(arr3);//[1, 2, 3,"cym", "wtx"]
数组的克隆(浅克隆:若是数组中含有引用类型,则克隆的是引用地址)
let arr1 = ['abc', 1, 2, 3, { name: 'cym' }];
let arr2 = [...arr1];
console.log(arr2);//[ 'abc', 1, 2, 3, { name: 'cym' } ]
将伪数组转化为真正的数组
let boxes = document.getElementsByTagName("div");
let arr = [...boxes];
console.log(arr);
十、对象方法的扩展
Object.is():判断两个值是否完全相等
console.log(Object.is(112, 112));//true
console.log(Object.is(NaN, NaN));//true
console.log(NaN === NaN);//false
Object.assign():对象的合并(若是两个对象中含有相同的属性,则后者会覆盖前者的属性值)
const obj1 = {
name: "spylent",
age: 13,
phone: 13737083879,
address: "dsjaibfcaweujdc"
}
const obj2 = {
name: "疯兔",
age: 15,
phone: 1328473623,
gender: "男"
}
console.log(Object.assign(obj2, obj1));
//{name: 'spylent',age: 13,phone: 13737083879,gender: '男',address: 'dsjaibfcaweujdc'}
Object.getOwnPropertyDescriptor():返回某个对象属性的描述对象
const obj = {
foo: 123,
get bar() { return 'abc' }
};
Object.getOwnPropertyDescriptors(obj)
// { foo:
// { value: 123,
// writable: true,
// enumerable: true,
// configurable: true },
// bar:
// { get: [Function: get bar],
// set: undefined,
// enumerable: true,
// configurable: true } }
Object.setPrototypeOf():用来设置一个对象的原型对象、Object.getPrototypeOf():用来获取一个对象的原型对象
const school = {
name: "啊啊啊"
}
const citys = {
xiaoqu: ["北京", "上海"]
}
Object.setPrototypeOf(school, citys);
console.log(Object.getPrototypeOf(school));//{ xiaoqu: [ '北京', '上海' ] }
console.log(school);//{ name: '啊啊啊' }
Object.keys(),Object.values(),Object.entries()
都会返回一个数组
var obj = { foo: 'bar', baz: 42 };
Object.keys(obj)
// ["foo", "baz"]
const obj = { foo: 'bar', baz: 42 };
Object.values(obj)
// ["bar", 42]
const obj = { foo: 'bar', baz: 42 };
Object.entries(obj)
// [ ["foo", "bar"], ["baz", 42] ]
Object.fromEntries()
Object.fromEntries()方法是Object.entries()的逆操作,用于将一个键值对数组转为对象。
该方法的主要目的,是将键值对的数据结构还原为对象,因此特别适合将 Map 结构转为对象。
Object.fromEntries([
['foo', 'bar'],
['baz', 42]
])
// { foo: "bar", baz: 42 }
十一、数组方法的扩展
Array.from()、Array.of()
from方法就是可以将两类对象转为数组:类数组对象以及可迭代对象(set/map)
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
Array.from('hello')
// ['h', 'e', 'l', 'l', 'o']
let namesSet = new Set(['a', 'b'])
Array.from(namesSet) // ['a', 'b']
of方法是为了弥补数组构造函数Array()的不足
let arr = new Array(5)
console.log(arr)//[ <5 empty items> ]
console.log(arr[0])//undefined
let arr1 = Array.of(5)
console.log(arr1)//[ 5 ]
find(),findIndex(),findLast(),findLastIndex()
- find():用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。
[1, 5, 10, 15].find(function(value, index, arr) {
return value > 9;
}) // 10
- findIndex():返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1
[1, 5, 10, 15].findIndex(function(value, index, arr) {
return value > 9;
}) // 2
这两个方法都可以发现NaN,弥补了数组的indexOf()方法的不足
- findLast()和findLastIndex(),从数组的最后一个成员开始,依次向前检查,其他都保持不变
const array = [
{ value: 1 },
{ value: 2 },
{ value: 3 },
{ value: 4 }
];
array.findLast(n => n.value % 2 === 1); // { value: 3 }
array.findLastIndex(n => n.value % 2 === 1); // 2
fill()
const array = [1, 2, 3, [4, 5]]
array.fill("spyelnt", 0, 2)
console.log(array)//[ 'spyelnt', 'spyelnt', 3, [ 4, 5 ] ]
const array1 = [1, 2, 3, [4, 5]]
array1.fill("spyelnt", 0, 4)
console.log(array1)//[ 'spyelnt', 'spy
entries(),keys() 和 values()
keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历
都会返回一个迭代器对象
for (let index of ['a', 'b'].keys()) {
console.log(index);
}
// 0
// 1
for (let elem of ['a', 'b'].values()) {
console.log(elem);
}
// 'a'
// 'b'
for (let [index, elem] of ['a', 'b'].entries()) {
console.log(index, elem);
}
// 0 "a"
// 1 "b"
includes()
弥补了indexOf无法发现NaN的缺陷;
该方法的第二个参数表示搜索的起始位置,默认为0。如果第二个参数为负数,则表示倒数的位置,如果这时它大于数组长度(比如第二个参数为-4,但数组长度为3),则会重置为从0开始。
[1, 2, 3].includes(2) // true
[1, 2, 3].includes(4) // false
[1, 2, NaN].includes(NaN) // true
[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true
flat(),flatMap()
- flat() :用于将数组扁平化,默认只会“拉平”一层,如果想要“拉平”多层的嵌套数组,可以将flat()方法的参数写成一个整数,表示想要拉平的层数
[1, 2, [3, 4]].flat()
// [1, 2, 3, 4]
[1, 2, [3, [4, 5]]].flat(2)
// [1, 2, 3, 4, 5]
//如果不管有多少层嵌套,都要转成一维数组,可以用Infinity关键字作为参数
[1, [2, [3]]].flat(Infinity)
// [1, 2, 3]
- flatMap()方法:对原数组的每个成员执行一个函数(相当于执行Array.prototype.map()),然后对返回值组成的数组执行flat()方法。该方法返回一个新数组,不改变原数组。
flatMap()只能展开一层数组
[2, 3, 4].flatMap((x) => [x, x * 2])
// [2, 4, 3, 6, 4, 8]
copyWithin()
在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组。
const array = [1, 2, 3, [4, 5], 11, 33];
console.log(array.copyWithin(0, 3, -2));//[ [ 4, 5 ], 2, 3, [ 4, 5 ], 11, 33 ]
//上述代码意思是将索引下标为3的元素到-2往前的一个元素为止,也就是只复制[4,5],
//覆盖0号位的元素
[1, 2, 3, 4, 5].copyWithin(0, 3)
// [4, 5, 3, 4, 5]
//上面代码表示将从 3 号位直到数组结束的成员(4 和 5),复制到从 0 号位开始的位置,结果覆盖了原来的 1 和 2