let 与 const
- var:ES5中用于声明变量的关键字,存在各种问题,例如
-
// 1.声明提升 console.log(name); //哈哈 var name = "哈哈"; // 2. 变量覆盖,发生变量覆盖可能会导致数据丢失以及各种不可预知的bug var demo = "小明"; var demo = "小红"; console.log(demo)//小红 // 3. 没有块级作用域,i是定义在循环体之内的,原则上讲只能在循环体内打印 function fn2(){ for(var i = 0; i < 5; i++){ } console.log(i); //5 } fn2();
-
let:ES6新增,用于声明变量,有块级作用域 下面换成let就不会存在上述问题:
-
// 1. 不会存在声明提前,必须先声明 console.log(name); //此处会报错 let name = "哈哈"; // 2. 不会有变量覆盖 let demo = "小明"; let demo = "小红"; console.log(demo)// 此处会报错 告诉你已经定义了此变量 // 3. 有块级作用域 function fn2(){ for(let i = 0; i < 5; i++){ } console.log(i); // 此处会报错,无法打印 } fn2();
- const:ES6新增 声明一个只读的常量,一旦声明,常量的值就不能改变
- 一般用于全局变量
- 通常变量名全部大写(请按照规则来,不要乱搞,容易出事情)
const PI = "3.1415926";
解构赋值
- 解构赋值是对赋值运算符的扩展
- 针对数组或者对象进行模式匹配,然后对其中的变量进行赋值
- 代码简洁且易读,语义更加清晰明了,方便了复杂对象中数据字段获取
- 如果解构不成功,变量的值就等于
undefined
1 对象解构赋值
let url = { username: "小红", password: "123455" };
let { username, password } = url;
console.log(username); //"小红"
console.log(password); //"123455"
2 数组解构赋值
let [, , third] = ["foo", "bar", "baz"];
console.log(third);// "baz"
3 函数参数的解构赋值
function move({ x = 0, y = 0 } = {}) {
return [x, y];
}
console.log(move({ x: 3, y: 8 })); // [3, 8]
console.log(move({ x: 3 })); // [3, 0]
console.log(move({})); // [0, 0]
console.log(move()); // [0, 0]
[...]扩展运算符
用三个点号表示,功能是把数组或类数组对象展开成一系列用逗号隔开的散列值。
将数组展开为函数的参数
如果一个函数需要接收多个参数,而这些参数已经存储在一个数组中,那么使用扩展运算符可以很方便地将数组展开为函数的参数,避免手动指定每个参数
例1.
function myFunction(a, b, c) {
console.log(a, b, c);
}
const arr = [1, 2, 3];
myFunction(...arr); // 相当于 myFunction(1, 2, 3);
例2.
function fun(name, age, city) {
console.log(`Name: ${name}, Age: ${age}, City: ${city}`);
}
const person = ['Alice', 30, 'New York'];
fun(...person); // Name: Alice, Age: 30, City: New York
复制数组
const array = [1, 2, 3];
const copiedarray = [...array];
console.log(copiedArray); // 输出 [1, 2, 3]
合并数组
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combinedArray = [...arr1, ...arr2];
console.log(combinedArray); // 输出 [1, 2, 3, 4, 5, 6]
浅拷贝
const obj1 = { key1: 'value1', key2: 'value2' };
const obj2 = { ...obj1, key3: 'value3' }; // 创建一个新对象,包含 obj1 的所有属性和新属性 key3
console.log(obj2); // 输出 { key1: 'value1', key2: 'value2', key3: 'value3' }
rest 参数
也是三个点号(形式为...变量名),不过其功能与扩展运算符恰好相反,把逗号隔开的散列组合成一个数组(返回数组就说明可以使用数组的一些方法),用于获取函数的多余参数
多个参数使用时rest 参数必须放在最后
function fun(a, b, ...values) {
console.log(a); //1
console.log(b); //2
console.log(values); //[3, 4, 5, 6]
}
fun(1, 2, 3, 4, 5, 6);
模板字符串
- 模板字符串相当于加强版的字符串,用反引号 ``
- 除了作为普通字符串,还可以用来定义多行字符串,可以在字符串中加入变量和表达式
1 普通字符串
// 普通字符串
let ttring = "hello"+"小兄弟";
console.log(ttring);// hello小兄弟
// 如果想要换行\n
let tring = "hello'\n'小兄弟"
console.log(tring);// hello
// 小兄弟
2 模板字符串
let str1 = "穿堂而过的";
let str2 = "风";
// 模板字符串
let newStr = `我是${str1}${str2}`;
console.log(newStr)// 我是穿堂而过的风
// 字符串中调用方法
function fn3(){
return "帅的不行!";
}
let string2= `我真是${fn3 ()}`;
console.log(string2); // 我真是帅的不行!
箭头函数
- 箭头函数是一种更加简洁的函数书写方式
- 箭头函数本身没有作用域(无this)
- 箭头函数的this指向上一层,上下文决定其this
- 基本语法:参数 => 函数体
a. 基本用法
let fn = v => v;
//等价于
let fnn = function(num){
return num;
}
fnn(100);
console.log(fnn(100));// 输出100
b. 带参数的写法
let fn2 = (num1,num2) => {
let result = num1 + num2;
return result;
}
fn2(3,2);
console.log(fn2(3,2));// 输出5
c. 箭头函数中的this指向问题
- 箭头函数体中的 this 对象,是定义函数时的对象,而不是使用函数时的对象。在函数定义的时候就已经决定了
function fn3(){
setTimeout(()=>{
// 定义时,this 绑定的是 fn3 中的 this 对象
console.log(this.a);
},0)
}
var a = 10;
// fn3 的 this 对象为 {a: 10},因为它指向全局: window.a
fn3.call({a: 18}); // 改变this指向,此时 a = 18
Class类
- class (类)作为对象的模板被引入,可以通过 class 关键字定义类
- class 的本质是 function,同样可以看成一个块
- 可以看作一个语法糖,让对象原型的写法更加清晰
- 更加标准的面向对象编程语法
1 类的定义
// 匿名类
let Demo = class {
constructor(a) {
this.a = a;
}
}
// 命名类
let Demo = class Demo {
constructor(a) {
this.a = a;
}
}
2 类的声明
- 类不能重复声明
- 类定义不会被提升,必须在访问前对类进行定义,否则就会报错。
- 类中方法不需要 function 关键字。
- 方法间不能加分号
class Demo {
constructor(a) {
this.a = a;
}
}
3 类的主体
- 公共属性(依然可以定义在原型上)
class Demo{} Demo.prototype.a = 2;
- 实例属性
class Demo { a = 2; constructor () { console.log(this.a); } }
- 方法:constructor
class Demo{ constructor(){ console.log('我是构造器'); } } new Demo(); // 我是构造器
5.4 实例化对象
class Demo { constructor(a, b) { this.a = a; this.b = b; console.log('Demo'); } sum() { return this.a + this.b; } } let demo1 = new Demo(2, 1); let demo2 = new Demo(3, 1); // 两者原型链是相等的 console.log(demo1._proto_ == demo2._proto_); // true
Map()
- Map 是一个构造函数,通过 new 生成 Map 数据结构实例。 类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应。
1 Maps 和 Objects 的区别
- 一个 Object 的键只能是字符串或者 Symbols,但一个 Map 的键可以是任意值
- Map 中的键值是有序的(FIFO 原则),而添加到对象中的键则不是
- Map 的键值对个数可以从 size 属性获取,而 Object 的键值对个数只能手动计算
//声明
let m = new Map();
//添加元素
m.set('name', '小红');
console.log(m); //key: "name" value: "小红"
2 Map中的key
// 1. key是字符串
let myMap = new Map();
let keyString = "string";
myMap.set(keyString, "和键'string'关联的值");
// keyString === 'string'
myMap.get(keyString); // "和键'string'关联的值"
myMap.get("string"); // "和键'string'关联的值"
// 2.key是对象
let myMap = new Map();
let keyObj = {},
myMap.set(keyObj, "和键 keyObj 关联的值");
myMap.get(keyObj); // "和键 keyObj 关联的值"
myMap.get({}); // undefined, 因为 keyObj !== {}
// 3. key也可以是函数或者NaN
3 Map 的迭代
// 1.使用 forEach
let myMap = new Map();
myMap.set(0, "zero");
myMap.set(1, "one");
// 0 = zero , 1 = one
myMap.forEach(function(value, key) {
console.log(key + " = " + value);
}, myMap)
// 2. 也可以使用 for...of
4 Map 与 Array的转换
letkvArray = [["key1", "value1"], ["key2", "value2"]];
// Map 构造函数可以将一个 二维 键值对数组转换成一个 Map 对象
let myMap = new Map(kvArray);
// 使用 Array.from 函数可以将一个 Map 对象转换成一个二维键值对数组
let outArray = Array.from(myMap);
5 Map和ForEach 的区别
.forEach() 适合于你并不打算改变数据的时候,而只是想用数据做一些事情
let arr = ['a', 'b', 'c', 'd'];
arr.forEach((val) => {
console.log(val); // 依次打印出 a,b,c,d
});
- map() 适用于你要改变数据值的时候,它更快,而且返回一个新的数组
let arr = [1, 2, 3, 4, 5]; let arr2 = arr.map(num => num * 2).filter(num => num > 5); console.log(arr2);// arr2 = [6, 8, 10]
set
var s2 = new Set(['小明', '小兰', '小红']);
console.log(s2, 's2'); //{'小明', '小兰', '小红'}
//删除元素
s2.delete('小明');
console.log(s2, 's2'); //{'小兰', '小红'}
//检测元素是否存在
console.log(s2.has('小明')); //false
//清空
s2.clear();
console.log(s2, 's2'); //Set(0) {}
数组去重
let arr = [1, 2, 3, 4, 5, 6, 2, 3, 1];
let result = [...new Set(arr)];
console.log(result); //1, 2, 3, 4, 5, 6
交集,相同的地方,包含
let arr = [1, 2, 3, 4, 5, 6, 2, 3, 1];
let arr1 = [4, 5, 6, 5, 6];
let result1 = [...new Set(arr)].filter(item => {
let s2 = new Set(arr1);
if (s2.has(item)) {
return true;
} else {
return false;
}
});
console.log(result1); //4, 5, 6
简写:
let result1 = [...new Set(arr)].filter(item => new Set(arr1).has(item));
并集
let arr = [1, 2, 3, 4, 5, 6, 2, 3, 1];
let arr1 = [4, 5, 6, 5, 6];
let union = [...new Set([...arr, ...arr1])];
console.log(union); // [1, 2, 3, 4, 5, 6]
差集,A里面有B里面没有,交集的逆运算
let arr = [1, 2, 3, 4, 5, 6, 2, 3, 1];
let arr1 = [4, 5, 6, 5, 6];
let result1 = [...new Set(arr)].filter(item => !(new Set(arr1).has(item)));
console.log(result1); //[1, 2, 3]
for...of 循环
可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如 arguments对象、DOM NodeList 对象)、字符串。 for...of 不能遍历普通 Object 对象。 for...in 循环,只能获得对象的键名,不能直接获取属性值。 for...of 循环,允许遍历获得属性值。
Promise
//实例化Promise,resolve参数成功,reject参数失败
const p = new Promise((resolve, reject) => {
setTimeout(function() {
let data = '数据库中的用户数据';
resolve(data, '成功');
let err = '数据读取失败';
reject(err, '失败');
}, 1000)
});
//调用then方法
p.then(function(value) {
console.log(value, '成功')
}, function(reason) {
console.log(reason, '失败')
})
使用Promise封装AJAX请求
const p = new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.xxx.com/xx');
xhr.send();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.response)
} else {
reject(xhr.status)
}
}
}
})
p.then(function(value) {
console.log(value, '成功');
}, function(reason) {
console.log(reason, '失败');
})
async/await
async 也是处理异步的,它是对 Promise 的一种扩展,让异步更加方便
async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数。当函数执行的时候,一旦遇到 await 就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
Symbol
作用:给对象添加方法和属性,独一无二的
//创建Symbol
let s = Symbol();
console.log(s, typeof s); //Symbol() 'symbol'
//创建Symbol
let s2 = Symbol('Symbol');
let s3 = Symbol('Symbol');
s2 === s3; //false
//Symbol.for()创建
let s4 = Symbol.for('Symbol');
let s5 = Symbol.for('Symbol');
s4 === s5; //true
例:给对象添加方法
let game = {
name: 'xiaohong'
}
let methods = {
up: Symbol(),
down: Symbol()
}
game[methods.up] = function() {
console.log('up');
}
game[methods.down] = function() {
console.log('down');
}
console.log(game); //{name: 'xiaohong', Symbol(): ƒ, Symbol(): ƒ}
game[methods.up](); // 调用 up 方法,输出 'up'
game[methods.down](); // 调用 down 方法,输出 'down'
迭代器
自定义遍历
var arr = ['小明', '小兰', '小红']
let iterator = arr[Symbol.iterator]();
//调用next方法返回一个对象,value表示当前成员的值,done表示遍历是否结束
console.log(iterator.next()); //value: '小明', done: false
console.log(iterator.next()); //value: '小兰', done: false
console.log(iterator.next()); //value: '小红', done: false
console.log(iterator.next()); //value: undefined, done: true
案例:
var arr = {
age: '18',
name: ['小明', '小兰', '小红'],
[Symbol.iterator]() {
let index = 0;
return {
next: () => {
if (index < this.name.length) {
const result = {value: this.name[index],done: false}
index++;
return result;
}else{
return {value: undefined,done: true}
}
}
}
}
}
for (let item of arr) {
console.log(item);
}
生成器
生成器其实就是一个特殊的函数
针对异步编程 纯回调函数 hode fs ajax mongodb
function* gen() {
console.log('hello');
console.log('111');
}
let iterator = gen();
iterator.next(); //hello
iterator.next(); //111
案例:1s后控制台输出111,2s后控制台输出222,3s后控制台输出333,
setTimeout
是用于在指定时间后执行指定函数的 JavaScript 函数,属于异步编程的一种常见方式。调用 setTimeout
不会阻塞后续代码的执行,而是在等待指定的时间后将回调函数放入任务队列中,等待事件循环执行
function noe() {
setTimeout(() => {
console.log('111');
iterator.next();
}, 1000)
}
function two() {
setTimeout(() => {
console.log('222');
iterator.next();
}, 2000)
}
function three() {
setTimeout(() => {
console.log('333');
iterator.next();
}, 3000)
}
function* gen() {
yield noe(); //调用函数
yield two();
yield three();
}
let iterator = gen();
iterator.next();