一.let
作用:同var一样用来声明变量
特点
在块级作用域内有效
不能重复声明
变量提升
全局变量提升:会创建一个变量对象(script)用来收集全局作用域下let定义的变量
局部变量提升:会将var let定义的变量全部放到当前函数的变量对象中
同var的变量提升的区别:let提升的变量在为赋值之前不允许被使用
应用
循环遍历加监听
使用let取代var是趋势
二.const关键字
作用:定义一个常量
特点:不能修改,其他特点同let
应用:保存不用改变的数据
三.解构赋值
理解:从对象或数组中提取数据,并赋值给变量(多个)
对象的解构赋值
let {n,a} = {n:“tom” , a:12}
数组的解构赋值
let {a ,b} = {1 ,2}
用途:给多个形参赋值
四.模板字符串
作用:简化字符串的拼接
写法:模板字符串必须使用`` 包含,变化部分用 ${xxxx}定义
let obj = {username: 'kobe', age: 43}; let str = '我的名字是: ' + obj.username + ', 我的年龄是: ' + obj.age; console.log(str); console.log('---------------模板字符串拼接--------------------'); let str1 = `我的名字是 ${obj.username}, 我的年龄是 ${obj.age}` console.log(str1);
五.对象的简写方式
同名属性可以省略–>key和value一样的时候
省略函数的function,冒号
let username = 'kobe'; let age = 43; console.log('---------------- 对象的常规书写方式---------------------'); let obj = { username: username, age: age, showName: function () { console.log(this.username); } } console.log(obj); obj.showName(); console.log('---------------- 对象的简写方式---------------------'); let obj = { username, // 同名的属性可省略 ---> key 和value一样的时候 age, showName() { // 省略函数的function,冒号 console.log(this.username); } } console.log(obj); obj.showName();
六.箭头函数
语法:
let fun = 形参 => 函数体
重点:函数体
只有一条语句或者表达式的时候,{ }可以省略
当多条语句时候,{ }不能省略
当{ }省略的时候,会自动return当前语句或者表达式的执行结果
特点:
this: 箭头函数没有自己this,不是调用的时候决定的,而是定义的时候决定的
理解:
定义的时候看外部是否有函数,如果没有函数,this指向window
如果有外部函数,this同外部函数的this指向是同一个对象
箭头函数不能用作构造函数
console.log('------------- 箭头函数 - 没有形参 -----------------'); // 当箭头函数没有形参的时候 () 不能省略 let fun = () => console.log('fun()'); fun(); console.log('------------- 箭头函数 - 有一个形参 -----------------'); // 当箭头函数只有一个形参的时候,() 可以省略,也可以不省略 let fun1 = a => console.log('只有一个形参的时候', a); fun1(123); console.log('------------- 箭头函数 - 有多个形参 -----------------'); // 当箭头函数有多个形参的时候, ()不能省略 let fun2 = (a, b) => console.log('有多个形参的时候', a, b); fun2(1,2); console.log('------------- 箭头函数 - 函数体只有一条语句的时候 -----------------'); // 当箭头函数的函数体只有一条语句的时候{}可以省略, 当{}省略的时候会自动 return 当前语句或者表达式的结果, 当{}不省略的时候,需要手动指定返回的结果, 否则默认执行 return value: undefined; let fun3 = () => console.log('函数体只有一条语句的时候'); console.log(fun3());// 语句的返回结果,undefined let fun3 = () => 123 console.log(fun3());//表达式的返回结果,123 console.log('------------- 箭头函数 - 函数体有多条语句的时候 -----------------'); // 当箭头函数的函数体有多条语句的时候,{}不能省略, 当{}不省略的时候,需要手动指定返回的结果, 否则默认执行 return value: undefined; let fun4 = () => { let a = 1; console.log('函数体有多条语句的时候', a); // return a;-->1 } console.log(fun4());//undefined
七.点点点运算符
1 …运算符去拆包指定的数组
//把arr2数组插入到arr里面1和6中间 let arr = [1, 6]; let arr2 = [2,3,4,5]; let arr3 = [1, ...arr2, 6]; console.log(arr3); // [1, 2, 3, 4, 5, 6]
2 …运算符去拆包不能直接去遍历对象
let obj2 = {name: 'kobe', age: 43}; console.log(...obj2);//报错
八.Symbol
前言:ES5中对象的属性名都是字符串,容易造成重名,污染环境
概念:ES6中的添加了一种原始数据类型symbol(已有的原始数据类型:String, Number, boolean, null, undefined, 对象)
特点:
1、Symbol属性对应的值是唯一的,解决命名冲突问题
2、Symbol值不能与其他数据进行计算,包括同字符串拼串
3、for in, for of遍历时不会遍历symbol属性
使用:
1、调用Symbol函数得到symbol值
let symbol = Symbol(); let obj = {}; obj[symbol] = 'hello';
2、传参标识
let symbol = Symbol('one'); let symbol2 = Symbol('two'); console.log(symbol);// Symbol('one') console.log(symbol2);// Symbol('two')
3、内置Symbol值
* 除了定义自己使用的Symbol值以外,ES6还提供了11个内置的Symbol值,指向语言内部使用的方法。
- Symbol.iterator
* 对象的Symbol.iterator属性,指向该对象的默认遍历器方法
九.Iterator遍历器(迭代器)
概念: iterator 是一种接口机制,为各种不同的数据结构提供统一的访问机制
作用:
1、为各种数据结构,提供一个统一的、简便的访问接口;
2、使得数据结构的成员能够按某种次序排列
3、ES6创造了一种新的遍历命令 for…of循环,Iterator接口主要供for…of消费。
工作原理:
- 创建一个指针对象(遍历器对象),指向数据结构的起始位置。
- 第一次调用next方法,指针自动指向数据结构的第一个成员
- 接下来不断调用next方法,指针会一直往后移动,直到指向最后一个成员
- 每调用next方法返回的是一个包含value和done的对象,{value: 当前成员的值,done: 布尔值}
* value表示当前成员的值,done对应的布尔值表示当前的数据的结构是否遍历结束。
* 当遍历结束的时候返回的value值是undefined,done值为false
原生具备iterator接口的数据(可用for of遍历)
1、Array
2、arguments
3、set容器
4、map容器
5、String
let arr = [2,3,4,5]; console.log(arr); for(let item of arr){ console.log(item); } //for of 遍历实现原理,调用了数组Array里面的Symbol.iterator 接口 function iteratorUtil(target) { let index = 0; // 标识指针的起始位置 return { // 生成iterator遍历器对象 next: function () { return index < target.length?{value: target[index++], done: false}:{value: target[index++], done: true} } } } let iteratorObj = iteratorUtil(arr); // 生成iterator遍历器对象 console.log(iteratorObj.next());//{value:2 , done:false } console.log(iteratorObj.next());//{value:3 , done:false } console.log(iteratorObj.next());//{value:4 , done:false } console.log(iteratorObj.next());//{value:5 , done:false } console.log(iteratorObj.next());//{value:undefeined , done:true }
Iterator接口实现底层原理,修改底层源码,拿来给自己用
function iteratorUtil( ) { console.log('我的方法被调用了', this); // this是遍历的目标数组 // 缓存this let that = this; let index = 0; // 标识指针的起始位置 let keys = Object.keys(that);// 获取对象中所有key的数组 if(this instanceof Array){ // 遍历数组 return { // 生成iterator遍历器对象 next: function (){ // 可以使用缓存this解决this的指向问题,也可以使用箭头函数解决this的指向问题 return index < that.length?{value: that[index++], done: false}:{value: that[index++], done: true} } } }else {// 遍历对象 return { // 生成iterator遍历器对象 next: function (){ return index < keys.length?{value: that[keys[index++]], done: false}:{value: that[keys[index++]], done: true} } } } } Array.prototype[Symbol.iterator] = iteratorUtil; Object.prototype[Symbol.iterator] = iteratorUtil; let arr = [2,3,4,5]; for(let item of arr){ console.log(item); } // for of 消费 iterator接口 // 三点运算符消费 iterator接口 console.log(...arr); let obj = { username: 'kobe', age: 43 } for(let item of obj){ console.log(item); } // console.log(Object.keys(obj));输出--> (2) ["username", "age"]
十.class类详解
class类的理解
1.关键字
2.class类本质上是function
3.和函数表达式和函数声明式一样,class类也具:有类的表达式和类声明
4.类和模块的内部,默认就是严格模式,所以不需要使用 use strict 指定运行模式
类的声明:即class关键字后面跟一个类名
class Person { constructor(name, age) { this.name = name this.age = age } }
1.类声明不会被提升,需要先进行声明,再去访问,否则会报错,而函数声明则会提升
var person= new Person() class Person { constructor(name, age) { this.name = name this.age = age } } //报错:Person is not defined
2.不可重复声明
class Person {} class Person {} // 报错: Identifier 'Person' has already been declared
3.必须使用 new 调用,否则会报错,而普通构造函数不用 new 也可以执行
class Person { constructor(name, age) { this.name = name this.age = age } } Person() // 报错: Class constructor Person cannot be invoked without 'new'
类表达式:可以为匿名或命名
//匿名类 let Person = class { constructor(name, age) { this.name = name this.age = age } } //命名的类 let Person = class Person { constructor(name, age) { this.name = name this.age = age } }
类的方法
1.constructor 方法:类的默认方法,通过 new 命令生成对象实例时,自动调用该方法(默认返回实例对象 this)
class Person { constructor(name, age) { this.name = name // 默认返回实例对象 this this.age = age } toString() { console.log(this.name + ', ' + this.age) } }
注意:
1.在类中声明方法的时候,方法前不加 function 关键字
2.方法间不能加分号,否则会报错
3.一个类中有且只有一个 constructor 方法
2.static静态方法:1.使用static可以给 类对象 自身添加属性,2.通过类名调用,不能通过实例对象调用,否则会报错
class Person { static sum(x , y) { console.log(x + y) } } var p = new Person() Person.sum(1, 2) // 3 p.sum(1,2) //报错: p.sum is not a function
3.原型方法:1.类的所有方法都定义在类的 prototype 属性上面,在类的实例上面调用方法,其实就是调用原型上的方法,2.原型方法可以通过实例对象调用,但不能通过类名调用,会报错
class Person { constructor( ) {// 默认返回实例对象 this } sum( ) { } toString( ) { console.log(123) } } // 给 Person 的原型添加方法 Person.prototype.showName = function() { console.log('My name is kobe') } // 等同于 Person.prototype = { constructor( ) {}, sum( ) {}, toString( ) {} } var p = new Person() p.toString( ) // 123 p.toVal( ) // My name is kobe Person.toString( ) //报错: Person.toStringis not a function Person.toVal( ) //报错: Person.toVal is not a function
4.实例方法:通过实例对象调用,但同样不能通过类名调用,会报错
class Person { constructor() { this.sum = function(x, y) { console.log(x + y) } } } var p = new Person() p.sum(1,2) // 3 Person.sum(1,2) //报错: Person.sum is not a function
class类的继承
class Person { // 静态资源修饰符,使用static可以给 类对象 自身添加属性 static num = 123; // 类的构造方法 constructor(name, age){ console.log('--- constructor() ---'); this.name = name; this.age = age; } // 类的一般方法 showInfo(){ // userInfo console.log(this.name, this.age); } } console.log('--------------- 实现类的继承: extends -----------------'); // 定义一个 子类 // 回顾原型继承: 子类的原型 成为 父类的实例 // 子类的构造函数.prototype = new 父类的构造函数() class Child extends Person { // 子类的原型 成为 父类的实例 constructor(name, age, sex) { // super做的事情: 1. 调用父类的构造方法,2. 改变父类构造方法的this指向为子类的实例 super(name, age); // super调用父类的构造方法 this.sex = sex; } // 父类的方法重写:当父类原型的方法不能满足子类实例需求的时候 showInfo(){ console.log(this.name, this.age, this.sex); } } let child1 = new Child('xiaoming', 18, '男'); console.log(child1); child1.showInfo(); console.log('--------------- 回顾之前的继承方式 -----------------'); function Person1(name, age) { this.name = name; this.age = age; } Person1.prototype.showInfo = function(){ console.log(this.name, this.age); } let person2 = new Person1('kobe', 43); console.log(person2); person2.showInfo(); // 子类的原型 成为 父类的实例 Child1.prototype = new Person1(); Child1.prototype.constructor = Child1; function Child1(name, age, sex) { // this.name = name; // this.age = age; Person1.call(this, name, age); // 借用构造函数继承 this.sex = sex; } let child2 = new Child1('xiaoming', 18, '男'); console.log(child2); child2.showInfo();
ES6其他新增加属性?
十一:Set和Map数据结构
1.Set理解:
Set的存储结构与数组类似,Set中不允许 存放重复数据,而且没有下标,即无序不可重复的多个value的集合体
声明一个重复的数组,中有重复的数据
var arr = [111,444,333,111,222,333,444]
使用Set
var newArr = new Set(arr);//Set(4){111,444,333,222}
将结果变为数组,使用展开运算符
var arr1 = [...newArr]//(4){111,444,333,222}
Set方法:
1.添加元素–>add(value)
let a = new Set( ) a.add(8)//Set(1){8}
2.删除方法–>delete(value)
let a = new Set( ) v.add(8) v.add(6) v.delete(8)//Set(1){6}
3.判断 Set 中是否包含某个元素–>has(value)
let a = new Set() a.add(8) a.add(6) a.has(8)//true
4.clear()
let a = new Set([1,2,3,4,5]) a.clear( )
5.获取 Set 中元素个数–>size
let a = new Set( ) a.add(8) a.add(6)
6.遍历Set
for of 形式
let a = new Set([1,2,3,3,4,5]) for(let item of a) { console.log(item) }//1,2,3,4,5
十二.数组的去重
ES5数组去重:indexOf
let arr = [1,2,3,4,5,1,2,3,4]; console.log(arr.indexOf(9)); // -1 function uniqArr(arr) { let result = []; arr.forEach(function (item, index) { // 判断新的数组中是否包含原数组中的元素 if(result.indexOf(item) === -1){ result.push(item); } }) return result; } console.log(uniqArr(arr));
ES6数组去重: set容器
let arr = [1,2,3,4,5,1,2,3,4]; function uniqArr2(arr) { let set = new Set(arr); console.log(set); let result = []; for(let item of set){ result.push(item); } return result; }
代码优化
let arr = [1,2,3,4,5,1,2,3,4]; let uniqArr2 = arr => [...new Set(arr)]; console.log(uniqArr2(arr));
十三.检查数据类型通用方法
function checkoutType(target){ return Object.prototype.toString.call(target).slice(8,-1); }
十四.深度克隆_复制数据
- 基本数据类型存放的就是实际的数据,可直接复制
let number2 = 2;
let number1 = number2;
- 克隆数据:对象/数组
1、区别: 浅拷贝/深度拷贝
判断深拷贝还是浅拷贝:修改拷贝之后的数据会不会影响原数据,如果没有影响则是深拷贝
知识点:对象数据存放的是对象在栈内存的引用,直接复制的是对象的引用
let obj = {username: ‘kobe’}
let obj1 = obj; // obj1 复制了obj在栈内存的引用
2、常用的拷贝技术
1). arr.concat(): 数组浅拷贝
2). arr.slice(): 数组浅拷贝
3). JSON.parse(JSON.stringify(arr/obj)): 数组或对象深拷贝, 缺陷是不能处理函数数据
4). 浅拷贝包含函数数据的对象/数组
5). 深拷贝包含函数数据的对象/数组
3、手写一个深拷贝
function checkoutType(target) { return Object.prototype.toString.call(target).slice(8, -1); } function clone(target) { let result; // 最终加工拷贝完的数据 // 判断拷贝的数据是对象 || 数组 || 其他(基本数据类型,函数), 检测数据类型 let targetType = checkoutType(target); if(targetType === 'Array'){ result = [ ]; }else if(targetType === 'Object'){ result = { }; }else { return target; } // 拷贝 // arr = [1,2,3] ====> []arr2 // obj = {username: 'kobe'} ===> {}obj2 for(let item in target){ // item: 对象(key), 数组(index) // target[item] 可以获取对应的value let value = target[item]; // arr2[item] = arr[item] // 判断是否是引用数据类型,因为Array或者Object里面可能还会包含Array,Object if(checkoutType(value) === 'Object' || 'Array'){ result[item] = clone(value); }else { result[item] = value; } } return result; } let obj = {username: 'kobe', age: 43, sex: ['男', '女']}; let obj2 = clone(obj); console.log(obj, obj2); obj.username = 'wade'; console.log(obj, obj2); obj2.sex[0] = '混合'; console.log(obj, obj2);