一、四种作用域
- 全局作用域
- 函数作用域
- 块状作用域 ,配合 let 和 const 使用
- 动态作用域
1.全局作用域
- ==var 声明的变量为全局变量==不能被删除
- 没有 var 定义的变量是作为 window 的属性定义的,因为 window 为全局对象 所以看上去它也具有全局属性,这是和window有关而不是因为它本身为一个 全局变量
- 在函数内部 没有用 var 定义的变量,它不是具备 函数作用域 而是具备全局作用域的
后两个都不是 ==全局变量== 都是作为 window的属性 存在的
2.函数作用域
- 函数作用域 = 局部作用域
- 在函数内部定义的变量,就是局部作用域。函数作用域内,对外是封闭的,从外层的作用域无法直接访问函数内部的作用域!
function bar() {
var testValue = 'inner';
}
console.log(testValue); // 报错:ReferenceError: testValue is not defined
如果想读取函数内的变量,必须借助 return 或者闭包。
function bar(value) {
var testValue = 'inner';
return testValue + value;
}
console.log(bar('fun')); // "innerfun"
这是借助 return 的方式,下面是闭包的方式:
function bar(value) {
var testValue = 'inner';
var rusult = testValue + value;
function innser() {
return rusult;
};
return innser();
}
console.log(bar('fun')); // "innerfun"
如果一个==变量 或者 其它表达式 不在“当前得作用域”==,那么JavaScript机制会继续==沿着作用域链向上查找==直到找到全局作用域,通常是指沿着链式的作用域查找,而不能从父作用域引用子作用域中的变量和引用-----作用域链
3.块状作用域 (let 和 const)
==注意: var 定义的变量会变量提升== 而let和const不会,所以var不能进行块状作用域的定义,会让变量进行提升
4.动态作用域
- this 具有动态指向 通过bind的使用
二、let && const
let
- let 声明的变量拥有块级作用域
- let 声明的全局变量不是全局对象的属性
这就意味着,你不可以通过 window.变量名 的方式访问这些变量,而 var 声明的全局变量是 window 的属性,是可以通过 window.变量名 的方式访问的。
- let不能重定义变量
- let声明的变量不会进行变量提升
const 声明的特点
- 只能赋值一次
- 初始化时一定要赋值
- 块状作用域
- const 声明的全局变量不是全局对象的属性
- 不能重复定义
- 不会变量提升
三、数组 Array 遍历方法
ES5 中遍历数组的方法
- for 循环
const arr = [1, 2, 3, 4, 5]
for (let i = 0; i < arr.length; i++) {
console.log(arr[i])
}
- forEach
不支持 break、continue
// forEach 不允许使用 break continue
arr.forEach(function (item) {
console.log(item)
})
- every (不支持continue 和 break 但可以实现类似效果)
使用 every 遍历就可以做到 break 那样的效果,简单的说 return false 等同于 break,return true 等同于 continue。如果不写,默认是 return false。
arr.every(function (item) {
if (item === 2) {
return false // 达到break的效果
}
console.log(item)
return true
})
- for in (数组也是一个对象) 其中的 ==index 为字符串== for…in 确实可以遍历数组,而且还支持 continue、break等功能,但是它真的没有瑕疵吗?==如果 array 有自定义属性,你发现也会被遍历出来(显然不合理)== 这是因为 for…in 是为遍历对象创造的({a:1,b:2}),不是为数组设计的。
arr.a = 8
for (let index in arr) {
console.log(index, arr[index])
}
// 注意:for…in代码块中不能有 return,不然会抛出异常。
ES6中遍历数组的方法
for of
==for..of适用遍历数/数组对象/字符串/map/set等拥有迭代器对象的集合.但是不能遍历对象,因为没有迭代器对象.==
for (variable of iterable) {
}
// 这个伪代码,of 后面是 iterable 既不是 for 循环规定的 array,也不是 for…in 规定的 Object,而是 iterable。
// 即 for…of 遍历的是一切可遍历的元素(数组、对象、集合)等,不要小瞧这个功能,因为在 ES6 中允许开发者 自定义遍历,换句话说任何数据结构都可以自定义一个遍历,这个遍历是不能被 for、for…in 理解和实现的
四、伪数组转化为数组
数组是开发中经常用到的数据结构,它非常好用。在 JavaScript 的世界里有些对象被理解为数组,然而却不能使用数组的原生 API,比如==函数中的 arguments、DOM中的 NodeList等==当然,还有一些可遍历的对象,看上去都像数组却不能直接使用数组的 API,因为它们是==伪数组Array-Like==++要想对这些对象使用数组的 API 就要想办法把它们转化为数组++
ES5中伪数组转换为数组
// call() 作用有两个,即调用函数并改变函数内部this指向
- 调用函数
调用 调用call()方法的这个函数,例如:fn.call()可以粗略的理解为fn()(暂时先不考虑参数的情况下)。
- 改变函数内部this指向
将函数fn内部的this指向call()方法的第一个对象参数,这么做的意义是可以使这个对象把fn这个函数(或者说方法)据为己有。
// [].slice.call(arrayLike)将经历这些步骤:让arrayLike拥有数组的slice方法,slice方法被调用,其作用目标为arrayLike,由于没有传入其他参数,slice()默认返回所有下标的元素并返回新数组
let args = [].slice.call(arguments) // collection
let imgs = [].slice.call(document.querySelectorAll('img')) // Nodelist
ES6中伪数组的转换方法
Array.from
// Array.prototype.from
let args = Array.from(arguments)
let imgs = Array.from(document.querySelectorAll('img'))
注意: ==伪数组具备两个特征,1. 按索引方式储存数据 2. 具有length属性==;如:
let arrLike = {
0: ‘a’,
1: ‘b’,
2: ‘c’,
length: 3
}
语法:Array.from(arrayLike[, mapFn[, thisArg]])
| 参数 | 含义 | 必选 |
|---|---|---|
| arrayLike | 想要转换成数组的伪数组对象或可迭代对象 | Y |
| mapFn | 如果指定了该参数,新数组中的每个元素会执行该回调函数 | N |
| thisArg | 可选参数,执行回调函数 mapFn 时 this 对象 | N |
看了这几个参数至少能看到 ==Array.from 还具备 map(遍历) 的功能==,比如我们想初始化一个长度为 5 的数组,每个数组元素默认为 1
Array.from({ length: 5 }, function () { return 1 })
// { length: 5 } 为一个伪数组(1.具备length属性,2.其储存值为空,相对于按索引方式存储)
五、生成一个新数组
ES5 生成一个新数组
let array = Array(5)
let arr = ['', ''] // 不能指定生成新数组的长度
ES6 生成一个新数组
- Array.from() 将伪数组转换为一个数组,也是生成一个新数组的方法
- Array.of() Array.of() 方法创建一个==具有可变数量参数==的新数组实例,而不考虑参数的数量或类型。
let array = Array.of(1, 2, 3, 4, 5)
console.log(array)
Array.of() 和 Array 构造函数之间的区别在于处理整数参数:Array.of(7) 创建一个具有单个元素 7 的数组,而 ==Array(7) 创建一个长度为7的空数组==(注意:这是指一个有7个空位(empty)的数组,而不是由7个undefined组成的数组)。
Array.of(7); // [7]
Array.of(1, 2, 3); // [1, 2, 3]
Array(7); // [ , , , , , , ]
Array(1, 2, 3); // [1, 2, 3]
- Array.fill() 用于 快速所有元素初始化 和 替换数组的某一块区域 fill() 方法用一个固定值填充一个数组中从++起始索引到终止索引内的全部元素。不包括终止索引。++
语法:arr.fill(value[, start[, end]])
let array = [1, 2, 3, 4]
array.fill(0, 1, 2)
// [1,0,3,4]
// 这个操作是将 array 数组的第二个元素(索引为1)到第三个元素(索引为2)内的数填充为 0,不包括第三个元素,所以结果是 [1,0,3,4]
六、数组中元素的查找
ES5
- arr.filter() 会把满足条件的元素都筛选出来,并返回一个新的数组
let array = Array.of(1, 2, 3, 4, 5)
let find = array.filter(function (item) {
return item % 2 === 0
})
console.log(find) // [2, 4]
- arr.indexOf() indexOf()方法返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。
ES6
- arr.find()
find() 方法返回数组中满足 函数 的第一个元素的值,否则返回 undefined。 关注的是有和没有,不是关注所有的
let array = [5, 12, 8, 130, 44];
let found = array.find(function(element) {
return element > 10;
});
console.log(found);
// 12
- arr.findIndex() findIndex()方法返回数组中满足提供的测试函数的==第一个元素的索引==。否则返回-1。其实这个和 find() 是成对的,不同的是它返回的是索引而不是值。
Class
Javascript是一种基于对象(object-based)的语言,你遇到的所有东西几乎都是对象。但是,它又不是一种真正的面向对象编程(OOP)语言,因为它的语法中没有class(类)。 这句话放在 ES5 可以说不为过,然而到了 ES6 这么说就已经不严谨了。因为 ES6 中已经有了专属的 class 语法了。
ES5 中如何声明一个类 (用函数的方法)
let Animal = function (type) {
this.type = type
this.walk = function () {
console.log(`I am walking`)
}
}
let dog = new Animal('dog')
let monkey = new Animal('monkey')
在上述代码中,我们定义了一个叫 Animal 的类,类中声明了一个属性 type、一个方法 walk;然后通过 new Animal 这个类生成实例,完成了类的定义和实例化。 ==但是,这样写其实是不对的(把共用的东西都放到函数体里),正确的写法应该是把函数当成构造函数去用,函数里放每个实例对象都有却不同的,大家同有的东西放到原型链上==
// ES5 如何声明一个 “类”
let Animal = function (type) {
this.type = type
} // 每个实例对象都有一个属于自己的 type
Animal.prototype.eat = function () {
console.log('hello i am eat')
} // 大家同有的 eat 方法 放到原型链上
let dog = new Animal('dog')
let cat = new Animal('cat')
console.log(dog)
console.log(cat)
// 修改实例上类的方法
cat.constructor.prototype.eat = function () {
console.log('hhhhhhh')
}
dog.eat()
cat.eat()
ES6中如何声明一个类
class Animal {
constructor (type) {
this.type = type
}
eat () {
console.log('hello i am eat')
}
}
let dog = new Animal('dog')
let cat = new Animal('cat')
console.log(dog)
console.log(cat)
console.log(typeof (Animal)) // function
可以发现 ==class 的类型还是 function== 可以看出在 Animal.prototype 对象上有两个方法,一个是构造函数(constructor)、一个是自定义的方法(eat)。这是不是和 ES5 的第二种写法一模一样,所以得出一个结论:==class 的方式是 function 方式的语法糖。==
二、ES6中 读写属性(set/get)
通过 get/set 来给类定一个属性
应用场景:
- 一个属性是个只读的
- 我们真的需要设置一个私有属性(闭包),然后通过一定的规则来限制对它的修改,利用 set/get就可以轻松实现。
// ES6 读写属性
let _age = 4 // 私有属性(闭包)
class Animal {
constructor (type) {
this.type = type
}
get age () {
return _age
}
set age (age) {
if (age > 4 && age < 7) {
_age = age
}
}
eat () {
console.log('hello i am eat')
}
}
let dog = new Animal('dog')
console.log(dog.age)
dog.age = 5
console.log(dog.age)
console.log(dog._age)
三、操作一个方法(实例对象的方法 和 静态方法)
ES5中操作一个方法
let Animal = function (type) {
this.type = type
}
Animal.prototype.eat = function () {
Animal.walk()
console.log('hello i am eat')
} // 类的实例对象的方法
Animal.walk = function () {
console.log('i am walking')
} // 类的静态方法---挂载到Animal上
let dog = new Animal('dog')
console.log(dog)
dog.eat()
ES6中操作一个方法(实例对象的方法 和 静态方法)
class Animal {
constructor (type) {
this.type = type
}
eat () {
Animal.walk()
console.log('i am eat')
}
static walk () {
console.log('i am walking')
}
}
let dog = new Animal('dog')
console.log(dog)
dog.eat()
Animal.walk()
注意:实例对象的方法 和 静态方法的 定义 和 使用
四、class 继承另一个类
ES5中如何继承一个类
let Animal = function (type) {
this.type = type
}
Animal.prototype.eat = function () {
Animal.walk()
console.log('hello i am eat')
} // 类的实例对象的方法
Animal.walk = function () {
console.log('i am walking')
} // 类的静态方法
let Dog = function () {
// 第一步 初始化父类的构造函数
// 这一步将 父类的this指向替换到子类中,将父类的构造函数里的所有东西都继承到子类里,
// 但是父类原型链上的方法并没有继承下来,所有需要第二部进行原型链的重新指向
Animal.call(this, 'dog')
this.run = function () {
console.log('i am running')
}
}
// 第二步: 将子类的原型链指向父类
Dog.prototype = Animal.prototype
let dog = new Dog('二哈')
console.log(dog)
dog.eat()
dog.run()
ES6 如何继承一个类
class Animal {
constructor (type) {
this.type = type
}
eat () {
Animal.walk()
console.log('i am eat')
}
static walk () {
console.log('i am walking')
}
}
class Dog extends Animal {
constructor (type) {
super(type) // Useless constructor 不写constructor时,会默认会执行这些
this.age = 3
}
}
let dog = new Dog('哈士奇')
console.log(dog)
dog.eat()
Animal.walk()
Function Update
一、如何处理函数参数的默认值
ES5 中怎样处理函数参数的默认值
// ES5
function f (x, y, z) {
if (y === undefined) {
y = 7
}
if (z === undefined) {
z = 42
}
return x + y + z
}
console.log(f(1))
ES6 中怎样处理函数参数的默认值
// ES6
function f (x, y = 7, z = 42) {
return x + y + z
}
console.log(f(1))
console.log(f(1, undefined, 43))
函数参数是从左到右解析,如果没有默认值会被解析成 undefined,所以最好把有默认值的参数写在后面
在ES6中我们不仅可以给参数默认赋值具体的数值,==同时参数赋值支持参数的逻辑运算进行赋值==,如下段代码所示:
// ==参数可以是前面参数的 表达式==
function f (x, y = 7, z = x + y) {
return z * 0.5
}
console.log(f(1, 7))// 4
扩展:
在函数体内,有时候需要==判断函数有几个参数==,一共有2个办法。在 ES5 中可以在函数体内使用 arguments 来判断。
function test (a, b = 1, c) {
console.log(arguments.length)
console.log(Array.from(arguments)) ['a', 'b']
}
test('a', 'b') //2
然而在 ==ES6== 中不能再使用 arguments 来判断了,但可以==借助 Function.length 来判断。==
function test (a, b = 1, c) {
console.log(test.length)
}
test('a', 'b') // 1
细心的同学发现 ==Function.length 结果和 arguments 的结果不同==! 没错,++Function.length ==是统计第一个默认参数前面的变量数:==++
function test (a = 2, b = 1, c) {
console.log(test.length)
}
test('a', 'b') // 第一个默认参数为a,其前面没有其他的变量,所以打印为0
二、如何处理不确定参数的问题
ES5中怎么处理不确定参数的问题
ES5 中利用 arguments 实现获取所有的参数
function sum () {
let sum = 0
// ES5 写法
Array.prototype.forEach.call(arguments, function (item) {
sum += item * 1
})
// ES6 写法,相比于ES5 可以进行伪数组到数组的转换
// let arr = Array.from(arguments)
// arr.forEach(function (item, index) {
// sum += item * 1
// })
return sum
}
console.log(sum(1, 23, 45, 88))
ES6中怎么处理不确定参数的问题
function sum (...nums) {
// Rest parameter
console.log(nums)
let sum = 0
nums.forEach(function (item) {
sum += item
})
return sum
}
console.log(sum(12, 22, 3))
==当然,Rest Parameter 也可以和其他参数一起来用,比如:==
function sum (base, ...nums) {
let num = base
nums.forEach(function (item) {
num += item * 1
})
return num
}
console.log(sum(30, 1, 2, 3)) // 36
console.log(sum(30, 1, 2, 3, 4)) // 40
==arguments 不是数组==,所以不能直接使用数组的原生 API 如 forEach,而 ==Rest Parameter 是数组==,可以直接使用数组的原生 API。
三、Rest 与 speard (二者相反,rest会把参数装到一个数组, speard会把数组中数据打散,传到函数中)
function sum (x = 1, y = 3, z = 9) {
return x + y + z
}
let data = [4, 5, 6] // 后端返回的数据
console.log(sum(data[0], data[1], data[2])) // 憨憨做法
console.log(sum.apply(this, data)) // ES5做法
// 调用apply方法的时候,第一个参数是对象(this), 第二个参数是一个数组集合,将这个数组集合赋值给 arguments
console.log(sum(...data)) // ES6做法 speard
==apply 与 call 的区别:==
四、ES6中的 箭头函数
- 箭头后面是个表达式,表达式的值就作为这个函数的返回值、
- 返回值是字面量对象 如果返回值是字面量对象,一定要用小括号包起来
let person = (name) => ({
age: 20,
addr: 'Beijing City'
})
- 其他情况就要中规中矩的写好
() => {
return {
.......
}
}
==关于 this 的拓展==
- 总结: ==作为事件处理函数==,es5的this指向触发当前事件的对象元素,es6箭头函数指向window
- ==在对象方法中的this== ES5中谁在调用这个函数,this就指向谁 箭头函数中 this 的指向是 在写这个代码的时候this的指向(即箭头函数中对 this 的处理是定义时) es6函数体内的this对象是【使用时】所在的对象,而不是【定义时】所在的对象 es6函数体内的this对象是【定义时】所在的对象,而不是【使用时】所在的对象
- 在window中定义的方法,es5和es6的this都一样指向window
注意: 默认指向定义它时,所处上下文的对象的this指向。即ES6箭头函数里this的指向就是上下文里对象this指向,偶尔没有上下文对象,this就指向window 上下文:即是找上下文的函数对象,就默认为window
Object
一、关于对象属性简写 的问题
1.在 ES6 之前 Object 的属性必须是 key-value 形式,如下:
var x = 0, y = 0;
obj = { x: x, y: y };
在 ES6 之后是可以用简写的形式来表达:
var x = 0, y = 0
obj = { x, y }
2.在 ES6 之前 Object 的 ++key 在定义时必须是字符串++,如果想增加“动态”的 key,必须是先计算出 key,利用 ==object[key] = value== 的方式来修改; 但是:在 ES6 之后可以直接用变量或者表达式来定义 key。
let x = 1 let y = 2 let z = 5
let obj = {
x,
[z + y]: 12 // ES6
}
obj.[z + y] = 12 // ES5
3.Method Properties 从 ES6 开始对象内的方法可以简写,包括常规函数和异步函数。
let x = 1
let y = 2
let z = 5
let obj = {
x,
[z + y]: 12,
hello: function () { // ES5 只支持常规的函数
console.log('hello')
},
hello () { // ES6 支持常规的函数和异步函数。
console.log('hello')
},
* hello () { // ES6 支持常规的函数和异步函数。
console.log('i am 异步函数')
}
}
console.log(obj)
二、Set 和 Map
在 JavaScript 里通常使用 Array 或 Object 来存储数据。但是在频繁操作数据的过程中查找或者统计并需要手动来实现,并不能简单的直接使用。 比如如何保证 Array 是去重的,如何统计 Object 的数据总数等,必须自己去手动实现类似的需求,不是很方便。 在 ES6 中为了解决上述痛点,新增了数据结构 Set 和 Map,它们分别对应传统数据结构的“集合”和“字典”。
Set 数据结构
1.生成 Set 实例
let s = new Set()
可以定义一个空的 Set 实例,也可以在实例化的同时传入默认的数据。
let s = new Set([1, 2, 3, 4])
==初始化的参数必须是可遍历的==,可以是数组,对象 或者 自定义遍历的数据结构。 2.添加数据
s.add('hello')
s.add('goodbye')
或者
s.add('hello').add('goodbye')
[!NOTE] 敲黑板了,==Set 数据结构不允许数据重复==,所以添加重复的数据是无效的
3.删除数据 删除数据分两种,一种是删除指定的数据,一种是删除全部数据。
// 删除指定数据
s.delete('hello') // true
// 删除全部数据
s.clear()
4.统计数据(has() size) Set 可以快速进行统计数据,如==数据是否存在、数据的总数。==
// 判断是否包含数据项,返回 true 或 false
s.has('hello') // true
// 计算数据项总数(size 是一个属性,不是方法)
s.size // 2
5.查询数据
- keys():返回 键名 的遍历器
- values():返回 键值 的遍历器
- entries():返回 键值对 的遍历器
- forEach():使用回调函数遍历每个成员
- for…of:可以直接遍历每个成员
console.log(s.keys()); // SetIterator {"hello", "goodbye"}
console.log(s.values()); // SetIterator {"hello", "goodbye"}
console.log(s.entries()); // SetIterator {"hello" => "hello", "goodbye" => "goodbye"}
s.forEach(item => {
console.log(item)
})
for (const item of s) {
console.log(item)
}
6.修改数据 要先删除 再add
Map 数据结构(==Map 里的 key 可以是任意值==)
Map 是用来实现字典的功能(Object 键值对) 一个Object的键只能是字符串或者 Symbols,但一个 Map 的键可以是任意值,包括函数、对象、基本类型。
1.实例化
let map = new Map([iterable])
let map = new Map([[1, 2], [2, 3], [3, 6]])
==Iterable 可以是一个 ++数组++ 或者 ++其他 iterable 对象(可遍历对象)++==,其元素为键值对(两个元素的数组,例如: [[ 1, ‘one’ ],[ 2, ‘two’ ]])。 每个键值对都会添加到新的 Map。null 会被当做 undefined。
2.添加数据(==Map 里的 key 可以是任意值==)
let keyObj = {}
let keyFunc = function () {}
let keyString = 'a string'
// 添加键
map.set(keyString, "和键'a string'关联的值")
map.set(keyObj, '和键keyObj关联的值123')
map.set(keyFunc, '和键keyFunc关联的值')
3.删除数据
// 删除指定的数据
map.delete(keyObj)
// 删除所有数据
map.clear()
4.统计数据
console.log(map.size) // 统计所有 key-value 的总数
// 判断是否有 key-value
console.log(map.has(keyObj)) // true
5.查询数据
- get() 方法返回某个 Map 对象中的一个指定元素
- keys() 返回一个新的 Iterator 对象。它包含按照顺序插入 Map 对象中每个元素的 key 值
- values() 方法返回一个新的 Iterator 对象。它包含按顺序插入Map对象中每个元素的 value 值
- entries() 方法返回一个新的包含 [key, value] 对的 Iterator 对象,返回的迭代器的迭代顺序与 Map 对象的插入顺序相同
- forEach() 方法将会以插入顺序对 Map 对象中的每一个键值对执行一次参数中提供的回调函数
- for…of 可以直接遍历每个成员
console.log(map.get(keyObj)) // 和键keyObj关联的值123
for ([key, value] of map) {
console.log(key, value)
}
三、对象的复制
ES5中对象的kaob
let target = {}
let source = {a: 4, b: 'hhh'}
console.log('source:', source)
for (const key in source) {
target[key] = source[key]
}
console.log('target:', target)
ES6中对象如何进行拷贝
基本语法 Object.assign(target, …sources)
从语法上可以看出源对象的个数是不限制的(零个或多个),如果是零个直接返回目的对象,如果是多个相同属性的会被后边的源对象的属相覆盖。
let target2 = {}
Object.assign(target2, source)
console.log(target2)
// source 为多个对象
const first = {
name: 'Bob'
}
const last = {
lastName: 'Smith'
}
let person = Object.assign(first, last)
console.log(person)/*{ name: 'Bob', lastName: 'Smith' }*/
Template
- 字符串很长要换行 对于多行的字符串,之前是这样处理 ES5
console.log('string text line1\n' + 'string text line2')
// "string text line 1
// string text line 2"
现在可以这样做了 ES6
console.log(`string text line1
string text line2`)
// "string text line 1
// string text line 2"
完全不需要 \n 来参与。 2.字符串中有变量或者表达式 ES5 字符串拼接:
var a = 20
var b = 10
var c = 'JavaScript'
var str = 'My age is ' + (a + b) + ' and I love ' + c
console.log(str)
ES6 模板字符串
let str = `your age is ${a + b} i love ${c}`
console.log(str)
在这里你可以任意==插入变量或者表达式==,只要用 ${} 包起来就好。
3.字符串中有逻辑运算 ES5 使用 字符串拼接+逻辑判断
var retailPrice = 20
var wholesalePrice = 16
var type = 'retail'
var showTxt = ''
if (type === 'retail') {
showTxt += '您此次的购买单价是:' + retailPrice
} else {
showTxt += '您此次的批发价是:' + wholesalePrice
}
ES6 用函数充当 ==模板引擎 Tag 函数==
你输入一个字符串 这个字符串会和一个函数作关联, 这个函数就是用来处理你 字符串逻辑的
function Price (strings, type) {
let str = strings[0]
const retailPrice = 20
const wholesalePrice = 10
let showText
if (type === 'retail') {
showText = '购买单价是:' + retailPrice
} else {
showText = '购买批发价是:' + wholesalePrice
}
return `${str}${showText}`
}
let showText = Price`您此次的${'retail'}` // `您此次的${'retail'}` 为参数
// 函数 return 的结果为最终的结果
console.log(showText)
strings 参数指的是 Tag 函数后面==被变量分割开的字符串集合==,type 参数是对应第一个变量,Tag 函数可以有==多个 type 类似的参数==
解构赋值
一、Array 的解构赋值
1.简单的数组赋值
let [firstName, surname] = ['Ilya', 'Kantor']
console.log(firstName) // Ilya
console.log(surname) // Kantor
let arr = ['hello', 'wrold']
let [firstName] = arr // 解构复制
console.log(firstName) // hello
2.跳过不关心的元素 进行赋值
如果想忽略数组的某个元素对变量进行赋值,可以使用逗号来处理。
// second element is not needed
let [firstName, , title] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];
console.log( title ); // Consul
3.==赋值元素可以是任意可遍历的对象== 赋值的元素不仅是数组,它可以是任意可遍历的对象
let [a, b, c] = "abc"; // ["a", "b", "c"]
let [one, two, three] = new Set([1, 2, 3]);
4.不仅可以 ==给简单的变量赋值==, 还可以 ==给对象的属性赋值==
注意: 给对象的属性赋值时, 由于对象已经存在,赋值时 不能加 let
let users = {};
[users.name, users.surname] = 'Ilya Kantor'.split(' ');
console.log(users.name) // Ilya
5.如何在循环体中做 变量赋值([key, value])
解构赋值在循环体中的应用,可以配合 entries 使用(改成键值对得形式)。
let user = {
name: 's',
surname: 'a'
}
for (const [key, value] of Object.entries(user)) {
console.log(key, value)
}
6.数组解构赋值如何处理 rest参数得问题
let [name1, name2, ...rest] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];
console.log(name1); // Julius
console.log(name2); // Caesar
// Note that type of `rest` is Array.
console.log(rest[0]); // Consul
console.log(rest[1]); // of the Roman Republic
console.log(rest.length); // 2
注意:我们可以使用 rest 来接受赋值数组的剩余元素,不过要确保这个 rest 参数是放在被赋值变量的最后一个位置上。
7.解构赋值 可以设置默认值 如果数组的内容少于变量的个数,并不会报错,没有分配到内容的变量会是 undefined。
let [firstName, surname] = [];
console.log(firstName); // undefined
console.log(surname); // undefined
当然你也可以给变量赋予默认值,防止 undefined 的情况出现:
let [name = "Guest", surname = "Anonymous"] = ["Julius"];
console.log(name); // Julius (from array)
console.log(surname); // Anonymous (default used)
二、Object 得解构赋值
1.基本用法
let {var1, var2} = {var1:…, var2…}
2.指定默认值
let options = {
title: "Menu"
};
let {width = 100, height = 200, title} = options;
alert(title); // Menu
alert(width); // 100
alert(height); // 200
3.对象解构赋值如何处理 ++rest 运算符++
let options = {
title: "Menu",
height: 200,
width: 100
};
let {title, ...rest} = options;
// now title="Menu", rest={height: 200, width: 100}
alert(rest.height); // 200
alert(rest.width); // 100
4.==嵌套对象== 的使用
如果一个 Array 或者 Object 比较复杂,它嵌套了 Array 或者 Object,那只要被赋值的结构和右侧赋值的元素的结构一致就好了 (左右结构一致)
let options = {
size: {
width: 100,
height: 200
},
items: ["Cake", "Donut"],
extra: true // something extra that we will not destruct
};
// destructuring assignment on multiple lines for clarity
let {
size: { // put size here
width,
height
},
items: [item1, item2], // assign items here
title = "Menu" // not present in the object (default value is used)
} = options;
alert(title); // Menu
alert(width); // 100
alert(height); // 200
alert(item1); // Cake
alert(item2); // Donut
Promise
一、ES5 中异步操作 与 回调的关系
// 异步 读取文件
const fs = require('fs');
const path = require('path');
// callback 方式 获取一个文件的内容
function getFileContent(fileName, callback) {
const fullFileName = path.resolve(__dirname, './files', fileName);
fs.readFile(fullFileName, (err, data) => {
if (err) {
console.error(err);
return
}
callback(
JSON.parse(data.toString()) // 将字符串转换为 json对象的形式
// JSON.parse(data.toString()) === aData
)
})
}
// 测试
getFileContent('a.json', aData => {
console.log('a.data', aData);
getFileContent(aData.next, bData => {
console.log('b.data', bData);
getFileContent(bData.next, cData => {
console.log('c.data', cData)
})
})
})
二、ES6 中 Promise的使用
Promise 就是为了解决“回调地狱”问题的,它可以将异步操作的处理变得很优雅。
1.基本语法
new Promise( function(resolve, reject) {…} );
- new Promise(fn) 返回一个Promise 对象
- 在fn 中指定异步等处理
- 处理结果正常的话,调用resolve(处理结果值)
- 处理结果错误的话,调用reject(Error对象)
2.状态转换
在这里必须说明下 ==Promise 内部是有状态的(pending、fulfilled、rejected)==,Promise 对象根据状态来确定执行哪个方法。Promise 在实例化的时候状态是默认 pending 的,当异步操作是完成的,状态会被修改为 fulfilled,如果异步操作遇到异常,状态会被修改为 rejected
状态转化是单向的,不可逆转,已经确定的状态(fulfilled/rejected)无法转回初始状态(pending),而且只能是从 pending 到 fulfilled 或者 rejected
==3.Promise.prototype.then()== 仔细阅读最后的 代码
基本语法: promise.then(onFulfilled, onRejected);
promise.then(onFulfilled, onRejected) onFulfilled对应 resolve方法 onRejected对应 reject方法 onFulfilled, onRejected 这两个参数 都是函数类型 如果你传入的参数 是非函数,那么.then()将会返回一个 空的 promise 对象
var promise = new Promise(function (resolve, reject) {
resolve('传递给then的值')
})
promise.then(function (value) { // resolve 的 参数传给 value
console.log(value)
}, function (error) { // reject 的参数传给 error
console.error(error)
})
这段代码创建一个 Promise 对象,定义了处理 onFulfilled 和 onRejected 的函数 这个 Promise 对象会在变为 resolve 或者 reject 的时候分别调用相应注册的回调函数。
- 返回一个正常值的时候,这个值会传递给 Promise 对象的 onFulfilled 方法。
- 产生异常的时候,这个值则会传递给 Promise 对象的 onRejected 方法。
==4.Promise.resolve()==
一般情况下我们都会使用 new Promise() 来创建 Promise 对象,但是除此之外我们也可以使用其他方法。
在这里,我们将会学习如何使用 Promise.resolve 和 Promise.reject 这两个方法。
==静态方法 Promise.resolve(value) 可以认为是 new Promise() 方法的快捷方式==
new Promise(function (resolve) {
resolve(42)
})
// 等同于
Promise.resolve(42)
5.Promise.prototype.catch()
使用 Promise 对象的 catch 方法来捕获异步操作过程中出现的任何异常。 ==可以省去每一个 .then() 中的 onRejected 函数参数,将对每一个.then()语句中的错误统一处理== throw Error 和 reject 都可以触发 catch 的捕获,但是不建议在 Promise 内部使用 throw 来触发异常,而是使用 reject(new Error()) 的方式来做,因为 ==throw 的方式并没有改变 Pronise 的状态==
==6.Promise.all() 应用于处理 同时需要返回很多接口的数据==
基本语法
Promise.all(promiseArray);
var p1 = Promise.resolve(1)
var p2 = Promise.resolve(2)
var p3 = Promise.resolve(3)
Promise.all([p1, p2, p3]).then(function (results) {
console.log(results) // [1, 2, 3]
})
-
Promise.all 生成并返回一个新的 Promise 对象,所以它可以使用 Promise 实例的所有方法,参数传递promise数组中所有的 Promise 对象都变为resolve的时候,该方法才会返回 resolve, 新创建的 Promise 则会使用这些 promise 的值。
-
如果参数中的任何一个promise为reject的话,则整个Promise.all调用会立即终止,并返回一个reject的新的 Promise 对象。
-
由于参数数组中的每个元素都是由 Promise.resolve 包装(wrap)的,所以Paomise.all 可以处理不同类型的 promose对象。
==7.Promise.race() 用于处理 主备用资源 谁先返回使用谁==
基本语法
Promise.race(promiseArray)
示例:
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2)
}, 10)
})
Promise.race([p1, p2]).then((value) => {
console.log(value)
})
使用Promise 对 上面ES5回调地狱 的改写
const fs = require('fs');
const path = require('path');
// 用 promise 获取文件 内容
function getFileContent(fileName) {
// new 一个Promise时 此时Promise的状态是 padding 结果是 undefined
return new Promise((resolve, reject) => {
const fullFileName = path.resolve(__dirname, 'files', fileName);
fs.readFile(fullFileName, (err, data) => {
if (err) {
reject(err); // reject 被执行,Promise的状态变为 reject 结果变为 error
return
}
// resolve 被执行,Promise的状态变为 fulfilled 结果变 JSON.parse(data.toString())
resolve(
JSON.parse(data.toString())
)
})
})
}
// 写法一:
// getFileContent('a.json')
// .then(getFileContent('b.json'))
// .then(getFileContent('c.json'))
// .then() 和 .catch() 方法是 promise 原型上的方法(即:实例方法)
/* promise.then(onFulfilled, onRejected) onFulfilled对应 resolve方法 onRejected对应 reject方法
* onFulfilled, onRejected 这两个参数 都是函数类型
* 但是 getFileContent('b.json') 不是函数,是函数执行后的 返回值
* 所以 按理说 应该传入两个函数,可是 传入一个 promise对象
* 原因是 :规定两个函数参数如果不传,将会执行传入的表达式,相当于执行了
*/
// 如果你传入的参数 是非函数,那么.then()将会返回一个 空的 promise 对象
// 写法二:
getFileContent('a.json')
.then(() => {
return getFileContent('b.json'); // 不加 return 返回的仍是一个空的 promise 对象,这样会导致对下一个.then() 不会造成影响
}, (err) => {
console.log(err);
})
.then(() => {
return getFileContent('c.json');
}, (err) => {
console.log(err);
});
// 写法三:
getFileContent('a.json')
.then(() => {
return getFileContent('b.json'); // 不加 return 返回的仍是一个空的 promise 对象,这样会导致对下一个.then() 不会造成影响
})
.then(() => {
return getFileContent('c.json');
})
.catch(err => {
console.log(err);
})
/*
* 在这三个文件中都有下一个文件的名字
* 可以通过 return getFileContent('b.json'); 返回的 resolve 值中获取下一个文件名
* 在下一个 then 方法中做参数使用
*/