一、let
1、声明变量
let a;
let b, c;//未赋值的输入undefined
let d = 3;
let e = 3, f = 'my is f', g = []
2、变量不能重复声明
//let重复声明会报错,但是使用var不会报错,使用let可以防止变量重复
let num = 100
let num2 = 200
3、块级作用域
//es5有三种作用域 全局,函数,eval(在es5严格模式里才会出现)
//if else while for 在这些里面都有块级作用域
{
let age = 18
}
console.log(age)//在外面不会输出结果(Uncaught ReferenceError: age is not defined),在括号里面才可以输出,这就是块级作用域,var可以输出
4、不存在变量提升
//console.log(myAge)
let myAge = 18
//let声明会报错,Uncaught ReferenceError: Cannot access 'myAge' before initialization
//var声明不会报错,会输出undefined
5、不影响作用域链
{
let age = 18
function fu() {
return function fu2() {
return function fu3() {
console.log(age)
}
}
}
fu()()()
}
二、const
//声明常量
const SCHOOL = "艾尼斯";
1、一定要赋初始值(没有初始值会报错)
2、一般常量使用大写(潜规则)
3、常量的值不能修改
4、块级作用域
{
const A = 'UZI'
console.log("4.块级作用域", A);
}
5、对于数组和对象的元素修改,不算做对常量的修改,不会报错
const TEAM = ['UZI', 'MLXG', 'Ming', 'Letme']
TEAM.push("Meiko")
console.log(TEAM);
三、解构赋值
ES6 允许按照一定模式从数组和对象中提取值,对变量进行赋值,这被称为解构赋值
1、数据解构赋值 (按照数组下标排序的)
const F4 = ['小沈阳', '刘能', '赵四', '宋小宝'];
let [xiao, liu, zhao, song] = F4;
console.log(xiao);
console.log(liu);
console.log(zhao);
console.log(song);
2、对象解构赋值(根据key值解构)
const PEOPLE = {
name: '小明',
age: '21',
speak() {
console.log("我可以说话");
}
}
let { name, age, speak } = PEOPLE
console.log(name);
console.log(age);
speak()
// let { speak } = PEOPLE
// speak()
四、模版字符串
ES6 引入新的声明字符串方式【``】 '' ""
1、声明
let str = `我是字符串`;
console.log("str", typeof str);
2、可以换行
let str2 = `<ul>
<li>沈腾</li>
<li>艾伦</li>
<li>魏翔</li>
</ul>`;
console.log(str2);
3、变量拼接
let name = '玛丽'
let str3 = `${name}在吃饭!`
console.log(str3);
五、对象简化写法
ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。
let name = "张三"
let speak = function () {
console.log("我可以说话!!");
}
const SCHOOl = {
name, //简写前name:name
speak, //简写前speak:speak
dance() {
console.log("我可以跳舞");
}
}
console.log(SCHOOl);
六、箭头函数
箭头函数适合与this 无关的回调,定时器,数组的方法回调
箭头函数不适合与this 有关的回调,事件回调,对象的方法
1、箭头函数基本形式
let fn = () => {
}
let func = (num) => num;
let func = () => num;
let sum = (num1,num2) => num1 + num2;
[1,2,3].map(x => x * x);
2、this指向
箭头函数this为父作用域的this,不是调用时的this
箭头函数的this永远指向其父作用域,任何方法都改变不了,包括call,apply,bind。
普通函数的this指向调用它的那个对象。
let person = {
name:'jike',
init:function(){
//为body添加一个点击事件,看看这个点击后的this属性有什么不同
document.body.onclick = ()=>{
alert(this.name);//?? this在浏览器默认是调用时的对象,可变的?
}
}
}
person.init();
上例中,init是function,以person.init调用,其内部this就是person本身,而onclick回调是箭头函数,
其内部的this,就是父作用域的this,就是person,能得到name。
let person = {
name:'jike',
init:()=>{
//为body添加一个点击事件,看看这个点击后的this属性有什么不同
document.body.onclick = ()=>{
alert(this.name);//?? this在浏览器默认是调用时的对象,可变的?
}
}
}
person.init();
上例中,init
为箭头函数,其内部的this为全局window, onclick
的this也就是 init
函数的this,也是window,
得到的 this.name
就为undefined。
3、箭头函数不能作为构造函数,不能使用new
Demo1
let Person = (name, age) => {
this.name = name
this.age = age
}
let me = new Person('xiao', 30)
console.log(me);
Demo2
//构造函数如下:
function Person(p){
this.name = p.name;
}
//如果用箭头函数作为构造函数,则如下
var Person = (p) => {
this.name = p.name;
}
由于this必须是对象实例,而箭头函数是没有实例的,此处的this指向别处,不能产生person实例,自相矛盾。
4、箭头函数没有arguments,caller,callee
箭头函数本身没有arguments,如果箭头函数在一个function内部,它会将外部函数的arguments拿过来使用。
箭头函数中要想接收不定参数,应该使用rest参数...解决。
let B = (b)=>{
console.log(arguments);
}
B(2,92,32,32); // Uncaught ReferenceError: arguments is not defined
let C = (...c) => {
console.log(c);
}
C(3,82,32,11323); // [3, 82, 32, 11323]
5、箭头函数通过call和apply调用,不会改变this指向,只会传入参数
let obj2 = {
a: 10,
b: function(n) {
let f = (n) => n + this.a;
return f(n);
},
c: function(n) {
let f = (n) => n + this.a;
let m = {
a: 20
};
return f.call(m,n);
}
};
console.log(obj2.b(1)); // 11
console.log(obj2.c(1)); // 11
6、箭头函数没有原型属性
var a = ()=>{
return 1;
}
function b(){
return 2;
}
console.log(a.prototype); // undefined
console.log(b.prototype); // {constructor: ƒ}
7、箭头函数不能作为Generator函数,不能使用yield关键字
8、箭头函数返回对象时,要加一个小括号
var func = () => ({ foo: 1 }); //正确
var func = () => { foo: 1 }; //错误
9、箭头函数在ES6 class中声明的方法为实例方法,不是原型方法
//deom1
class Super{
sayName(){
//do some thing here
}
}
//通过Super.prototype可以访问到sayName方法,这种形式定义的方法,都是定义在prototype上
var a = new Super()
var b = new Super()
a.sayName === b.sayName //true
//所有实例化之后的对象共享prototypy上的sayName方法
//demo2
class Super{
sayName =()=>{
//do some thing here
}
}
//通过Super.prototype访问不到sayName方法,该方法没有定义在prototype上
var a = new Super()
var b = new Super()
a.sayName === b.sayName //false
//实例化之后的对象各自拥有自己的sayName方法,比demo1需要更多的内存空间
因此,在class中尽量少用箭头函数声明方法。
10、多重箭头函数就是一个高阶函数,相当于内嵌函数
const add = x => y => y + x;
//相当于
function add(x){
return function(y){
return y + x;
};
}
11、箭头函数常见错误
let a = {
foo: 1,
bar: () => console.log(this.foo)
}
a.bar() //undefined
bar函数中的this指向父作用域,而a对象没有作用域,因此this不是a,打印结果为undefined
function A() {
this.foo = 1
}
A.prototype.bar = () => console.log(this.foo)
let a = new A()
a.bar() //undefined
原型上使用箭头函数,this指向是其父作用域,并不是对象a,因此得不到预期结果
12、箭头函数的简写
let add = (n) => { return n + n }
//参数只有一个的时候,可以省略小括号,
let add = n => { return n + n }
//代码体只有一行的时候可以省略大括号,return也必须省略,执行结果就是函数的返回值
let add = n => n + n
七、函数参数默认值设置
1、形参初始值具有默认的参数,一般位置要靠后(潜规则)
function add(a, b, c = 5) {
return a + b + c
}
console.log(add(1, 2));//如果传值以传的值为准,如果不传以默认值为准
2、与解构赋值结合
function connect({ host = "192.168.1.1", username, password, port }) {
console.log(host);
console.log(username);
console.log(password);
console.log(port);
}
connect({ host: "baidu.com", username: 'root', password: 'root', port: 8080 })
八、rest参数
1、ES6 rest参数,用于获取函数的实参,用来代替arguments
//ES5 获取实参方式
function add() {
console.log("ES5 获取实参方式", arguments);
}
add(9, 4, 6)
//ES6 获取实参方式
function add2(...args) {
console.log("ES6 获取实参方式", args);
}
add2(9, 4, 6)
2、rest参数必须放到参数最后
function num(a, b, ...args) {
console.log(a);
console.log(b);
console.log(...args);
}
num(9, 4, 6, 1, 6, 8, 9)
九、扩展运算符
...
扩展运算符能将[数组] 转换为逗号分隔的[参数序列]
const arr = ['张三', '李四', '王五'];
const arr2 = ['赵六', '孙七', '钱八'];
console.log(arr);//["张三", "李四", "王五"]
console.log(...arr);//张三 李四 王五
1、数组合并
console.log("1.concat数组合并", arr.concat(arr2));
console.log("2.扩展运算符数组合并", [...arr, ...arr2]);
2、数组克隆
const arr3 = [...arr2];
console.log("数组克隆", arr3);
3、伪数组转为真正的数组
const divs = document.querySelectorAll('div')
console.log(...divs);
十、Symbol
ES6 引入了一种新的原始数据类型 Symbol ,表示独一无二的值,他是javascript语言的第七种数据类型,是一种类似于字符串的数据类型
- Symbol的值是唯一的,用来解决命名冲突的问题
- Symbol的值不能与其他数据进行运算
- Symbol 定义的对象属性不能使用for...in 循环遍历,但是可以使用Reflect.ownkeys来获取对象的所有键名
ES6 引入了一种新的原始数据类型 Symbol ,表示独一无二的值,最大的用法是用来定义对象的唯一属性名。
//ES6 数据类型除了 Number 、 String 、 Boolean 、 Object、 null 和 undefined ,还新增了 Symbol 。
// USONB ===>you are so niubility
// u undefined
// s String Symbol
// o Object
// n Number null
// b Boolean
1、声明
let s = Symbol();
console.log(typeof s);//symbol
console.log(typeof (s));//symbol
2、相同参数 Symbol() 返回的值不相等
let s1 = Symbol('ANS');
let s2 = Symbol('ANS');
console.log(s1 === s2);//false
3、用for创建数据类型是一致的
虽然这样保证了Symbol的唯一性,但我们不排除希望能够多次使用同一个symbol值的情况。
为此,官方提供了全局注册并登记的方法:Symbol.for()
let s3 = Symbol.for('ANS');//检测到未创建后新建
let s4 = Symbol.for('ANS');//检测到已创建后返回
console.log(s3 === s4);//true
4、不能与其他数据类型进行运算
console.log(s1 + 100);
console.log(s1 > 100);
console.log(s1 + s3);
5、当symbol值作为对象的属性名的时候,不能用点运算符获取对应的值。
let name = Symbol();
let person = {
[name]: "张三"
};
console.log(person[name]);//张三
console.log(person.name);//undefined
symbol
类型的值作为对象的属性名的时候,一定要用中括号[ ]不能用点运算符,因为用点运算符的话,
会导致 javascript
把后面的属性名为理解为一个字符串类型,而不是 symbol
类型
let name = Symbol();
let person = {};
person.name = "张三";
console.log(person[name]); //结果:undefined
console.log(person['name']); //结果:张三
console.log(person.name); //结果:张三
6、向对象中添加方法
向对象中添加一个独一无二的方法,methods.speak
对应都是Symbol数据类型,可以向 people 对象中添加一个独一无二的方法,不会破坏原有的方法
关于Symbol创建对象函数时的调用方法
let people = {
name: 'ZS',
age: 19,
speak() {
console.log("说话");
},
eat() {
console.log("吃饭");
}
}
let methods = {
speak: Symbol(),
eat: Symbol(),
}
people[methods.speak] = function () {
console.log("shuohua");
}
people[methods.eat] = function () {
console.log("chifan");
}
people.eat()//吃饭
people[methods.eat]()//chifan
第二种方式:
let zibao = Symbol();
let say = Symbol();
let youxi = {
name: '狼人杀',
[say]: function () {
console.log("我可以发言");
},
[zibao]: function () {
console.log("我可以自爆");
}
}
console.log(youxi);
youxi[say]()
youxi[zibao]()
第三种方式
let youxi = {
name: '狼人杀',
[Symbol('say')]() {
console.log("我可以发言");
},
[Symbol('zibao')]() {
console.log("我可以自爆");
}
}
// 第一种调用方法
const langrensha=Object.getOwnPropertySymbols(youxi)
youxi[langrensha[0]]()
youxi[langrensha[1]]()
// 第二种调用方法
const langrensha2=Reflect.ownKeys(youxi)
youxi[langrensha2[1]]()
youxi[langrensha2[2]]()
7 、symbol 内置值
内置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环境排除。 |
这些属性都是symbol里面的一个属性,是固定写法,他们整体又作为我们对象的属性去设置,来改变我们对象在特定场景下的表现,说白了就是扩展对象功能的
Symbol.hasInstance 使用
检测类型
class Person {
static [Symbol.hasInstance](param) {
console.log(param);
console.log("我用来检测类型");
return true;
}
}
let o=[2,3,4];
//instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
console.log(o instanceof Person);
Symbol.isConcatSpreadable 使用
设置数组是否展开
let arr = [1, 2, 24, 23]
let arr2 = [42, 25, 24, 235]
//控制arr2是否可以展开
arr2[Symbol.isConcatSpreadable] = false
console.log(arr.concat(arr2)) //(5)[1, 2, 24, 23, Array(4)]
十一、迭代器
迭代器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作。
-
ES6 创造了一种新的遍历命令 for...of循环,Iterator接口主要提供 for...of 消费
-
原生具备Iterator 接口的数据(可用for of 遍历)
- Array
- Arguments
- Set
- Map
- String
- TypedArray
- NodeList
-
工作原理
- 创建一个指针对象,指向当前数据结构的起始位置
- 第一次调用对象的next方法,指针自动指向数据结构的第一个成员
- 接下来不断调用next方法,指针一直往后移动,直到指向最后一个成员
- 每调用next方法返回一个包含 value 和 done 属性的对象
注意:需要自定义遍历数据的时候,要想到迭代器
let arr = ['zhansgan', 'lisi', 'wangwu']
for (let a in arr) {
// 使用for...in a保存的是键名
console.log(a);//0 1 2
}
for (let a of arr) {
// 使用for...of a保存的是键值
console.log(a);//zhansgan lisi wangwu
}
//for in和 for of 区别 第一个前面常量保存是键名,后面保存的是键值
//为什么数组可以使用for of 遍及呢,只要他对象里面有symbol.iteratoa属性就可以 我们来看一看,只要打印这个变量看一下就可以,他的值是一个函数
// console.log(arr);
let iterator=arr[Symbol.iterator]();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
迭代自定义遍厉对象例子
要求:使用遍及对象,让他返回数组中的每个成员,按照自己意愿遍厉,按照自己意愿去做这个事情
const banji = {
name: '终极一班',
stus: [
"zhangsan",
"lisi",
"wangwu",
"zhaoliu"
],
}
//要求:使用遍及对象,让他返回数组中的每个成员,按照自己意愿遍厉,按照自己意愿去做这个事情
for (let v of banji) {
console.log(v);
}
//直接只用for循环不行
需要添加 Symbol.iterator
const banji = {
name: '终极一班',
stus: [
"zhangsan",
"lisi",
"wangwu",
"zhaoliu"
],
[Symbol.iterator]() {
//索引变量
let index = 0;
let _this = this;
return {
next: function () {
if (index < _this.stus.length) {
const result = { value: _this.stus[index], done: false };
index++;//下标自增
return result;//返回结果
} else {
return { value: undefined, done: true };
}
}
}
}
}
for (let v of banji) {
console.log(v);
}
十二、生成器
生成器他是一个特殊的函数
作用:用来执行异步编程。 之前用的是纯回调函数比如 node 里面fs模块
ajax
monhobd数据库
他们都是异步的我们用的方式都是回调函数一层调一层,会形成回调地狱。 生成器就是对异步编程的一种解决方案
function* gen() {
console.log("hello");
}
// gen().next();
let iterator=gen();
iterator.next();
1.声明特殊,
需要加上 `*` 号( `*` 位置靠前、靠后、居中都可以)
2.执行特殊
直接调用不可以,需要.next()
- 还有一个特殊,就是在生成器函数中可以出现
yield
语句后面使用自变量或者表达式,yield
语句他是函数代码的分隔符,下图是分成四块
他不想之前一下子执行完成,是通过next方法向下执行的
function* gen() {
console.log(11);
yield '一只没有耳朵';
console.log(22);
yield '一只没有尾巴';
console.log(33);
yield '真奇怪';
console.log(44);
}
let iterator = gen();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
//iterator既然他是一个对象,可以使用for of来做遍及,每一次调用返回的结果是yield后面的表达式或者是自定义变量
for (let v of gen()) {
console.log(v);
}
生成器函数传参 第二次调用将作为第一次 yeild
语句整体的返回结果也就是说传的参数将作为上一个 yeild
语句的返回结果
function* gen(arg) {
console.log(arg);//AAA
let one = yield '一只没有耳朵';
console.log(one);//BBB
let two = yield '一只没有尾巴';
console.log(two);//CCC
let three = yield '真奇怪';
console.log(three);//DDD
}
//执行获取迭代器对象
let iterator = gen("AAA")
console.log(iterator.next());//{value: "一只没有耳朵", done: false}
//next方法可以传入实参
console.log(iterator.next("BBB"));//{value: "一只没有尾巴", done: false}
console.log(iterator.next("CCC"));//{value: "真奇怪", done: false}
console.log(iterator.next("DDD"));//{value: undefined, done: true}
Demo1 例子:
要求:1s后控制台输出111 2s后控制台输出222 3s后控制台输出333
原始方法:
//形成回调地狱
setTimeout(() => {
console.log("111");
setTimeout(() => {
console.log("222");
setTimeout(() => {
console.log("333");
}, 3000)
}, 2000)
}, 1000)
使用生成器
function one() {
setTimeout(() => {
console.log("111");
iterator.next();
}, 1000)
}
function two() {
setTimeout(() => {
console.log("222");
iterator.next();
}, 2000)
}
function three() {
setTimeout(() => {
console.log("333");
iterator.next();
}, 3000)
}
function* gen() {
yield one();
yield two();
yield three();
}
//调用生成器函数
let iterator = gen();
iterator.next();
这样看着给同步的代码是一样的,实际上执行的时候他是异步的,这是生成器函数在异步任务的表现,成功解决了回调地狱的问题
Demo2 例子:
要求:模拟获取 (传参数) 用户数据 订单数据 商品数据
function getUser() {
setTimeout(() => {
let data = "用户数据"
iterator.next(data);//传参数
}, 1000)
}
function getOrders() {
setTimeout(() => {
let data = "订单数据"
iterator.next(data);//传参数
}, 1000)
}
function getGoods() {
setTimeout(() => {
let data = "商品数据"
iterator.next(data);//传参数
}, 1000)
}
function* gen(sss) {
console.log(sss);
let users = yield getUser();
console.log(users);
let orders = yield getOrders();
console.log(orders);
let goods = yield getGoods();
console.log(goods);
}
//调用生成器函数
let iterator = gen("传参数");//传参数
iterator.next();
这样看着给同步的代码是一样的,实际上执行的时候他是异步的,这是生成器函数在异步任务的表现,成功解决了回调地狱的问题
十三、promise
promises 是ES6引入的异步编程的新解决方案,语法上Promise是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果
promise是es6非常重要的一个内容,也是面试经常问到的点,异步编程主要指io的代码,包括文件io,数据库io ,网络请求等
//实例化Promise对象
const p = new Promise(function (resolve, reject) {
setTimeout(function () {
// let data = '数据库中的数据'
// resolve(data)
let err = '读取数据失败'
reject(err)
}, 1000)
});
//调用promise 对象的then方法
p.then(function (value) {
console.log(value);
}, function (reason) {
console.error(reason);
})
promise
里面封装一个异步操作,用定时器模拟一个异步操作,得到数据后调用 resolve
和 reject
两个函数来改变 promise
对象的状态。
调用 resolve
promise对象状态(初始化,成功,失败)就会变成成功,成功就会调用 promise
对象的 then
方法,then
接受两个参数,这两个参数都是函数(函数带个参数),一个成功,一个失败,成功会调用then方法第一个回调, 失败调用reject
会调用then第二个回调。 通过这样方式,我们就吧一个异步任务封装在promise对象里面 ,而且通过resolve reject这两个函数来改变promise对象的状态,改变状态后来调用then方法里面的回调
封装ajax请求
接口地址:https://api.apiopen.top/getJoke
原始封装方法
//1.创建对象
const xhr = new XMLHttpRequest();
//2.初始化
xhr.open("GET", "https://api.apiopen.top/getJoke");
//3.发送
xhr.send();
//4.绑定事件,处理响应结果
xhr.onreadystatechange = function () {
//判断
if (xhr.readyState === 4) {
//判断响应状态码 200-299
if (xhr.status >= 200 && xhr.status < 300){
//表示成功
console.log(xhr.response);
}else{
//如果失败
console.error(xhr.status);
}
}
}
promise 封装方法
具体百度查询吧
十四、set
ES6提供了新的数据结构 Set(集合)。它类似于数组,但成员的值都是唯一的。集合实现了iterator接口。所以可以使用扩展运算符和for of 进行遍历。
集合的属性和方法
- size 返回集合的元素个数
- add 增加一个新元素,返回当前集合
- delete 删除元素,返回boolean 值
- has 检测集合中是否包含某个元素,返回boolean值
let s = new Set();//声明一个set
let s2 = new Set(['大事', '小事', '好事', '坏事']);
console.log(s2.size);//获取个数
s2.add('喜事')//添加
s2.delete('坏事')//删除
console.log(s2.has('好事'));//检测是否包含
s2.clear()//清空
console.log(s2);
// for(let v of s2){
// console.log(v);
// }
1.数据去重复
let arr=[1,2,3,4,5,1,2,3,7,11,66];
let result=[...new Set(arr)]
console.log(result);//[1, 2, 3, 4, 5, 7, 11, 66]
2.交集
交集 (数组1和数组2 都有的) 中括号是转化成数组,通过集合吧元素去重,去重以后还是个集合,本身是个集合转化成数组
let arr=[1,2,3,4,5,1,2,3,7,11,66];
let arr2 = [4, 5, 6, 5, 6,10]
let result = [...new Set(arr)].filter(item => {
let s2 = new Set(arr2);//4,5
if (s2.has(item)) {
return true;
} else {
return false;
}
})
//简写
//let result = [...new Set(arr)].filter(item => new Set(arr2).has(item));
console.log(result);//[4, 5]
3.并集 (两个数据合并去重)
let arr=[1,2,3,4,5,1,2,3,7,11,66];
let arr2 = [4, 5, 6, 5, 6,10]
let union=[...new Set([...arr,...arr2])];
console.log(union);//[1, 2, 3, 4, 5, 7, 11, 66, 6, 10]
4.差集(arr2 和 arr 相差的,也就是说arr2 不包含的)
let arr=[1,2,3,4,5,1,2,3,7,11,66];
let arr2 = [4, 5, 6, 5, 6,10]
let diff = [...new Set(arr)].filter(item => !(new Set(arr2).has(item)));
console.log(diff);// [1, 2, 3, 7, 11, 66]
十五、Map
ES6 提供了 Map数据结构。它类似于对象,也是键值对的集合。但是"键"的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map也实现了iterator接口,所以可以使用扩展运算符和for of 进行遍历。
Map 的属性和方法
size 返回Map 的元素个数
set 增加一个新元素,返回当前Map
get 返回键名对象的键值
has 检测Map 中是否包含某个元素,返回boolean值
clear 清空集合,返回undefined
map其实就是升级版的对象,原来key只能是字符串,现在可以设置成对象,
1.声明
let m = new Map()//声明
2.添加元素
m.set('name','张三')
m.set('speak',function(){
console.log("说话");
})
let key={
school:'ANS'
}
m.set(key,['北京','上海','天津'])
console.log(m);
3.获取个数
console.log(m.size);
4.删除
m.delete('name')
5.获取
console.log(m.get('speak'));
console.log(m.get(key));
6.清空
m.clear();
7.遍历
for(let v of m){
console.log(v);
}
十六、class
ES5通过构造函数实例化对象的语法
父类
function Phone(brand, price) {
this.brand = brand;
this.price = price;
}
//通过原型对象添加方法
Phone.prototype.call = function () {
console.log("我可以打电话");
}
子类
//智能手机
function SmartPhone(brand, price, color, size) {
Phone.call(this, brand, price);//call改变this指向
this.color = color;
this.size = size;
}
//让子类的原型对象指向父类实例, 这样一来在 SmartPhone 实例中找不到的属性和方法就会到原型对象(父类实例)上寻找
SmartPhone.prototype = new Phone();
// 根据原型链的规则,顺便绑定一下 constructor, 这一步不影响继承, 只是在用到constructor时会需要
SmartPhone.prototype.constructor = SmartPhone;
// SmartPhone.prototype = {
// constructor: Phone,
// photo: function () {
// console.log("我可以拍照");
// },
// playGame: function () {
// console.log("玩游戏");
// }
// }
SmartPhone.prototype.photo = function () {
console.log("我可以拍照");
}
SmartPhone.prototype.playGame = function () {
console.log("玩游戏");
}
调用
let xm = new SmartPhone("小米", 1499, '红色', '5.5inch')
console.log(xm);
xm.call()
// let huawei = new Phone("华为", 2000)
// huawei.call()
// console.log(huawei);
ES6 类
父类
class Shouji {
//构造方法,名字不能修改
constructor(brand, price) {
this.brand = brand;
this.price = price;
}
//方法必须使用该语法,不能使用ES5的对象完整形式
call() {
console.log("打电话");
}
}
子类
class Znsj extends Shouji {
constructor(brand, price, color, size) {
// super 就是父类的constructor方法 通过这样的方式来完成调用
super(brand, price)//Phone.call(this, brand, price);
this.color = color;
this.size = size;
}
photo() {
console.log("拍照");
}
playGame() {
console.log("玩游戏");
}
}
调用
let znsj = new Znsj("魅族", 1500,'白色','5.5inch')
console.log(znsj);
znsj.playGame()
let mz = new Shouji("魅族", 1500)
// console.log(mz);
十七、数值扩展
二进制和八进制
let b = 0b1010;
let o = 0o777;
let d = 100;
let x = 0xff;
console.log(b);
console.log(o);
console.log(d);
console.log(x);
Number.EPSILON
//Number.EPSILON 是 javascript 表示的最小精度
//EPSILON 属性的值接近于 2.2204460492503130808472633361816E-16
function equal(a, b) {
if (Math.abs(a - b) < Number.EPSILON) {
return true
} else {
return false
}
}
// console.log(0.1+0.2===0.3);
console.log(equal(0.1 + 0.2, 0.3));
// console.log(Math.abs(-5));
Number.isFinite
//Number.isFinite 检测一个数值是否为有限数
console.log(Number.isFinite(1000));
console.log(Number.isFinite(1000/0));
console.log(Number.isFinite(Infinity));
Number.isNaN
//Number.isNaN 检测一个数值是否为NaN
console.log(Number.isNaN(123));
Number.parseInt(Number.parseFloat)
//Number.parseInt Number.parseFloat 字符串转整数
console.log(Number.parseInt('524545sadasd'));
console.log
Number.isInteger
//Number.isInteger 判断是否为整数
console.log(Number.isInteger(5));
console.log(Number.isIn
Math.trunc
// Math.trunc 将数字的小数部分抹掉
console.log(Math.trunc(3.8));
Math.sign
// Math.sign 判断一个数到底为正数 负数 还是零
console.log(Math.sign(100));
console.log(Math.sign(0));
console.log(Math.sign(-100));
十八、get set 设置
ES5 也使用过,对对象属性进行方法的绑定,当对某一个属性进行获取时,去执行get对应函数
Set 必须传个参数,Get 调用这个,就会执行里面代吗,这个返回值就是这个属性的值
使用场景,get通常对动态属性进行封装,他这个属性是变化的,比如求总数,求平均数,他的值是随着数据变化而变化,而不是固定的,这时候我们通过get函数动态计算结果,就变得非常方便
Set 添加更多控制,和判断 比如传过来的这个值是否合法的,我要num类型你给我传个字符串那肯定不行啊,这个时候可以通过判断成功就赋值,失败就不赋值
class Phone {
get price() {
console.log('价格属性被读取了');
return 'iloveyou'
}
set price(newVal) {
console.log('价格属性被修改了');
}
}
//实例化对象
let p = new Phone();
console.log(p.price);
p.price = '3000'