一、ES6
1、全局变量
//两种生成全局变量的方法(都是写在函数体外)
var a=123;
b=456;
//两种方法生成全局变量的区别:
//1、
//没有使用 var 生成的全局变量,是作为window的属性存在,
//不是真正意义上的全局变量,可以用 delete b (或者delete window.b)删除,删除之后获取b则是 not defind,
//而通过 var 生成的全局变量,无法使用 delete 删除
//2、
//在函数内部,没有使用 var 创建的变量,是全局变量;反之则是局部作用域
2、作用域
作用域包括:全局作用域(global,window),函数作用域(function,局部作用域),块级作用域({}),动态作用域(this)
注:ES5及之前,作用域只有全局作用域和局部作用域(函数作用域),没有块作用域的概念,
function test(){
var a=1;
if(a == 1){
var b=2;
console.log(a)
}else{
console.log(a)
}
console.log(b) //ES5环境中输出 2 ,因为ES5没有块级作用域
}
3、let 和 const
- let
- 不能重复声明
- 块级作用域
- 不存在变量提升
- const
- 初始化时需要赋值
- 一般常量使用大写
- 块级作用域
- 常量的值不能修改
- 对于数组和对象的元素修改,不算做对常量的修改,不会报错(同过下标和key对数组、对象进行修改,不会改变引用地址)
var,let,const 区别
1)var,let 是定义变量(定义后可以修改),const 是定义常量(定义后无法修改,声明时就需要赋值,不能先声明后赋值)
2)var 声明的变量具有全局作用域或者函数作用域,let 和 const 声明的变量/常量具有块级作用域
3)var 声明的全局变量可以使用全局对象的属性去访问,let,const 声明的全局变量不能用全局对象的属性(即 window.a)获取,
4)var 可以重复声明定义同一个变量,但是 let,const 不可以重复用定义同一个变量
5)var 声明的变量有变量提升,let,const 没有变量提升
4、解构赋值
//1、数组的解构赋值
let arr = ["张","王","李","赵"]
let [zhang, wang, li, zhao] = arr
console.log(zhang); //张
console.log(wang); //王
console.log(li); //李
console.log(zhao); //赵
//2、对象的解构赋值
let obj = {
name: "张三",
age: 18,
sayHello: function () {
console.log(`你好啊,我叫张三,18岁了`);
}
}
let { name, age, sayHello } = obj
console.log(name); //张三
console.log(age); //18
console.log(sayHello); //ƒ () {console.log(`你好啊,我叫张三,18岁了`);}
sayHello() //你好啊,我叫张三,18岁了
5、模版字符串
//1、可以直接进行换行
let el = `
<div>
<span>哈哈哈哈</span>
</div>
`
//2、可以使用$拼接
let age = 18
let msg = `我今年${age}岁了`
console.log(msg);//我今年18岁了
6、简化对象的写法
let name = "张三"
let obj = {
name
}
console.log(obj);
7、箭头函数及声明特点
let fn = () => {}
- this 是静态的,始终指向函数声明时所在作用域的值,call、apply也无法更改
function getName() {
console.log(this.name)
}
let getName2 = () => {
console.log(this.name)
}
window.name = "张三"
const school = {
name: "李四"
}
// getName()//张三
// getName2()//张三
getName.call(school)//李四--这里call改变了this指向
getName2.call(school)//张三--this指向没有被改变,这里getName2仍然是定义时的this指向
- 不能作为构造函数实例化对象
function fn() {
console.log(111);
}
let fn2 = () => {
console.log(222);
}
let newFn = new fn()
let newFn2 = new fn2()//这里会报错
- 不能使用arguments变量
function fn() {
console.log(arguments);
}
let fn2 = (data) => {
console.log(arguments);
}
fn(1,2,3);//打印出 arguments 数据[1,2,3...]
fn2(1,2,3);//报错:arguments is not defined
- 箭头函数的简写
- 省略小括号:当形参有且只有一个的时候
let fn = n => { return n++ }
- 省略花括号和return关键字:当前代码体只有一条语句的时候,语句的执行结果就是函数的返回值
let fn = n => n++
8、函数参数的增加的默认设置
- 形参允许使用默认值,一般位置要考后
//传入参数c时,c取传入的参数,不传时,默认为0
function fn(a,b,c = 0) {
return a + b + c
}
let result = fn(1,2)
let result2 = fn(1,2)
console.log(result);//3
console.log(result2);//6
- 可以与结构赋值结合
function fn({ name = "佚名", age }) {
console.log(name);
console.log(age);
}
let obj = {
name: "张三",
age: 18,
}
let obj2 = {
age: 18,
}
fn(obj)//张三、18
fn(obj2)//佚名 、18
9、rest 参数
ES6引入rest参数,用于获取函数的实参,用来代替arguments
//ES5的函数参数
function fn() {
console.log(arguments);
}
fn("张三","李四","王五")//arguments 对象
//ES6的函数参数,args是个数组
function fn2(...args) {
console.log(args);//数组:['张', '李', '王']
}
fn2("张","李","王")
//args是除了a,b,以外的其他参数组成的数组
function fn3(a,b,...args) {
console.log(a);//张
console.log(b);//李
console.log(args);//数组:['王', '赵', '周']
}
fn3("张","李","王","赵","周")
10、扩展运算符
- 在函数中使用
const arr = ["张","王","李"]
function fn() {
console.log(arguments);
}
fn(...arr)//返回的arguments包括数组中的每一项
fn(arr);//返回的arguments中是一个数组
此处在函数中与rest相似,但是不同的是,rest中是在形参中使用的,这里是在实参中使用。
2) 对数组进行合并
let arr1 = ["1","2","3"]
let arr2 = ["4","5","6"]
console.log([...arr1,...arr2]);// ['1', '2', '3', '4', '5', '6']
- 对数组进行克隆
let arr3 = ["a","b","c",{name: "张三"}]
let arr4 = [...arr3]
arr3[3].name = "李四"
arr3[4] = 4
console.log(arr3);// ['a', 'b', 'c', {name: '李四'}, 4]
console.log(arr4);// ['a', 'b', 'c', {name: '李四'},]
//注:是浅复制
- 将伪数组转化为真正的数组
let divs = document.querySelectorAll("div")
console.log(divs);
let divArr = [...divs]
console.log(divArr);
11、新的数据类型:Symbol
ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值
- 创建方式:Symbol()、Symbol.for()
- 不能与其他数据进行计算
let s2 = Symbol("张三")
let s3 = Symbol("张三")
console.log(s2 === s3);//false
let s4 = Symbol.for("张三")
let s5 = Symbol.for("张三")
console.log(s4 === s5);//true
let result1 = s2 + 100
let result2 = s3 > 100
let result3 = s4 + "-"
前端数据类型:
- undefined
- null
- boolean
- string
- number
- object
- symbol
- 可以作为对象的属性
let s = Symbol('say');
let youxi = {
name: "狼人杀",
[s]: function () {
console.log("说话了");
}
}
// youxi.say()
console.log(youxi);//{name: '狼人杀', Symbol(say): ƒ}
youxi[s]()//说话了
12、迭代器
//需求:使用 for of 打印出hobby的每一项值
let obj = {
name: "张三",
hobby: [
"吃饭",
"睡觉",
"打豆豆"
],
[Symbol.iterator] () {
let index = 0
let _this = this
return {
next: function () {
if(index < _this.hobby.length) {
const result = { value: _this.hobby[index], done: false }
index ++
return result
} else {
return { value: undefined, done: true }
}
}
}
}
}
for(let i of obj) {
console.log(i);
}
13、promise
promise 是ES6引入的异步编程的新解决方案。语法上proise是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果。
- Promise 构造函数:Promise(excutor){}
- Promise.ptototype.then 方法
- Promise.prototype.catch 方法
简单实现:
let p = new Promise(function(resolve, reject) {
setTimeout(() => {
// let data = "成功"
// resolve(data)
let err = "失败"
reject(err)
})
})
p.then((value) => {
console.log("value" ,value);
}, (reason) => {
console.log("reason",reason);
})
读取文件中promise的应用
//导入node 中 fs 模块
const fs = require('fs')
// 正常实现
// fs.readFile("./成绩.txt", function (err,dataStr) {
// if(err) throw err
// console.log(dataStr.toString())
// })
//通过promise实现
let p = new Promise(function (resolve,reject) {
fs.readFile("./成绩.txt", function (err,dataStr) {
if(err) reject(err)
resolve(dataStr)
})
})
p.then((value) => {
console.log(value.toString());
},(err) => {
console.log("读取失败");
})
then 方法:
0) 接收两个回调,第一个是resolve成功后的回调,第二个是reject失败后的回调
- then方法返回的结果是 Promise 对象,对象状态由回调函数的返回值决定
- 如果回调函数中返回的结果是 非promise 类型的属性,状态为成功,返回值为对象成功的值
- 如果返回promise对象,返回状态及对象则由这个promise对象决定
14、set
- 声明:
let s = new Set()
let s2 = new Set(["张三","李四","王五","李四"])
- 添加
s2.add("马六")
- 删除
s2.delete("张三")
- 是否存在
s2.has("张三")
- 清空
s2.clear()
15、Map
16、Class
- 创建类
//ES5创建类
function Person (name, age) {
this.name = name;
this.age = age
}
Person.address = "中国"
Person.prototype.sayHello = function () {
console.log(`我的姓名是${this.name},我的年龄是${this.age}`);
}
let p1 = new Person("张三", 18)
p1.sayHello(0)
console.log(Person.address);//中国----直接同过 类.属性 赋值时,属性仅属于类,而不属于实例,Es6中的 static 功能也是如此
console.log(p1.address);//undefined
class Phone{
static memory = "256G"
constructor(brand, price) {
this.brand = brand;
this.price = price
}
call() {
console.log(`${this.brand}手机价值${this.price}`);
}
}
let p2 = new Phone("华为",5999)
p2.call()
console.log(Phone.memory);//256G
console.log(p2.memory);//undefined
- 构造函数继承
ES5的继承
function Phone(brand, price) {
this.brand = brand;
this.price = price;
}
Phone.prototype.showMsg = function () {
console.log(`${this.brand}手机价值${this.price}元`);
}
function SmartPhone(brand,price,color,memory) {
Phone.call(this, brand,price)
this.color = color
this.memory = memory
}
SmartPhone.prototype = new Phone;
SmartPhone.prototype.constructor = SmartPhone
SmartPhone.prototype.photo = function () {
console.log("我可以拍照");
}
SmartPhone.prototype.playGame = function () {
console.log("我可以玩游戏");
}
const huawei = new SmartPhone("华为", 5999, "白色", "256G")
console.log(huawei);
ES6的继承:
class Phone{
constructor(brand, price) {
this.brand = brand
this.price = price
}
showMsg() {
console.log(`${this.brand}手机价值${this.price}元`);
}
}
class SmartPhone extends Phone{
constructor(brand, price, color, memory) {
super(brand, price) //此处等价于 Phone.call(this, brand, price)
this.color = color
this.memory = memory
}
photo() {
console.log("我可以拍照");
}
playGame() {
console.log("我可以玩游戏");
}
}
let huawei = new SmartPhone("华为", 5999, "白色", "256G")
console.log(huawei);
huawei.showMsg()
huawei.photo()
huawei.playGame()
17、数值扩展
- Number.EPSILON:Javascript 表示的最小精度,两数之差小于这个数,就表示两数相等
function equal(a,b) {
if(Math.abs(a-b) < Number.EPSILON) {
return true
} {
return false
}
}
console.log(equal(0.1+0.2, 0.3));//true
- 二进制、八进制
- Number.isFinite 检测一个数值是否为有限数
- Number.parseInt,Number.parseFloat
- Number.isInteger 判断一个数是否为整数
- Math.trunc 将数字的小数部分抹掉
- Math.sign 判断一个数是正数、负数还是0
18、对象方法扩展
- Object.is 判断两个值是否完全相等–只能判断基本数据类型,数组、对象返回false
console.log(Object.is([1,2,3],[1,2,3]));//false
console.log(Object.is(["2"],"2"));//false
- Object.assign 对象的合并–浅拷贝,后者覆盖前者
- Object.setPrototypeOf,Object.getPrototypeof 设置原型对象,获取原型对象
19、export
- 方法1
//m1.js 导出
export let name1 = "张三"
export function fn1() {
console.log("模块化呀");
}
//index.html 引入
import * as m1 from "./m1.js"
m1.fn1()
console.log(m1);
- 方法2
//m1.js 导出
let name2 = "张三"
function fn2() {
console.log("模块化呀~~")
}
export {name2, fn2}
//index.html 引入
import * as m1 from "./m1.js"
m1.fn2()
console.log(m1);
- 方法3
//m1.js 导出
export default {
name: "李四",
fn3() {
console.log("李四模块化")
}
}
//index.html 引入
import * as m1 from "./m1.js"
m1.default.fn3()
console.log(m1);
20、import
- 通用引入
import * as m1 from "./m1.js"
- 解构赋值引入
import { name, fn1 } from './m1.js'
//使用as别名,下面可使用func1(),为了解决命名冲突的情况
import { name, fn1 as func1 } from './m1.js'
- 简便形式,针对默认暴露(见19-3)
import m1 from "./m1.js"
打印m1如下:
二、ES8
1、async 函数
async函数的返回:
- 返回的结果不是Promise类型的对象时(如字符串、数字),返回的结果会转化为成功的Promise类型的对象
- 抛出错误时,返回的结果是一个失败的Promise类型的数据
- 返回的结果是Promise对象时,async函数返回这个Promis对象
async function fn() {
// return "123"
// throw new Error("出错啦")
return new Promise((resolve,reject) => {
// resolve("成功了")
reject("失败了")
})
}
let result = fn()
console.log(result);
result.then((vaule) => {
console.log("成功");
},(err) => {
console.log("失败");
})
await 函数
注:await 要放到 async 里面使用,async 里面可以没有await
let p = new Promise((resolve, reject) => {
console.log("p---"+"成功");
resolve("成功了")
})
async function main() {
let result = await p
console.log(result);
}
main()
4、数组
1)ES5 及 ES6 的遍历
2)什么是伪数组?
- 存储数据按照索引方式
- 有一个 length 属性
例:
let likearry={
0:'a',
1:'b',
length:2
}
3)伪数组转换为数组
//ES5
let args=[].slice.call(arguments) //collection 集合转换为数组
let imgs-[].slice.call(document.querySelectorAll('img')) //NodeList node列表转换为数组
//ES6 (Array.from)
let args=Array.from(arguments)
let imgs=Array.from(document.querySelectorAll('img'))
Array.from(arrayLike,mapFn,thisArg)
arrayLike:伪数组,mapFn:遍历函数,thisArg:this
4)生成新数组的方法(ES5,ES6)
5)数组的方法(ES6)
- Array.of()
将一组数值转化为数组
Array.of() //[]
Array.of(3,11,18) //[3,11,18]
Array.of(3) //[3]
Array.of(3).length //1
Array.of 方法可以用以下代码模拟实现
function ArrayOf(){
return [].slice.call(arguments)
}
注:Array.of 总是返回参数值组成的数组,如果没有参数就返回一个空数组
- Array.fill()
填充数组
说明:该方法接收三个参数,
若没有参数array.fill()
,则数组每一项都为 undefind
若有一个参数array.fill(1)
,则将数组的每一项都修改为1
若有两个参数array.fill(1,2)
,则将数组中下标>=2的对应的数组值改为1
若有三个参数array.fill(1,2,4)
,则将数组中下标>=2,<4的值改为1
let array=Array(5).fill(7); //[7,7,7,7,7]
['a','b','c'].fill(7); //[7,7,7]
new Array(3).fill(7); //[7,7,7]
['a','b','c','d'].fill(7,1,3); //['a','7','7','d']
['a','b','c','d'].fill(); //[undefined, undefined, undefined, undefined]
['a','b','c','d'].fill(7,1); //["a", 7, 7, 7]
- Array.find()
查找数组中第一个符合条件的数组成员,参数是一个回调函数。返回符合条件的数组成员,如果没有符合条件的成员,返回 undefind
let arr=[1,2,3,4,5];
let val=arr.find(function(item,index,arr){ //参数分别是当前的数组元素,当前索引值,被查找的数组
return intem % 2 === 0
})
console.log(val); //2
ES5中的
filter
也可以用来进行数组查找let arr=[1,2,3,4,5]; let result=arr.filter(function(oitem){ return item %2 ==0 }) console.log(result) //[2,4]
filter
与find
的区别:
filter
即使查到符合条件的成员,仍然会继续往下查找,不会停止,而find
只要查到符合条件的成员就是停止查找,返回第一个符合条件的成员
- Array.findIndex()
查找符合条件的数组成员的位置,若所以成员都不符合,则返回 -1
let arr=[1,2,3,4,5,6];
let result = arr.findIndex(function(item ){
return item > 5;
})
console.log(result ) //5
5、class
1)ES5 中定义类
法一:
let Animal=function(){
this.type=type;
this.walk=function(){
console.log('I am walking');
}
}
let dog=new Animal('dog');
let monkey=new Animal('monkey');
monkey.walk=function (){
console.log('monkey is walking')
}
monkey.walk(); //monkey is walking
dog.walk(); //I am walking
该写法将每个方法挂载到了实例对象上面,导致每个实例对象会大
法二:
let Animal=function(){
this.type=type;
}
Animal.prototype.walk=function(){
console.log('I am walking');
}
let dog=new Animal('dog');
let monkey=new Animal('monkey');
monkey.walk=function(){
console.log('change type')
};
console.log(dog);
console.log(monkey);
该写法将
walk
方法放到了原型链(原型链上做继承)上,实例对象不会很大,dog
和monkey
实例中的,若修改Animal.prototype.walk
中的逻辑,所有实例对象都会受影响,但是若修改实例对象上的walk
,,其他对象上的walk
方法不会被修改。
延申:
let Animal=function(){
this.type=type;
}
Animal.prototype.walk=function(){
console.log('I am walking');
}
let dog=new Animal('dog');
let monkey=![在这里插入图片描述](https://img-blog.csdnimg.cn/20200320155730523.png)new Animal('monkey');
monkey.constructor.prototype.walk=function(){
console.log('change type')
};
console.log(dog);
console.log(monkey);
monkey.constructor.prototype.walk
这种写法修改walk
,会修改共同的walk
方法
2)ES6 创建 class 类
class Animal{
constructor(type){ //构造函数,里面写属性
this.type=type;
}
walk(){ //方法
console.log('I am walking');
}
}
let dog=new Animal('dog');
let monkey=new Animal('monkey');
虽然 ES6 在类的定义是仅是 ES5 定义类的语法糖,但是从开发的角度而言,开发更有效率了,代码可阅读性大大提升。
3)Setter & Getter(如何读写属性)
作用:保护私有变量,通过一定规则来限制对属性的修改。
class Animal{
constructor(type){ //构造函数,里面写属性
this.type=type;
}
get age(){ //这里的 age 不是真正意义上的属性,只是用来读取 age 的入口
return 4;
}
walk(){ //方法
console.log('I am walking');
}
}
let dog=new Animal();
dog.age=5;
console.log(dog.age) //4,get相当于 getter ,只允许读取数据,不允许修改数据;若要修改需使用 set 方法
set 、get 配合使用
let _age = 4
class Animal {
constructor (type) { // 构造函数,里面写属性
this.type = type
}
get age () {
return _age
}
set age (val) {
if (val > 4 && val < 7) {
return _age = val
}
}
walk () { // 方法
console.log('I am walking')
}
}
let dog = new Animal('dog');
console.log(dog.age); //4
dog.age=5;
console.log(dog.age); //5
4)Statuc Methods(静态方法)
类的实例方法:可通过实例去访问的方法;方法依赖于对象的属性或方法时(内部要引用实例对象的信息),需要定义为实例对象方法。
类的静态方法:不属于对象实例,只属于类;不能通过实例去访问该方法,只能通过类去访问;不涉及实例对象的内容时,可以定义为静态方法。
//ES5中利用 function 实现
let Animal=function (type) {
this.type=type
this.walk=function (){
console.log('I am walking')
Animal.eat() //这里因为 eat 是静态方法,无法使用 this.eat() ,因为 this 指的是实例对象
}
}
Animal.eat=function (food){ //这个方法就是静态方法,实例对象无法访问,只能通过 Animal.eat() 访问
console.log('I am eating')
}
let dog=new Animal('dog')
dog.walk() //先后输出 ‘I am walking’,‘I am eating’
dog.eat() //报错:dog.eat is not a function
//eat 是静态方法,只能通过 Animal.eat() 去使用,不能通过实例对象使用
ES6 使用 static
标记是否是静态方法
Class Animal {
constructor(type){
this.type=type
}
walk(){
console.log('I am walking')
}
static eat(){ //静态方法
console.log('I am eating')
}
}
5)Sub Classes(类的继承)
ES5 中的继承
let Animal=function (type) {
this.type=type
this.walk=function (){
console.log('I am walking')
}
}
Animal.prototype.eat=function(){
this.walk()
console.log('I am eat')
}
let Dog=function(){
Animal.call(this,'dog')
//call 方法实现了构造函数中的继承,即此时 Dog 只继承了 Animal 的 type 属性和 walk 方法,没有继承 eat 方法
//第一个参数 this ,将 Animal 中的 this 指向了 Dog 的实例对象
//第三个参数是 Animal 中的 type ,若没有这个参数, 实例对象dog 就没有 type 属性,即 dog.type 为undefined
}
Dog.prototype=Animal.prototype
//这里实现了原型链上的继承,此处表示,Dog 类上,挂载了 eat 方法
let dog=new Dog()
dog.eat();
ES6 中类的继承
class Animal{
constructor(type){
this.type=type
}
eat(){
this.walk()
console.log('Im eat food')
}
walk(){
console.log('I am walking')
}
}
class Dog extends Animal{
constructor(type){
super(type)
this.age=2 //若子类没有增加新的属性,constructor 函数可以省略不写
}
}
var dog=new Dog()
dog.eat()
练习
哪些场景可以用类来实现
阅读
类
Classes(ES6)Sample
ES6 class
Classes
ES6 Class Tutorial
6、function 参数
1)默认参数
ES5 默认参数:
function f(x,y,z){
if(y === undefined){
y=7
}
if(z === undefined){
z=42
}
console.log(arguments.length) //这里获取到参数的长度
return x + y + z
}
console.log(f(1))
ES5 的函数中,可以通过 arguments 去获取参数信息,包括接受的参数值、参数长度
ES6 默认参数:
function f(x,y=7,z=43){ //function f(x,y=7,z=x+y){
console.log(f.length) //这里可以获取到没有默认参数的长度
return x + y + z
}
console.log(f(1)) //51
console.log(f(1,2,3)) //6
console.log(f(1,undefined,3)) //11
注:ES6中,函数若有多个参数,如上例所示
1、若要使用参数 y 的默认值,可以在调用函数时,参数写为 undefined ,若 y 、z 都要使用默认值,可直接省略
2、没有默认值的参数,写在前面,需要默认值的参数写在后面,如示例中,x 在 y 和 z 的前面
3、默认值可以是常量,也可以是参数表达式
2)Rest Parameter(不确定参数)
ES5
function f(){
let num=0
//arguments 是伪数组,所以需要转换为数组,再去遍历
//Array.prototype.forEach.call(arguments,function(item){
// num+=item*1
//})
Array.form(arguments).forEach(function(item){
num+=item*1
})
console.log(num)
}
f(1,2,3,4)
ES6
function f(base,...nums){
//...为展开运算符,若没有参数base,nums中包含传入的所以参数,
//在这里,base表示第一个参数,nums表示之后的所有参数
let num=0+base*2
nums.forEach(function (item){ //nums 是数组
num+=item*1
})
console.log(num)
}
f(1,2,3,4,5)
3)Spread Operator(reset 参数的逆运算)
函数定义时参数确定,传入的数据不确定时:
function f(x=1,y=2,z=3){
return x + y + z
}
let data=[4,5,6]
//ES5
console.log(f.apply(this,data)) //16
//ES6
console.log(f(...data)) //16
Spread Operator 和 Rest Parameter 是形似但意义相反的操作符,简单来说,Rest Parameter 是把不定的参数 “收敛” 到数组,而 Spread Operator 是把固定的数组内容 “打散” 到对应的参数。
7、箭头函数
1)函数声明
ES5
function hello(){
...
}
//或
let hello=function (){
...
}
ES6:
let hello = (name) => {
console.log('hello',name)
}
2)简写
- 如果只有一个参数
只有一个参数,可以省略小括号,否则一定要记得带括号
let hello = name => {
console.log('hello',name)
}
- 如果返回值是表达式
返回值是表达式时,可以省略 return 和 {}
let pow = x => x * x
- 如果返回值时字面量对象
返回值时字面量对象,一定要用小括号抱起来
ler person = (name) => ({
age:20,
addr:'Beijing Ciry'
})
- 其他情况下,不能简写,只能中规中矩
3)拓展(this)
ES5 中的普通函数中,有 this
说法,谁调用函数,this
就指向谁,
而箭头函数对 this
的处理是在定义时,this
的指向,之后不做改变
练习:
1、如何用箭头函数来实现一个数组的排序
2、箭头函数对 this 的处理还有什么妙用
阅读:
箭头函数
默认参数
reset-paramrters
Three dots (…) in Javascript
8、Object
1)定义
ES5
var x=1,y=2,z=3;
var obj={
x:x,
y:y,
f:function(){ //对象中的函数
...
}
}
obj[a]=0 //新增
ES6
let x=1,y=2,z=3;
let obj={
x, //ES6 写法,key 和 value 一样的情况下,可以只写一个
y:y,
[x+y]:3 //可以通过 [] 来新增属性,[]里面可以放键名,也可以是 表达式
f(a,b){ //ES6 对象中的方法可以简写,该方法为同步方法
...
}
* q(a,b){ //带有 * 的为异步方法,后面会学习到;**ES5 中不支持异步函数**
...
}
}
9、Set 数据结构
ES6 中的 Set 数据结构对应传统数据结构的 “集合”,类似于数组,但成员的值是唯一的,没有重复。Set 本身是一个构造函数,用来生成 Set 数据结构。
1)生成 Set 实例
let s=new Set() //定义空的 Set 实例
let s=new Set([1,2,3,4]) //实例化的同时传入默认的数据
注:
初始化的参数必须是可遍历的,可以是数组或者自定义遍历的数据结构
2)添加数据
let s=new Set()
s.add('hello')
s.add('world')
//或者
s.add('hello').add('world')
console.log(s)
注:
Set 结构不允许数据重复,所以添加重复的数据是无效的,
如:s.add('hello').add('world').add('hello')
,结果还是上图所示
3)删除数据
let s=new Set(['hello','world'])
s.delete('hello') //返回 true
s.delete('hi') //返回 false
4)清空数据
let s=new Set(['hello','world'])
s.clear()
5)统计数据
let s=new Set(['hello','world'])
s.has('hello') //查询数据,存在返回 true,否则返回 false
console.log(s.size) //2
s.add('hi')
console.log(s.size) //3
注:
这里的 size 是 Set 数据的属性,不是方法
6)查询数据
- keys():返回键名的遍历器
- values():返回键值的遍历器
- enries():返回键值对的遍历器
- forEach():返回回调函数遍历的每个成员
- for…of:可以直接遍历每个成员
let s=new Set(['hello','world'])
console.log(s.keys()) //SetIterator {'hello','world'}
console.log(s.values()) //SetIterator {'hello','world'}
console.log(s.entries()) //SetIterator {"hello" => "hello", "world" => "world"}
s.forEach(item => {
console.log(item) //hello//world
})
for(let item of s){
console.log(item) //hello//world
}
注:
SetIterator 后续会学习
思考:
1、因为 Set 中的值总是唯一的,所以需要判断两个值是否相等,那么 +0,-0,NaN,undefined 可以被重复添加吗?
不可以。
2、Set 对存储的类型是不限的,如果想限制只能存储对象,该怎么办?请使用 WeakSet
10、Map 数据结构
Map
数据结构,类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object
结构提供了“字符串–值”的对应,Map
结构提供了“值–值”的对应,是一种更完美的 Hash 结构实现。如果需要“键值对”的数据结构,Map
比Object
更合适。
1)生成 Map 实例
//法1:数组
let M=new Map([['name','张三'],['title','Author']])
//参数是数组时,需要数组成员时一个个表示键值对的数组,
//该例中,新建Map实例时,就制定了两个键--name和title
console.log(M) //Map(2) {"name" => "张三", "title" => "Author"}
//法2
let m=new Map()
let o={p:'hello world'}
m.set(o,'content-1')
console.log(m) //见下图
2)添加数据
let map=new Map()
let keyObj={}
let keyFunc=function(){}
let keyString='a string'
map.set(keyString,"和键 a string 关联的值")
map.set(keyObj,"和键 keyObj 关联的值")
map.set(keyFunc,"和键 keyFunc 关联的值")
console.log(map)
3)删除数据
代码接 2)
map.delete(keyObj) //返回 true,若map中不包含 keyObj ,则返回false
4)清空数据
代码接 2)
map.clear()
5)统计数据
代码接 2)
console.log(map.size) //3
6)查询数据
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 关联的值"
console.log(map.keys()) //MapIterator {"a string", {…}, ƒ}
console.log(map.values()) //MapIterator {"和键 a string 关联的值", "和键 keyObj 关联的值", "和键 keyFunc 关联的值"}
console.log(map.entries()) //MapIterator {"a string" => "和键 a string 关联的值", {…} => "和键 keyObj 关联的值", ƒ => "和键 keyFunc 关联的值"}
map.forEach((value,key,map){
console.log(value,key,map)
})
for([key,value] of map){ //这里循环项要用 [],第一个参数是 key,第二个参数是 value
console.log(key,value)
}
Object 和 Map 的区别
- 键的类型
一个Object的键只能是字符串或Symbols,但一个Map的键可以是任意值,包括函数、对象、基本类型 - 键的顺序
Map 中的键值是有序的,而添加到对象中的键不是。因此,当对他进行遍历时,Map 对象时按中插入的顺序返回键值 - 键值对的统计
你可以通过 size 属性直接获取一个 map 的键值对个数,而 Object 的键值对只能手动计算 - 键值对的遍历
Map 可直接进行迭代,而 Object 的迭代需要先获取它的键数组,然后再进行迭代 - 性能
Map 在涉及频繁增删键值对的场景下会有性能优势
11、Object.assign()
该方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象,它将返回目标对象。实现的是 浅拷贝
语法:
Object.assign(target,…sources)
let target={a:1,b:2}
let source={b:4,c:5}
let newTarget=Object.assign(target,source)
console.log(target) //{a: 1, b: 4, c: 5}
console.log(newTarget) //{a: 1, b: 4, c: 5}
思考:
1、如果目标对象传入的是 undefined 和 null 将会怎样?
2、如果源对象的参数是 undefined 和 null 又会怎样?
3、如果目标对象是个嵌套的对象,子对象的属性会被覆盖吗?
阅读
es6-enhanced-object-literals
es6-features
Object.assign()
阅读资料:
1、内存管理
2、javascript 世界里有哪些元素是可以遍历的
3、如何给数组结构自定义遍历
4、find()和 ES5 中的有什么区别