ES6&高级JS

前言

在这里插入图片描述

ES6

拓展运算符

在这里插入图片描述

在这里插入图片描述

能将数组转换为逗号分隔的 [参数序列]
与rest参数有所不同
扩展运算符放在了函数调用的实参立里面
而rest放在了函数声明的形参位置

1.应用

1)数组的合并

在这里插入图片描述

2)数组的克隆(浅拷贝)

在这里插入图片描述

3)将伪数组转为真正的数组

rest参数

Es6引入rest参数 用于获取函数的实参,用来代替arguments
Es5的获取方式不是返回一个数组,而是一个object对象
利用rest获取的是一个数组,可以用数组的各种方法

注意:rest参数必须放在参数最后
在这里插入图片描述

Symbol

Es6引入了一种新的原始数据类型Symbol,表示独一无二的值,它是javascript语言的第七种数据类型,是一种类似于字符串的数据类型

1.Symbol()和Symbol.for()

在这里插入图片描述
在这里插入图片描述
Symbol 函数的参数只是表示对当前Symbol值的描述,因此相同参数的Symbol函数的返回值是不相等的,symbol.for访问的是一个全局的Symbol表,如果有了就访问对应对象,没有就重新创建

2.特点

● symbol的值是唯一的
● Symbol值不能与 其他数据类型进行运算
● Symbol定义的对象属性不能使用for…in…循环遍历,但是可以使用Reflect.ownKeys来获取对象所有的键名

3.对象添加Symbol类型的属性

 //向对象中添加方法 up down
  let game = {
    name:'俄罗斯方块',
    up: function(){},
    down: function(){}
  };
  // 声明一个对象
  let methods = {
    up: Symbol(),
    down: Symbol()
  };
  game[methods.up] = function(){
    console.log("我可以改变形状");
  }
  game[methods.down] = function(){
    console.log("我可以快速下降!!");
  }
game[methods.up]();
game[methods.down]();
let youxi = {
    name:"狼人杀",
    [Symbol.for('say')]: function(){
      console.log("我可以发言")
    },
    [Symbol.for('zibao')]: function(){
      console.log('我可以自爆');
    }
  }
  console.log(youxi);
  //Symbol.for('say')产生的标识是唯一的
//所以可以调用定义在youxi里面的[Symbol.for('say')]方法
  youxi[Symbol.for('say')]();
  youxi[Symbol.for('zibao')]();
let youxi1 = {
    name:"狼人杀",
    [Symbol('say')]: function(){
      console.log("我可以发言")
    },
    [Symbol('zibao')]: function(){
      console.log('我可以自爆');
    }
  }
  youxi[Reflect.ownKeys(youxi)[1]]();
  youxi[Reflect.ownKeys(youxi)[2]]();
  //静态方法 Reflect.ownKeys() 返回一个由目标对象自身的属性键组成的数组。
  //这种情况无法直接调用,因为每次 Symbol("xxx") 都是不一样的
  //因为Symbol('say')生成的标识不是唯一的,所以定义的在youxi1里面的[Symbol('say')]是无法调用的
//除非把Symbol('say')放块外面定义,并用变量保存
  console.log(Symbol('say')===Symbol('say')); //false
  // youxi1[Symbol('say')](); //报错
  // youxi1[Symbol('zibao')](); //报错

4.Symbol 的内置值在这里插入图片描述

1)hasInstance

在这里插入图片描述

2)isConcatSpreadable

在这里插入图片描述

Iterator 迭代器

1.作用

Iterator 的作用有三个:
一是为各种数据结构,提供一个统一的、简便的访问接口;
二是使得数据结构的成员能够按某种次序排列;
三是 ES6 创造了一种新的遍历命令for…of循环,Iterator 接口主要供for…of消费。
在这里插入图片描述

2.遍历过程

1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
(2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
(3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
(4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。
每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。

在这里插入图片描述

生成器

1.特点

Generator 函数 是一个普通函数,但是有两个特征

1.function 关键字与函数名之间有一个星号
函数体内部使用yield表达式,定义不同的内部状态

理解 Generator 调用后该函数并不执行,返回的也不是函数运行的结果,而是一个指向内部状态的指针对象,必须调用next()方法才可执行,yield表达式是暂停执行的标记(即每次执行都会执行yield之间的语句,遇到yield会暂停,next方法会恢复执行)

在这里插入图片描述
在这里插入图片描述

上面代码中 yield后面的表达式1+2 不会立即求值,只会在next方法将指针移到这一句时才会求值

在这里插入图片描述

如果在控制台打印 h.next() 不仅会将yield之间的表达式执行,还会打印出value值和done

return 和yield的区别

相似之处:都能返回紧跟在语句后面的那个表达式的值
区别:每次遇到yield 函数暂停执行,下一次再从该位置继续向前执行,而return 语句不具备位置记忆的功能,一个函数里面,只能执行一次(或者说一个)return 语句,但是可以执行多次(或者说多个)yield表达式。正常函数只能返回一个值,因为只能执行一次return;Generator函数可以返回一系列的值,因为可以有任意多个yield

注意:yield表达式如果在另一个表达式中,必放在圆括号里
在这里插入图片描述
forEach函数不能使用yield,因为forEach函数是一个普通函数,可以使用for循环和for of
在这里插入图片描述

2.生成器函数的参数传递

1)next参数

next方法可以传递实参
:第二个next()传入的实参将作为第一个yield语句的返回值
在这里插入图片描述
在这里插入图片描述

集合

1.Set

类似于数组但是成员值唯一,该集合实现了Iterator接口,所以可以是使用拓展运算符和for…of 进行遍历

1)集合的属性和方法

(1)size 返回元素的个数
(2)add 增加一个新元素,返回当前集合
(3)delete 删除元素,返回布尔
(4)has()判断元素是否在集合中,返回布尔
(5)clear()清空所有成员,没有返回值

2)Object 结构与Set结构的写法不同

在这里插入图片描述

3)将Set转换为数组(将数组去重的方法之一)
const items = new Set([1, 2, 3, 4, 5]);
const array = Array.from(items);

数组去重

function dedupe(array) {
  return Array.from(new Set(array));
}

dedupe([1, 1, 2, 3]) // [1, 2, 3]
let arr=[1,2,3,4,5,4,3,2,1];
//new Set(arr)可将其转为类数组,而且里面元素是不重复的,
//...是扩展运算符 将类数组转为数组
let result=[...newSet(arr)];
console.log(result);
4)4个遍历方法

(1)keys() 返回键名的遍历器
(2)values() 返回键值
(3)entries()返回键值对
(4)forEach()使用回调函数遍历每个成员

注意:由于Set方法没有键名只有键值(或者是键名和键值是同一个值),所以keys等于values

let set = new Set(['red', 'green', 'blue']);

for (let item of set.keys()) {
  console.log(item);
}
// red
// green
// blue

for (let item of set.values()) {
  console.log(item);
}
// red
// green
// blue

for (let item of set.entries()) {
  console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
5)实践
(1)求交集

filter()方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
item指调用者的所有的元素,方法中的是条件,filter会返回一个新数组,数组中的元素均满足该条件

let arr2 = [4, 5, 6, 5, 6];
    let s2 = new Set(arr2);
    let result2 = [...new Set(arr2)].filter((item) => s2.has(item));
    console.log(result2); //[4,5,6]
(2)求并集
let union=[...new Set([...arr,...arr2])]
(3)求差集
// arr2的差集(arr中有arr2没有的)
let diff = [...new Set(arr)].filter((item) => !new Set(arr2).has(item));
console.log(diff);

2.Map

类似于对象,也是键值对的集合,但是键的范围不限于字符串,各种类型的值(包括对象)都可以当做键,Map也实现了iterator接口,所以可以使用拓展运算符和for…of进行遍历

1)属性和方法

(1)size 返回MAP元素的个数
(2)set 增加一个元素,返回当前的map
(3) get 返回键名对象的键值
(4)has 检查Map中是否包含某个元素,返回Boolean值
(5)clear 清空集合 返回Undefined

在这里插入图片描述

Class

1.简介

ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。
基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰

2.使用

// 当使用new加类名的时候,就会自动执行实例对象的constructor方法
class Phone {
  // 构造方法,名字固定
  constructor(brand, price) {
    this.brand = brand;
    this.price = price;
  }
  // 方法必须使用该语法,不能使用ES5的对象完整形式
  call() {
    console.log("我可以打电话");
  }
}
let onePlus = new Phone("1+", 1999);
onePlus.call();

3.静态成员

注意:对于static 标注的属性和方法。它属于类,并不属于创建的实例对象
如果静态方法包含this关键字,这个this指的是类,而不是实例

所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。

4.类的继承

父类的静态方法可以被子类继承,静态方法也可以从super对象上调用
注意:父类中所有的属性和方法,都会被子类继承,除了私有的属性和方法(除非使用特殊方法)

class Phone {
  constructor(brand, price) {
    this.brand = brand;
    this.price = price;
  }
  call() {
    console.log("打电话");
  }
}
class smartPhone extends Phone {
  //构造方法
  constructor(brand, price, color, size) {
    super(brand, price);
    this.color = color;
    this.size = size;
  }
  photo() {
    console.log("拍照");
  }
  playGame() {
    console.log("玩游戏");
  }
}
const xiaomi = new smartPhone("小米", 799, "黑色", "4.7英尺");
console.log(xiaomi);
xiaomi.call();



1)私有属性和私有方法的继承

子类无法继承父类的私有属性,或者说私有属性只能定义它的class里面使用
如果父类定义了私有属性的读写方法,子类就可以通过这些方法,读写私有属性

class Foo {
  #p = 1;
  getP() {
    return this.#p;
  }
}

class Bar extends Foo {
  constructor() {
    super();
    console.log(this.getP()); // 1
  }
}

5.get和set

注意:与java有所不同

class Phone {
      a = 1;
      get price() {
        console.log("价格属性被读取了");
        return this.a;
      }
      set price(value) {
        console.log("setter:", value);
        this.a = value * 2;
      }
    }
    let s = new Phone();
    // getter 是取值函数,setter是存值函数

    s.price = 2000; //存值时调用setter方法
    console.log(s.price); //取值时调用getter方法

在这里插入图片描述

6.class的私有属性

在属性名之前用# 表示
只能在类的内部使用,如果在外部使用会报错

由于#是属性名的一部分,因此#x和x是两个不同的属性
这种方法不仅可以写私有属性,还可以写私有方法

另外私有属性也可以设置getter和setter方法

class Counter {
  #xValue = 0;

  constructor() {
    console.log(this.#x);
  }

  get #x() { return this.#xValue; }
  set #x(value) {
    this.#xValue = value;
  }
}

Module

模块功能主要由两个命令构成 :export 和import
export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。

1.export 命令

一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量

范例:

//proifile.js文件

//法一
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;


//法二(推荐)
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export { firstName, lastName, year };

// main.js文件
import { firstName, lastName, year } from './profile.js';

function setName(element) {
  element.textContent = firstName + ' ' + lastName;
}

export 命令还可以输出函数和类

export function multiply(x, y) {
  return x * y;
};

通常情况下,export输出的变量就是本来的名字,但是可以使用as关键字重命名

//代码使用as关键字,重命名了函数v1和v2的对外接口。
//重命名后,v2可以用不同的名字输出两次。

function v1() { ... }
function v2() { ... }

export {
  v1 as streamV1,
  v2 as streamV2,
  v2 as streamLatestVersion
};

需要注意的是:export 命令规定的是对外接口,必须与模块内部的变量建立一一对应的关系

//错误写法
// 报错
export 1;

// 报错
var m = 1;
export m;

上面两种写法都会报错,因为没有提供对外的接口。第一种写法直接输出 1,第二种写法通过变量m,还是直接输出 1,1只是一个值,不是接口。正确的写法是下面这样。

// 写法一
export var m = 1;

// 写法二
var m = 1;
export {m};

// 写法三
var n = 1;
export {n as m};
// 报错
function f() {}
export f;

// 正确
export function f() {};

// 正确
function f() {}
export {f};

2.import

import { lastName as surname } from './profile.js';

注意:import命令输入的变量都是只读,不能重新赋值,因为本质是输入接口

高级JS

原型与原型链

1.原型(prototype)

1)函数的protype属性
每个函数都有一个protype属性,它默认指向一个Object空对象(即称为原型对象,理解:没有我们的属性 )
原型对象中有一个属性constructor ,它指向函数对象
2)给原型对象添加属性(一般是方法)
作用:函数的所有实例对象自动拥有原型中的属性(方法)

原型上的方法是给实例对象用的

在这里插入图片描述
在这里插入图片描述
如果函数名叫Type,有一个属性叫prototype,这个属性指向Type的原型对象,原型对象里有个属性constructor,constructor又指向Type
即 我(Type)爷爷(prototype)的孙子(constructor)是我(Type)

2.显示原型和隐式原型

1)每个函数function都有一个prototype ,即显式原型
2)每个实例对象都有一个 proto 属性,可称为隐式原型
3)对象的隐式原型的值为其对应构造函数的显式原型的值
4)内存结构(图)
在这里插入图片描述
5)总结
函数的prototype属性:在定义函数时自动增加的,默认值是一个空Object对象
对象的__proto__属性:创建对象时自动添加的,默认值为构造函数的prototype属性值

3.原型链

  1. 原型链(图解)
    在这里插入图片描述

test1不在Fn上,实在this上,这里的this是fn,所以挂在了fn上,Fn上根本就没有test1

访问一个对象的属性时,先在自身属性中查找,找到返回
如果没有,再沿着__proto__这条链向上查找,找到返回
如果最终没找到,返回Undefined
*别名:隐式原型链
*作用:查找对象的属性(方法)
2)构造函数/原型/实体对象的关系(图解)
3)构造函数/原型/实体对象的关系2(图解)
理解一:总的来说,原型中有一个总共的父原型,创建的函数对应的是一个个的子原型,可以通过原型的原型来返回到最开始的父原型中
理解二:我们创建的函数对象都是object的实例,所以都有一个__proto__属性指向object的原型对象,而object的原型对象里面有一些固定方法, 所以后面的实例对象都可以通过这个原型链访问到那些方法

4.原型链的属性问题

1)读取对象的属性值时,会自动到原型链中查找
2)设置对象的属性值时,不会查找原型链,如果当前对象中没有此属性,直接添加此属性并设置其值
3)方法一般定义在原型中,属性一般通过构造函数定义在对象本身上

5.探索instanceof

1.instanceof 是如何判断的
表达式 A instanceof B
如果B函数的显式原型对象在A对象的原型链上,返回true
2.Function 是通过new 自己产生的实例

所有函数的实例对象都是Object的实例,Object自身除外
Object作为实例对象时,会指向Function的原型对象,再指向Object自身原型对象

6.面试题

在这里插入图片描述
打印出来是 1 Undefined 2 3
因为A改写了原型prototype,在堆内存重新开辟了空间,而b还是指向原来的内存地址

执行上下文与上下文栈

1.变量提升和函数提升

1.变量声明提升
通过var定义(声明)的变量,在定义语句之前就可以访问到
值;undefined
2.函数声明提升
通过function声明的函数,在之前就可以调用、
值:函数定义(对象)
3.问题:变量提升和函数提升是如何产生的?

2.执行上下文

1.代码分类(位置)
*全局代码
*函数(局部)代码
2.全局执行上下文
● 在执行全局代码前将window确定为全局执行上下文
● 对全局数据进行预处理
*var 定义的全局变量=>undefined 添加为window属性
*function 声明的全局函数=>赋值(fun),添加为window的方法
* this=>赋值(window)
● 开始执行全局代码
3.函数执行上下文
● 在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象(虚拟的,存在于栈中)
● 对局部数据进行预处理
*形参变量=>赋值(实参)=>添加为执行上下文的属性
*arguments=>赋值(实参列表),添加为执行上下文的属性
*var 定义的局部变量=>undefined 添加为执行上下文的属性
*function声明的函数=>赋值(fun)添加为执行上下文的方法
*this=> 赋值 (调用函数的对象)
● 开始执行函数体代码

3.执行上下文栈

1)在全局代码执行前,JS引擎就会创建一个栈来存储管理所有的执行上下文对象
2)在全局执行上下文(window)确定后,将其添加到栈中(压栈)
3)在函数执行上下文创建后,将栈顶的对象移除(出栈)
4)在当前函数执行完后,将栈顶的对象移除(出栈)
5)当所有的代码执行完后,栈中只剩下window
后进先出

总结

这周学习了Es6和js高级,深度逐渐增加,不过还好之前学超哥的课有所了解现在的高级js,不至于听不懂,下周计划:将这两周学习的知识掌握

  • 20
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值