let 关键字用来声明变量,使用let 声明的变量有几个特点:
1) 不允许重复声明
2) 块儿级作用域
3) 不存在变量提升
4) 不影响作用域链
5) 暂时性死区
6)不与顶级对象挂钩
在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”。该变量在声明之前使用都属于“暂时性死区“。
应用场景:以后声明变量使用let 就对了
const 关键字用来声明常量,const 声明有以下特点
1) 声明必须赋初始值
2) 标识符一般为大写(建议)
3) 不允许重复声明
4) 值不允许修改
const实际上保存的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。
但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。
注意: 对象属性修改和数组元素变化不会出发const 错误, 对象地址不可改变。
5) 块儿级作用域
6)、 不与顶层对象挂钩
应用场景:声明对象类型使用const,非对象类型声明选择let
面试题1.let和const的区别(/var,let,const的区别?)
let声明的变量可以改变,值和类型都可以改变(let:声明的是变量);
const声明的常量不可以改变,这意味着,const一旦声明,就必须立即初始化,不能以后再赋值,当然数组和对象等复合类型的变量,变量名不指向数据,而是指向数据所在的地址。
const只保证变量名指向的地址不变,并不保证该地址的数据不变。
let和const总结
let 声明的变量会产生块作用域,var 不会产生块作用域
const 声明的常量也会产生块作用域
不同代码块之间的变量无法互相访问
注意: 对象属性修改和数组元素变化不会出发 const 错误 (数组和对象存的是引用地址)
应用场景:声明对象类型使用 const,非对象类型声明选择 let
cosnt声明必须赋初始值,标识符一般为大写,值不允许修改。
ES6 允许按照一定模式,从数组和对象中快速的提取成员,对变量进行赋值,这被称为解构赋值。
本质上,只要等号两边的模式相同,左边的变量就会被赋予对应的值。
ES6中允许从数组中提取值,按照对应位置,对变量赋值
对象的解构与数组有一个重要的不同。数组的元素是按顺序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值,否则解构失败就是undefined。
let [a, b, c] = "hello";
console.log(a); //h
console.log(b); //e
console.log(c); //l
let { length } = "hello";
console.log(length);//5
let a = 1;
let b = 2;
[a, b] = [b, a];
console.log(a);
function myfun() {
return [2, 3, 4];
}
let [a, b, c] = myfun();
console.log([a, b, c]);//[2, 3, 4]
console.log(a);//2
console.log(b);//3
console.log(c);//4
function myfun([a, b, c]) {
console.log(a);
}
myfun([4, 5, 6]);
ES6 引入新的声明字符串的方式 『``』 '' ""
判断字符串中是否存在指定字符,返回布尔值, 语法:string.includes("xxx")
repeat()方法返回一个新字符串,表示将原字符串重复n次。str.repeat(数值)
JavaScript 表示的最小精度,一般用在浮点数运算上,
EPSILON 属性的值接近于 2.2204460492503130808472633361816E-16
console.log(0.1 + 0.2 === 0.3);//false
function equal(a, b) {
if (Math.abs(a - b) < Number.EPSILON) {
return true;
} else {
return false;
}
}
console.log(equal(0.1 + 0.2, 0.3));//true
let b = 0b1010;// 0b开头 二进制
let o = 0o77;// 0o开头 八进制
let d = 100; //十进制
let x = 0xff;//0x开头 十六进制
如果传递的值是有限数字,则返回true。 布尔值,字符串,对象,数组等所有其他内容均返回false:
console.log(Number.isFinite(100));//true
console.log(Number.isFinite(100/0));//false
console.log(Number.isFinite(Infinity));//false
console.log(Number.isFinite(NaN)); //false
检测一个数值是否为 NaN,只有对于NaN才返回true,非NaN一律返回false。
console.log(Number.isNaN(123));//false
字符串转整数,必须以数字开头
console.log(Number.parseInt('5211314love'));
console.log(Number.parseFloat('3.1415926神奇'));
判断一个数是否为整数
console.log(Number.isInteger(5));//true
console.log(Number.isInteger(2.5));//false
将数字的小数部分抹掉
console.log(Math.trunc(3.5));//3
判断一个数到底为正数负数还是零,对于非数值,会先将其转换为数值。
console.log(Math.sign(100));//1
console.log(Math.sign(0));//0
console.log(Math.sign(-20000));//-1
将伪数组或可遍历对象转换为真正的数组。
该方法主要应用于查找第一个符合条件的数组元素
它的参数是一个回调函数。
在回调函数中可以写你要查找元素的条件,当条件成立为true时,返回该元素。
如果没有符合条件的元素,返回值为undefined
定义:用于找出第一个符合条件的数组成员的位置,如果没有找到返回-1。
定义:判断某个数组是否包含给定的值,返回布尔值。
将一组值转化为数组,即新建数组
使用自己想要的参数替换原数组内容,但是会改变原来的数组
可以传多个参数
1个参数:默认从数组第一位开始替换
2个参数:(替换的值,替换索引位置)
3个参数:(替换的值,替换索引开始位置,替换索引结束位置【不包括】)
JavaScript中31个数组方法,包括es5,es6新增方法(关注收藏,持续更新)_js数组方法以及es6新增的数组方法有哪些-CSDN博客
扩展运算符(spread)也是三个点(...)。它好比rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列,对数组进行解包。
扩展运算符的应用
ES6 新增了一些 Object 对象的方法
ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。
注意:对象简写形式简化了代码,所以以后用简写就对了
比较两个值是否严格相等,与『===』行为基本一致 (+0 与 NaN)
console.log(Object.is(120, 120));// ===
console.log(Object.is(NaN, NaN));// true
console.log(NaN === NaN);// false
对象的合并,将原对象的所有可枚举属性,复制到目标对象,后面的对象会覆盖前面的对象一样的属性
Object.assign(target, object1,object2)的第一个参数是目标对象,后面可以跟一个或多个源对象作为参数。
target:参数合并后存放的对象
object1:参数1
object2:参数2
const obj1 = {
name: "tom",
age: 18,
};
const obj2 = {
name: "bob",
age: 28,
sex: "男",
};
console.log(Object.assign(obj1, obj2));
//{name: 'bob', age: 28, sex: '男'}
可以直接设置对象的原型,不建议使用
setPrototypeOf(school, cities)
参数1:给谁设置原型对象 school
参数2:设置哪个原型对象 cities
const school = {
name: "bdqn",
};
const cities = {
xiaoqu: ["北京", "上海", "深圳"],
};
// 设置原型
Object.setPrototypeOf(school, cities);
// 获取原型
Object.getPrototypeOf(school);
console.log(school);
ES6 允许使用「箭头」(=>)定义函数。
箭头函数只能简写函数表达式,不能简写声明式函数
function fn() {} // 不能简写
const fun = function () {}; // 可以简写
const obj = {
fn: function () {}, // 可以简写
};
():函数的形参
=>:必须的语法,指向代码块
{}:代码块
this 是静态的. this 始终指向函数声明时所在作用域下的 this 的值,没有自己的this
不能作为构造实例化对象 会报错
不能使用 arguments 变量
箭头函数的简写
1) 省略小括号, 当形参有且只有一个的时候
2) 函数体如果只有一条语句,则花括号可以省略,函数的返回值为该条语句的
执行结果
注意:箭头函数不会更改this 指向,用来指定回调函数会非常合适
补充this指向问题汇总
window.age = 18;
function fn() {
console.log(this.age);//18
}
fn()
当函数作为对象方法被调用时this指向该对象
var obj = {
age: 18,
fn: function () {
console.log(this === obj);//true
console.log(this.age);//18
}
}
console.log(obj.fn());
构造函数里的this指向 new创建的实例化对象(没有return的情况下)
如果构造函数内出现了return并且是一个object对象那么最终的运算结果返回这个对象
只要构造函数不返回数据或者返回基本数据类型 this仍然指向实例
function Fn() {
this.age = 18;
//此时a.age是return的结果20
return {
age: 20,
};
}
let a = new Fn();
console.log(a.age); //20
在function的原型上有三个方法 call apply bind,所有函数都是Function的实例,所以所有的函数都可以调用这三个方法,而这三个方法都是用来改变this指向的
定义:call(thisObj,Object) 调用一个对象的一个方法,以另一个对象替换当前对象。
说明:call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。
function fn(x, y) {
console.log('加油');
console.log(this);//this指向window
console.log(x + y);
}
var o = {
name: 'andy'
}
fn.call()//call 呼叫 可以调用函数
fn.call(o, 1, 2)//第一个值是this指向, 后边实参
fn.apply(o, [10, 20])//apply传递数组
fn.call(10, 20)//this-->new Numbe(10) x-->20 y-->undefined
箭头函数里面没有自己的this 他只会从自己的作用域链上一层继承this
this.age = 20;
var obj = {
age: 18,
fn: () => {
console.log(this.age);//20
}
}
obj.fn()
function add(a, b, c = 10) {
return a + b + c;
}
//let res1 = add(1, 2, 3);//6
let res2 = add(1,1);//12
cons
ole.log(res2);
function connect({ name, age, sex, price = "3000" }) {
console.log(name);
console.log(age);
console.log(sex);
console.log(price);
}
connect({
name: "章三",
age: 20,
sex: "男",
// price: 4000,
});
const fn = (a = 10) => {
console.log(a);
};
fn(); // 不传递参数的时候,函数内部的 a 就是 10
fn(20); // 传递了参数 20 的时候,函数内部的 a 就是 20
注意: 箭头函数如果你需要使用默认值的话,那么一个参数的时候也需要写()
ES6 引入rest 参数,用于获取函数的实参,用来代替arguments
注意:rest 参数非常适合不定个数参数函数的场景
ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它是JavaScript 语言的第七种数据类型,是一种类似于字符串的数据类型。
undefined string symbol object null number boolean
Symbol 特点
1) Symbol 的值是唯一的,用来解决命名冲突的问题
2) Symbol 值不能与其他数据进行运算
3) Symbol 定义的对象属性不能使用for…in 循环遍历,但是可以使用Reflect.ownKeys 来获取对象的所有键名
注: 遇到唯一性的场景时要想到Symbol
script>
//创建方式
//创建Symbol方式1
let s = Symbol();
// console.log(s, typeof s);
//创建方式2
// Symbol()函数可以接受一个字符串作为参数,
// 表示对 Symbol 实例的描述。这主要是为了在控制台显示,比较容易区分。
let s2 = Symbol('bdqn');//'bdqn'这个内容只是个标识
let s3 = Symbol('bdqn');
console.log('s2===s3',s2===s3);//false
// 创建方式3 Symbol.for
// Symbol.for并不是每次都会创建一个新的symbol,它会先检索symbol表中是否有,没有再创建新的,有就返回上次存储的
let s4 = Symbol.for('bdqn');
let s5 = Symbol.for('bdqn');
console.log('s4===s5',s4===s5);//true
//注意事项
//1:不能与其他数据进行运算
// let result = s + 100;
// let result = s > 100;
// let result = s + s;
可以给对象添加属性和方法
除了定义自己使用的Symbol 值以外,ES6 还提供了11 个内置的Symbol 值,指向语言内部使用的方法。可以称这些方法为魔术方法,因为它们会在特定的场景下自动执行。
Symbol.hasInstance |
当其他对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法 |
Symbol.isConcatSpreadable |
对象的Symbol.isConcatSpreadable属性等于的是一个布尔值,表示该对象用于Array.prototype.concat()时,是否可以展开。 |
Symbol.species |
创建衍生对象时,会使用该属性 |
Symbol.match |
当执行str.match(myObject)时,如果该属性存在,会调用它,返回该方法的返回值。 |
Symbol.replace |
当该对象被str.replace(myObject)方法调用时,会返回该方法的返回值。 |
Symbol.search |
当该对象被str.search(myObject)方法调用时,会返回该方法的返回值。 |
Symbol.split |
当该对象被str.split(myObject)方法调用时,会返回该方法的返回值。 |
Symbol.iterator |
对象进行for...of循环时,会调用Symbol.iterator方法,返回该对象的默认遍历器 |
Symbol.toPrimitive |
该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。 |
Symbol.toStringTag |
在该对象上面调用toString方法时,返回该方法的返回值 |
Symbol.unscopables |
该对象指定了使用with关键字时,哪些属性会被with环境排除。 |
遍历器(Iterator)就是一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator 接口,就可以完成遍历操作。(Iterator 接口就是对象里面的一个属性)
Iterator 接口就是对象的一个属性
Iterator 的作用有三个:
一是为各种数据结构,提供一个统一的、简便的访问接口;
二是使得数据结构的成员能够按某种次序排列;
三是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of循环
1) ES6 创造了一种新的遍历命令for...of 循环,Iterator 接口主要供for...of 消费
2) 原生具备iterator 接口的数据(可用for of 遍历)
a) Array
b) Arguments
c) Set
d) Map
e) String
f) TypedArray
g) NodeList
3) 工作原理(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
(2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
(3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
(4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。
(5) 每调用next 方法返回一个包含value 和done 属性的对象
done:是否循环完毕
注: 迭代器就是按照一定的顺序对元素进行遍历的过程
Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同
Generator 函数是一个状态机,封装了多个内部状态。
执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。
通过function关键字后的星号(*)来表示,函数中会用到新的关键字yield。星号(*)可以紧挨着function关键字,也可以在中间添加一个空格
代码说明:
1) cook()前的星号 * 表明它是一个生成器
2) 生成器函数返回的结果是迭代器对象,调用迭代器对象的next 方法可以得到yield 语句后的值
3) yield 相当于函数的暂停标记,每当执行完一条yield语句后函数就会自动停止执行,也可以认为是函数的分隔符,每调用一次next方法,执行一段代码
4) next 方法可以传递实参,作为yield 语句的返回值
5)yield关键字只可在生成器内部使用,在其他地方使用会导致程序抛出错误
概念:next('BBB')传入的参数作为上一个next方法的返回值。
(3)、生成器函数实例
回调地狱:就是回调函数嵌套过多导致的
当一个回调函数嵌套一个回调函数的时候
就会出现一个嵌套结构
当嵌套的多了就会出现回调地狱的情况
比如我们发送三个 ajax 请求
第一个正常发送
第二个请求需要第一个请求的结果中的某一个值作为参数
第三个请求需要第二个请求的结果中的某一个值作为参数
Promise是ES6异步编程的一种解决方案(目前最先进的解决方案是async和await的搭配(ES8),但是它们是基于promise的)
从语法上讲,Promise是一个对象或者说是构造函数,用来封装异步操作并可以获取其成功或失败的结果。
a、对象的状态不受外界影响。
Promise 对象通过自身的状态,来控制异步操作。Promise 实例具有三种状态。
pending: 等待中,或者进行中,表示还没有得到结果
resolved(Fulfilled): 已经完成,表示得到了我们想要的结果,可以继续往下执行
rejected: 也表示得到结果,但是由于结果并非我们所愿,因此拒绝执行
b、Promise对象三种状态不受外界影响,三种的状态的变化途径只有两种。
从“未完成”到“成功”
从“未完成”到“失败”
一旦状态发生变化,就凝固了,不会再有新的状态变化。这也是 Promise 这个名字的由来,它的英语意思是“承诺”,一旦承诺成效,就不得再改变了。这也意味着,Promise 实例的状态变化只可能发生一次。
c、Promise 的最终结果只有两种。
异步操作成功,Promise 实例传回一个值(value),状态变为fulfilled。
异步操作失败,Promise 实例抛出一个错误(error),状态变为rejected。
//写法一
new Promise(function (resolve, reject) {
// resolve 表示成功的回调
// reject 表示失败的回调
}).then(function (res) {
// 成功的函数
}).catch(function (err) {
// 失败的函数
})
//写法二
new Promise(function (resolve, reject) {
// resolve 表示成功的回调
// reject 表示失败的回调
}).then(function (res) {
// 成功的函数
},function(res){
// 失败的函数
})
出现了new关键字,就明白了Promise对象其实就是一个构造函数,是用来生成Promise实例的。能看出来构造函数接收了一个函数作为参数,该函数就是Promise构造函数的回调函数,该函数中有两个参数resolve和reject,这两个参数也分别是两个函数!
简单的去理解的话
resolve函数的目的是将Promise对象状态变成成功状态,在异步操作成功时调用,将异步操作的结果,作为参数传递出去。
reject函数的目的是将Promise对象的状态变成失败状态,在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
注意:then方法可以接受两个函数,第一个函数为promise状态为成功的回调函数,第二个函数为promise状态为失败的回调函数(可以不写,一般用catch方法捕获promise状态为失败的异常信息)
const promise = new Promise((resolve,reject)=>{
//异步代码
setTimeout(()=>{
// resolve(['111','222','333'])
reject('error')
},2000)
})
//写法一
promise.then((res)=>{
//兑现承诺,这个函数被执行
console.log('success',res);
}).catch((err)=>{
//拒绝承诺,这个函数就会被执行
console.log('fail',err);
})
//写法二
promise.then(
function (value) {
console.log(value);
},
function (reason) {
console.error(reason);
}
);
p1,p2,p3为promise的实例对象
Promise.then()方法
then方法的返回结果是 Promise 对象, 返回对象状态由回调函数的执行结果决定,结果有以下:
a. 如果回调函数中返回的结果是 非 promise 类型的属性, 状态为成功, 返回值为对象的成功的值
b. 是 promise 对象,内部返回的状态,就是它的状态
c. 抛出错误 返回失败的promise状态
链式调用 可以改变回调地狱的情况
Promise.catch()
一般用catch方法捕获promise状态为失败的异常信息
Promise.all()
并发处理多个异步任务,所有任务都执行完成才能得到结果
Promise.all( [p1,p2,p3] ) .then ( (result) => {consoleog (result)
})
Promise.race()
只返回异步任务数组中第一个执行完的结果,其他任务仍在执行,不过结果会被抛弃
应用场景:
几个接口返回一样的数据,哪个快用哪个
Promise.race ( [p1,p2] ).then ( (result)=>{
console. log (result)
})
ES6 提供了新的数据结构 Set(集合)。它类似于数组,但成员的值都是唯一的,集合实现了iterator 接口,所以可以使用『扩展运算符』和『for…of…』进行遍历
实例的属性和方法
size:返回Set实例的成员总数。
Set.prototype.add(value):添加某个value。
Set.prototype.delete(value):删除某个value,返回一个布尔值,表示删除是否成功。
Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员。
Set.prototype.clear():清除所有成员,没有返回值。
set遍历
Set.prototype.keys():返回键名的遍历器
Set.prototype.values():返回键值的遍历器
Set.prototype.entries():返回键值对的遍历器
Set.prototype.forEach():遍历每个成员
let arr = new Set(["red", "green", "blue"]);
// 返回键名
for (let item of arr.keys()) {
// console.log(item);//red green blue
}
// 返回键值
for (let item of arr.values()) {
// console.log(item);//red green blue
}
// set 键名=键值
// 返回键值对
for (let item of arr.entries()) {
// console.log(item);// ['red', 'red'] ['green', 'green'] ['blue', 'blue']
}
// set也有forEach()方法
arr.forEach((value, key) => console.log(key + " : " + value));
ES6 提供了Map 数据结构。它类似于对象,也是键值对的集合。但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map 也实现了iterator 接口,所以可以使用『扩展运算符』和『for…of…』进行遍历。
Map 的属性和方法:
1) size 返回Map 的元素个数
2) set 增加一个新元素,返回当前Map
3) get 返回键名对象的键值
4) has 检测Map 中是否包含某个元素,返回boolean 值
5) clear 清空集合,返回undefined
JavaScript 语言中,生成实例对象的传统方法是通过构造函数,ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。
说明:使用class关键词 声明类,constructor为构造方法,一个类必须有constructor()方法,如果没有显式定义,一个空的constructor()方法会被默认添加,
this关键字则代表实例对象,
getstr()为普通方法,不要用es5完整写法,getstr()存在 prototype上。
pn1.constructor === pn1.prototype.constructor // true
es5可以直接给构造函数添加属性,方法,这些属性方法不在这个构造函数实例的对象身上
对应的es6中,类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
说明:Class 可以通过extends关键字实现继承,让子类继承父类的属性和方法。ES6 规定,子类必须在constructor()方法中调用super(),否则就会报错。
模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。
1) 防止命名冲突
2) 代码复用
3) 高维护性,可以对某些模块化升级
模块功能主要由两个命令构成:export 和import。
export 命令用于规定模块的对外接口,对外暴露
import 命令用于输入其他模块提供的功能 ,引入暴露的文件
(1)、分别暴露
//m1.js 分别暴露
export let school = 'bdqn';
export function teach() {
console.log("我们可以教给你开发技能");
}
// m2.js 统一暴露
let school = 'bdqn';
function findJob(){
console.log("我们可以帮助你找工作!!");
}
export {school, findJob};
//m3.js 默认暴露
export default {
school: 'bdqn',
change: function(){
console.log("我们可以改变你!!");
}
}
export和export default的区别(面试题)
1、两者均可用于导出常量、函数、文件、模块;
2、在一个文件中可以多次使用export,但是export default只能用一次;
3、通过export输出的,在import导入时需要使用{},export default不需要;
4、export与export default不可同时使用;
//1. 通用的导入方式
//引入 m1.js 模块内容
import * as m1 from './js/m1.js'
//引入 m2.js 模块内容
import * as m2 from "./js/m2.js";
//引入 m3.js
import * as m3 from "./js/m3.js";
console.log(m1);
//2. 解构赋值形式的导入方式
import { school, teach } from "./js/m1.js";
//用别名
import {school as yyzx, findJob} from "./js/m2.js";
import {default as m3} from "./js/m3.js";
//3. 简便形式的导入方式 针对默认暴露
import m3 from "./js/m3.js";
// app.js 入口文件
//模块引入
import * as m1 from "./m1.js";
import * as m2 from "./m2.js";
import * as m3 from "./m3.js";
console.log(m1);
console.log(m2);
console.log(m3);