Var,Let,Const 区别
var 声明
变量提升机制
,利用javaScript引擎,在代码预编译时,JavaScript引擎会自动将所有代码里面的var
关键字声明的语句都会提升到当前作用域的顶端。
下面我们来看:⬇⬇⬇
function person1(status) {
if (status) {
var value = "Cola"
} else {
console.log(value) // undefined
}
console.log(value) // undefined
}
function person2(status) {
var value;
if (status) {
value = "Cola"
} else {
console.log(value) // undefined
}
console.log(value) // undefined
}
经过 变量提升机制 person1的代码将变为 person2。
因此,Escript6 中为我们带了块级声明
。
- 只在当前函数下声明的变量有效;
- 在代码块和{ }括号之内有效;
let 声明
- 块级作用域,所有外面的语句块访问不到‘
- let是没有变量提升的;
下面我们来看:⬇⬇⬇
//let是没有变量提升
function person3(status) {
if (status) {
let value = "Cola"
} else {
console.log(value) // 报错
}
console.log(value) // 报错
}
-----------------------------------------------------
//块级作用域
console.log(value) // 报错
let value = "Cola"
ECMAscript6 中还提供了const
关键字声明,const
声明指的是常量,常量就是一旦定义完就不能修改的值。还有一点需要注意的是,常量定义必须初始化值,如果不初始化值就会报错,下面我们来看const。
const 声明
-
块级作用域,所有外面的语句块访问不到,没有变量提升;
-
const 定义的值必须定义初始值,并且不能修改;
-
const声明对象可以修改值,但是不可以重写整个对象;
const age; // 报错 必须定义初始值 const age = 14; age = 16; //报错 不能修改 const person = { name: "Cola", age: 23 } person.age = 18 person = {} // 报错 不能修改对象指针
let 和 const没有变量提升是为什么呢?
※ 暂时死区
报错是因为用 let 定义并初始化变量语句是不会执行的。此时的value还是处于在JavaScript所谓的暂时死区(temporal dead zone)
简称为TDZ。
我们来说一下 TDZ 工作原理,JavaScript引擎 在扫描代码时发现变量声明时,如果遇到var
就会将它们提升到当前作用域的顶端,如果遇到let或const
就会将声明放到 TDZ 中,此时访问 TDZ 中的变量就会抛出错误,只有执行完TDZ中的变量才会将它移出,然后就可以正常方法。这机制只会在当前作用域生效。
我们来看:
console.log(value) // 'undefined'
if (true) {
let value = "Cola"
}
此时就属于我们上面说的 暂时死区
var let const 最大的区别
var 在全局作用域声明的变量有一种行为会挂载在 window 对象上,它会创建一个新的全局变量作为全局对象的属性,这种行为说不定会覆盖到 window 对象上的某个属性,而let和const
声明的变量则不会有这一行为。
我们往下看:⬇⬇⬇
var value1 = "Cola1"
let value2 = "Cola11"
const value3 = "Cola111"
console.log(window.value1) // Cola1
console.log(window.value2) // undefined
console.log(window.value3) // undefined
箭头函数
注意使用场景:
- 方法中不能定义 构造函数 ;
- 不能够调用 arguments ;
- this 会跟随父级指向,父级没有默认指向 window ;
- 可以调用 call() 方法;
- 不能通过 new 关键字调用;
- 没有 原型 ;
- 适合与this无关的回调( 定时器 ,数组的方法 等);
- 不适合与this有关的回调( 对象的方法 ,事件回调 );
基本写法:
//ES6 增加了箭头函数:
let func = value => value;
//如果需要给函数传入多个参数:
let func = (value, num) => value * num;
//如果需要直接返回一个对象:
let func = (value, num) => ({total: value * num});
//与变量解构结合:
let func = ({value, num}) => ({total: value * num})
// 使用
var result = func({
value: 10,
num: 10
})
console.log(result); // {total: 100}
//自执行函数
(function(){
console.log(1)
})()
(() => {
console.log(1)
})()
扩展运算符
- 扩展运算符( ...a ) 允许一个表达式在期望多个参数(用于函数调用)或多个元素(用于数组字面量)或多个变量(用于解构赋值)的位置扩展。
下面我们直接来看这个东西的用处:
数组中使用:
//数组的合并
var a = [1, 2];
var b = [0, ...a, 3]
//数组的分割
var [a, ...b] = [0, 1, 2];
console.log(b) //[1, 2]
//数组的拷贝
var a = [1, 2];
var b = [...a];
对象中使用:
let { x, y, ...z } = {
x: 1,
y: 2,
a: 3,
b: 4
};
console.log(z) // {a: 3, b: 4}
以上简单的介绍了一下,都是经常用的。
symbol 属性
ES6 新增了第 7 种原始数据类型 Symbol,简单介绍一下它的使用方法及使用场景
⭐ 注意:通过 Symbol 方法创建值的时候不用使用 new 操作符,原因是通过 new 实例化的结果是一个 object 对象,而不是原始类型的 symbol,并且定义的是 独一无二的值。
创建Symbol类型得值:
const a = Symbol();
console.log(typeof s); // 'symbol'
//可以接收参数
const a = Symbol('111');
//独一无二的值
const a = Symbol('111');
const b = Symbol('111');
console.log(a === b); // false
常用方法:
Symbol.for
:检测上下文中是否已经存在使用该方法且相同参数创建的 symbol 值,如果存在则返回已经存在的值,如果不存在则新建。
const a1 = Symbol.for('111');
const q2 = Symbol.for('111');
console.log(a1 === q2); // true
Symbol.keyFor
:返回一个使用 Symbol.for 方法创建的 symbol 值的 key。
let A = Symbol.for('uid');
console.log(Symbol.keyFor(A)); // "uid"
let B = Symbol.for('uid');
console.log(Symbol.keyFor(B)); // "uid"
let C = Symbol('uid');
console.log(Symbol.keyFor(C)); // undefined
以上简单列举了几个,还有其他的可以看官方文档。🤭es6.ruanyifeng.com/?search=Sym…
迭代器 (Iterator)
初步认识迭代器
-
Iterator
是一种接口,目的是为不同的数据结构提供统一的数据访问机制。也可以理解为Iterator
接口主要为for of
服务的,供for...of
进行消费。 下面我们来看下迭代器的基本使用方法:const a = ['a','b','c'] let arr = aSymbol.iterator
//调用对象next方法 console.log(iterator.next()) //{value:'a',done:false} console.log(iterator.next()) //{value:'b',done:false} console.log(iterator.next()) //{value:'c',done:false} console.log(iterator.next()) //{value:undefined,done:true}
工作原理
- 创建一个指针对象,指向当前数据结构的 起始位置;
- 第一次调用对象的 next 方法,指针自动指向数据结构的 第一个成员;
- 接下来不断调用 next 方法,指针自动往后移动,直到指向最后一个成员;
- 每调用 next 方法返回一个包含 value 和 done 属性的对象;
实现迭代器
下面我们来做一个迭代器: ⬇ ⬇ ⬇
let obj = {
name: '中级三班',
array: [
'eqebj',
'daduo',
'grguy',
'rgrmo'
],
[Symbol.iterator]() {
let index = 0
let _this = this
let _done = false
return {
next: function () {
if (index < _this.array.length) {
const result = {value: _this.array[index], done: _done}
index++
return result
} else {
return {value: undefined, done: !_done}
}
}
}
}
}
//遍历这个对象,使用迭代器方式
for (let arr of obj) {
console.log(arr)
}
让我们来接着往下看,我们的 迭代器 (Interator) 还有什么用处 --- ---
解构赋值
let obj = {
name: '中级三班',
array: [
'eqebj',
'daduo',
'grguy',
'rgrmo'
],
[Symbol.iterator]() {
let index = 0
let _this = this
let _done = false
return {
next: function () {
if (index < _this.array.length) {
const result = {value: _this.array[index], done: _done}
index++
return result
} else {
return {value: undefined, done: !_done}
}
}
}
}
}
// 解构赋值
let [a, b, c] = obj
console.log(a) //eqebj
console.log(b) //daduo
console.log(c) //grguy
好啦,目前这几种比较常用,大家要记住哦!😊 ---偷偷告诉你们面试会加分的哦!(●ˇ∀ˇ●)
哎呦!突然还想起来个小知识点,for...in 和for...of 的区别:
- for in 循环 --- 键名
- for of 循环 --- 键值
生成器 (generator)
下面又到了我们的生成器了,我们继续来看 💪💪💪
概念:
- Generator 函数是 ES6 提供的一种 异步编程 解决方案,语法行为与传统函数完全不同;
- 语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态;
- Generator 函数除了 状态机,还是一个 遍历器对象生成函数;
- 可 暂停函数(惰性求值), yield 可暂停,next方法 可启动。每次返回的是yield后的表达式结果;
主要作用:解决回调地域问题 --- (异步)
下面我们来看使用方法:⬇ ⬇ ⬇
基本使用
Generator 函数是分段执行的,调用 next方法 函数内部逻辑开始执行,遇到 yield 表达式停止,返回{value: yield后的表达式结果/undefined, done: false/true}
,再次调用 next 方法会从上一次停止时的 yield 处开始,直到最后。
function* fun() {
yield '111';
yield '222';
return '333';
}
var h = fun();
h.next()// { value: '111', done: false }
h.next()// { value: '222', done: false }
h.next()// { value: '333', done: true }
h.next()// { value: undefined, done: true }
next 传递参数
yield 表达式本身没有返回值,或者说总是返回 undefined。next方法可以带一个参数,该参数就会被当作上一个 yield 表达式的返回值。
function* fun () {
console.log('开始执行')
let result = yield '111'
console.log(result)
yield '222'
}
let M = fun()
M.next()
M.next(11)
//执行结果:
// 开始执行
// 11
// {value: "222", done: false}
与迭代器的关系
由于 Generator 函数就是遍历器生成函数,因此可以把 Generator 赋值给对象的Symbol.iterator属性,从而使得该对象具有 Iterator 接口。
let obj = {
name: 'Cola',
age: 19
}
obj[Symbol.iterator] = function* fun() {
yield 1;
yield 2;
yield 3;
};
for (let i of obj) {
console.log(i) // 1 2 3
}
Generator 函数赋值给 Symbol.iterator 属性,从而使得obj对象具有了 Iterator 接口,可以被for of遍历了。
promise 异步
Promise的构造函数接收一个参数:函数,并且这个函数需要传入两个参数:
- resolve :异步操作执行成功后的回调函数
- reject:异步操作执行失败后的回调函数
认识 promise
let p = new Promise((resolve, reject) => {
//异步回调
setTimeout(() => {
console.log('执行完成');
resolve('我是成功!!');
}, 2000);
});
promise 是用来解决两个问题的:
- 回调地狱,代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象;
- promise 可以支持多个 并发 的请求,获取并发请求中的数据;
- 这个promise可以解决 异步 的问题,本身不能说promise是异步的;
promise封装ajax请求
我们每学习一点都要应用到 实战 中,接下来我们看 promise封装异步ajax。
const p = new Promise((resolve, reject) => {
//创建对象
let xml = new XMLHttpRequest()
// 初始化
xml.open('get', '/data/person.json')
// 发送请求
xml.send()
//绑定事件处理 回调函数
xml.onreadystatechange = function () {
if (xml.readyState === 4) {
if (xml.status >= 200 && xml.status < 300) { // 请求成功
resolve(xml.response)
} else { // 请求失败
reject(xml.status)
}
}
}
})
p.then(function (value) {
console.log(value)
}, function (reason) {
console.error(reason)
})
以上就是我们经常用的异步的发送请求,大家有空都联系一下。。。
then 的链式操作
当 多个请求 要一起请求或者一次性 请求多个文件 的时候,用普通的方法很可能或造成回调地狱,因此,就可以用链式操作来防止一次性请求多个文件 回调地狱 的发生。
下面我们来看这个例子:
const fs = require('fs')
const p = new Promise((resolve, reject) => {
fs.readFile('./data1.md', (err, data) => {
if (true) resolve(data)
else reject(err)
})
})
p.then(value => {
return new Promise((resolve, reject) => {
fs.readFile('./data2.md', (err, data) => {
if (true) resolve([value, data])
else reject(err)
})
})
}, reason => {
console.error(reason)
}).then(value => {
return new Promise((resolve, reject) => {
fs.readFile('./data3.md', (err, data) => {
if (true) {
value.push(data)
resolve(value)
} else {
reject(err)
}
})
})
}, reason => {
console.error(reason)
}).then(value => {
console.log(value.toString())
}, reason => {
console.error(reason)
})
这个例子用到了 nodejs配置 ,应用的时候可以试着配一下!!! 这部分我们就先介绍这几点,还有 promise.all,promise.race,promise.any,promise.try 等其他的函数,没事的时候可以看下官方文档:es6.ruanyifeng.com/#docs/promi…
接下来我们继续看:⬇ ⬇ ⬇
集合Map和Set
set
Set
本身是一个构造函数,用来生成 Set
数据结构。Set
函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。Set
对象允许你存储任何类型的值,无论是原始值或者是对象引用。它类似于数组,但是成员的值都是唯一的,没有重复的值。
add,delete,has
-
add(value)
:添加某个值,返回Set
结构本身(可链式调用)。 -
delete(value)
:删除某个值,删除成功返回true
,否则返回false
。 -
has(value)
:返回一个布尔值,表示该值是否为Set
的成员。 -
clear()
:清除所有成员,没有返回值。let set = new Set() set.add('123') set.add('42') set.add('424') console.log(set) // { '123', '42', '424' } set.delete('123') console.log(set) //{ '42', '424' } console.log(set.has('42')) // true
-
keys()
:返回键名的遍历器; -
values()
:返回键值的遍历器; -
entries()
:返回键值对的遍历器; -
forEach()
:使用回调函数遍历每个成员;
由于 Set
结构没有键名,只有键值(或者说键名和键值是同一个值),所以 keys
方法和 values
方法的行为完全一致。
接下来我们来简单的实现几个例子:
去重,交集,并集,差集
let s = ['123', '432', '424', '424', '123', '432']
let s1 = ['313', '442', '123', '424']
// 去重
let result = new Set(s)
console.log(Array.from(result)) //[ '123', '432', '424' ]
// 交集
let array1 = Array.from(new Set(s)).filter(index => Array.from(new Set(s1)).indexOf(index) > -1)
console.log(array1) //[ '123', '424' ]
// 并集
let array = Array.from(new Set([...new Set(s), ...new Set(s1)]))
console.log(array) // [ '123', '432', '424', '313', '442' ]
//差集
let array2 = Array.from(new Set(s)).filter(index => Array.from(new Set(s1)).indexOf(index) === -1)
console.log(array2) //[ '432' ]
以上就是set的常用方法,下面我们继续来看。
map
Map
对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。构造函数Map
可以接受一个数组作为参数。
set,add,delete,get,size,clean
-
size
:返回Map对象中所包含的键值对个数 -
add
:向Map中添加新元素; -
set(key, val)
: 向Map中添加新元素; -
get(key)
: 通过键值查找特定的数值并返回; -
has(key)
: 判断Map对象中是否有Key所对应的值,有返回true,否则返回false; -
delete(key)
: 通过键值从Map中移除对应的数据; -
clear()
: 将这个Map中的所有元素删除;const m1 = new Map([['a', 111], ['b', 222]]) console.log(m1) // {"a" => 111, "b" => 222} m1.get('a') // 111 const m2 = new Map([['c', 3]]) const m3 = new Map(m2) m3.get('c') // 3 m3.has('c') // true m3.set('d', 555) m3.get('d') // 555
遍历方法
-
keys()
:返回键名的遍历器 -
values()
:返回键值的遍历器 -
entries()
:返回键值对的遍历器 -
forEach()
:使用回调函数遍历每个成员const map = new Map([['a', 1], ['b', 2]]) for (let key of map.keys()) { console.log(key) // 'a' 'b' } for (let value of map.values()) { console.log(value) //1 2 } for (let item of map.entries()) { console.log(item) // ['a', 1] ['b', 2] } // for...of...遍历map等同于使用map.entries() for (let [key, value] of map) { console.log(key, value) // 'a' 1 'b' 2 }
es7 新特性
inclued() 函数
inclued()
: 函数用来判断一个数组是否包含一个指定的值,如果包含则返回 true
,否则返回false
。
let arr = ['123','234','345']
let x = '123'
arr.includes(x) //true
指数操作符
在ES7中引入了指数运算符**
,**
具有与Math.pow(..)
等效的计算结果。
console.log(Math.pow(2, 10)); // 输出1024
console.log(2**10);// 输出1024
es8 新特性
async...await
ES2018引入异步迭代器(asynchronous iterators),这就像常规迭代器,除了next()
方法返回一个Promise。因此await
可以和for...of
循环一起使用,以串行的方式运行异步操作。例如:
async function process(array) {
for await (let i of array) {
doSomething(i);
}
}
keys,values,entries,getOwnPropertyDescriptors
Object.values()
Object.values()
是一个与Object.keys()
类似的新函数,但返回的是Object自身属性的所有值,不包括继承的值;Object.entries()
函数返回一个给定对象自身可枚举属性的键值对的数组;Object.getOwnPropertyDescriptors()
函数用来获取一个对象的所有自身属性的描述符,如果没有任何自身属性,则返回空对象。
这篇的知识点不少啦🤭,看到这里大家应该都累了,注意休息哈,下篇随后就出来,加油哦!努力的程序小猿们。