前端学习笔记(二)(持续性更新)

194 阅读6分钟

Part1-2 ES新特性与TypeScript、JS性能优化

1.ECMAScript

ECMAScript(简称ES)通常被认为是JavaScript的标准化规范,JavaScript是ECMAScript的扩展语言。ECMAScript从2015年开始之后,开始使用年份来命名,ECMAScript2015也成ES6,后续的ES2016,ES2017,ES2018。

2.ES新特性

2.1 let、const和var的区别

// let const 形成一个块级作用域,不会有变量声明的提升
// let用来声明变量
// for (var i = 0; i < 3; i++) {
//   console.log(i);
// }
// console.log(i);
for (let i = 0; i < 3; i++) {
  console.log(i);
}
// console.log(i);

// const声明常量,一旦声明之后不能在更改
const b = 1;

2.2 数组的解构

// 数组的解构
const arr = ['one', 'two', 'three']
const [, str1,str] = arr
console.log(str1, str); // two three

2.3 对象的解构

// 对象的解构
const obj = {
  name: 'zs'
}
// :后设置别名,防止命名冲突
const { name: namea } = obj
console.log(namea);

2.4 模板字符串

// 模板字符串
// const str = `123`
// 带标签的模板字符串,可以用来处理字符串中的插值数据
const name = 'tom'
const gender = true
const tag = (s, name, gender) => {
  console.log(s, name, gender);
  const sex = gender ? 'man' : 'women'
  return s[0] + name + s[1] + sex
}
const str1 = tag`hi, my name is ${name}, I am a ${gender}`
console.log(str1); // hi, my name is tom, I am a man

2.5 箭头函数

// 箭头函数
const fn = (a, b) => a + b 
const arr = [1, 2, 3]
const result = arr.filter(v => v % 2)
// 不会改变this指向
const obj = {
  name: 'zs',
  sayHi: function () {
    console.log('my name is ' + this.name);
  },
  sayHi1: () => {
    console.log('my name is ' + this.name);
  }
}
obj.sayHi() // my name is zs
// this作用于当前作用域,上下文
obj.sayHi1() // my name is undefined

2.6 对象字面量

// 对象字面量
const name = 'zd'
const age = '18'
const obj1 = {
  name,
  age,
  // fn: function () {}
  fn () {
    console.log(this);
  }
}
console.log(obj1); // { name: 'zd', age: '18', fn: [Function: fn] }
obj1.fn() // { name: 'zd', age: '18', fn: [Function: fn] }

2.7 Proxy

// proxy
const obj = {
  name: 'zs',
  age: 18
}
const proxyObj = new Proxy(obj, {
  get (target, property) {
    console.log(target, property);
    // return 100
    return property in target ? target[property] : 'none'
  },
  set (target, property, value) {
    if (property === 'age') {
      if (!Number.isInteger(value)) {
        throw new Error('Error')
      }
    }
    target[property] = value
  },
  deleteProperty (target, property) {
    console.log(target, property);
    delete target[property]
  }
})
// proxyObj.age = '19' // Error
proxyObj.age = 19 // { name: 'zs', age: 19 }
console.log(proxyObj.name); // zs
console.log(proxyObj.name1); // none

// Proxy和defineProperty对比
delete proxyObj.name
console.log(proxyObj);

const p1 = {}
Object.defineProperty(p1, 'age', {
  get () {
    console.log('age访问了');
    return p1._age
  },
  set (val) {
    console.log('age设置了');
  }
})
Object.defineProperty(p1, 'name', {
  get () {
    console.log('name访问了');
    return p1._name
  },
  set (val) {
    console.log('name设置了');
  }
})
p1.age = 1
console.log(p1.age);

Proxy数据劫持的方式比Object.defineProperty更加方便,新增了很多Object.defineProperty不具备的方法。

2.8 Reflect

// Reflect 
const obj = {
  name: 'zs',
  age: 18
}
const proxyObj = new Proxy(obj, {
  get (target, property) {
    console.log(target, property);
    return Reflect.get(target, property)
  }
})
console.log(proxyObj.name);
// 统一操作对象的api
console.log(
  Reflect.has(obj, 'name'),
  Reflect.ownKeys(obj),
  Reflect.deleteProperty(obj, 'age'),
)

2.9 class类

// class
// function Person (name) {
//   this.name = name
// }
// Person.prototype.say = function () {
//   console.log(`${this.name}`);
// }
class Person {
  constructor (name) {
    this.name = name
  }

  say () {
    console.log(`my name is ${this.name}`);
  }
}
const p = new Person('tom')
p.say()

2.9.1 static静态修饰词

// 静态方法修饰词static
class Person {
  constructor (name) {
    this.name = name
  }

  say () {
    console.log(`my name is ${this.name}`);
  }

  static create (val) {
    return new Person(val)
  }
}
Person.create('zs').say()

2.9.2 继承extends

// extends继承
class Teacher {
  constructor (name, age) {
    this.name = name
    this.age = age
  }
  say () {
    console.log(`my name is ${this.name}, i am ${this.age} years old`);
  }
}

class Student extends Teacher {
  constructor (name, age, num) {
    // 调用父级的属性
    super(name, age)
    this.num = num
  }

  hello () {
    // 调用父级的方法
    super.say()
    console.log(`my num is ${this.num}`);
  }
}
const s = new Student('jack', 12, 123)
s.hello()
// my name is jack, i am 12 years old
// my num is 123

2.10 set数据结构

// set数据结构
const s = new Set()
// add:往set中添加元素
s.add(1).add(2).add(3)
console.log(s); // Set { 1, 2, 3 }
// for of遍历
for (let i of s) {
  console.log(i);
}
// size:长度
console.log(s.size);
// has:判断是否有某个元素
console.log(s.has(100));
// delete:删除某个元素,删除成功返回true,反之返回false
console.log(s.delete(1));
// clear:清空set
s.clear()
console.log(s); // Set {}
// 数组去重
const arr = new Set([1, 2, 2, 3, 1, 5, 5])
console.log(arr); // Set { 1, 2, 3, 5 }
console.log([...arr]); // [ 1, 2, 3, 5 ]
console.log(Array.from(arr));// [ 1, 2, 3, 5 ]

2.11 map数据结构

// map数据结构
// 可以使用任意类型的值作为健
// 普通对象结构
const obj = {}
obj[123] = 123
obj[true] = 345
obj[{a: 123}] = 456
// 会自动将值toString之后作为健
console.log(obj); // { '123': 123, true: 345, '[object Object]': 456 }
console.log(obj[{}]); // 456

// map 
const m = new Map()
const a = { name: 'tom'}
m.set(a, 10)
m.set(true, 101)
m.set(123, 16)
// 遍历
m.forEach((value, key) => {
  console.log(value, key);
})
// 10 { name: 'tom' }
// 101 true
// 16 123
console.log(m); // Map { { name: 'tom' } => 10 }
console.log(m.get(a)); // 10
m.delete(a)
console.log(m); // Map { true => 101, 123 => 16 }
// 清空
m.clear()
console.log(m); // Map {}

2.12 Symbol 数据类型

Symbol 数据类型

// 独一无二的存在
const s1 = Symbol()
const s2 = Symbol()
console.log(s1 === s2); // false
// 可以给Symbol设置一个别称用于区分
console.log(
  Symbol('a'),
  Symbol('b'),
  Symbol('c'),
);
// Symbol(a) Symbol(b) Symbol(c)
// 为对象设置一个独一无二的属性
const name = Symbol()
const obj = {
  [name]: 'zs',
  say() {
    console.log(`my name is ${this[name]}`);
  }
}
obj.say()

// Symbol.for方法,参数会调用toString方法转成字符串
console.log(Symbol.for('foo') === Symbol.for('foo')); // true
console.log(Object.getOwnPropertySymbols(obj)); // [ Symbol() ]
const obj1 = {
  [Symbol.toStringTag]: 'xObj' // [object xObj]
}
console.log(obj1.toString()); // [object Object]

2.13 for...of遍历

for of 遍历所有数据结构的一种方式

// 数组的遍历,可以随时使用break停止遍历
const arr = [1, 2, 3, 3, 5]

for (const item of arr) {
  if (item > 2) {
    break
  }
  console.log(item);
}
// set遍历
const s = new Set([1, 2, 3])
for (const i of s) {
  console.log(i);
}
// map遍历
const m = new Map()
m.set('name', 'zs')
m.set('age', 18)
for (const [key, value] of m) {
  console.log(key, value);
}
// 普通对象不可遍历,obj is not iterable报错
// const obj = { a: 1, b: 2}
// for (const v of obj) {
//   console.log(v);
// }

2.14 Iterable可迭代接口

// Iterable 可迭代接口
// 实现Iterable接口是for...of...的前提
// iterator迭代器
const arr = ['one', 'two']
const iterator = arr[Symbol.iterator]()
console.log(iterator.next()); // { value: 'one', done: false }
console.log(iterator.next()); // { value: 'two', done: false }
console.log(iterator.next()); // { value: undefined, done: true }

// 实现 Iterable 可迭代接口
const obj = {
  store: ['one', 'two', 'three'],
  // 返回一个迭代器方法
  [Symbol.iterator]: function () {
    const self = this
    let index = 0
    // 返回一个实现迭代器对象
    return {
      // 提供一个next方法
      next: function () {
        // 返回迭代结果
        const result = {
          value: self.store[index], // 存储结果
          done: index >= self.store.length // 表示迭代状态
        }
        index++
        return result
      }
    }
  }
}
for (const key of obj) {
  console.log(key);
}

3.TypeScript

3.1 断言

const arr = [100, 200, 300]
const res = arr.find(x => x > 0)
// 设置断言的两种方式
const num  = res as number
const num1 = <number>res
// const num1 = res * res

3.2 枚举

// 默认从0开始累加
enum Status {
  Empty, 
  One,
  Two
}
// 设置初始值之后后面的值累加
// enum Status {
//   Empty = 6, 
//   One,
//   Two
// }
// 设置字符串之后,后面的值都要设置为字符串
// enum Status {
//   Empty = 'abc', 
//   One = 'bcd',
//   Two = 'edg'
// }

const obj = {
  value: Status.Empty // 1, 2
}

3.3 元组类型

// 明确元素数量,以及每一个元素类型的一个数组
const tuple: [number, string] = [1, '2']
const [age, name] = tuple
Object.entries({
  name: 'zs',
  age: 18
})

3.4 数组

// 常用的两种方式
const arr1: Array<number> = [1, 2]
const arr2: number[] = [222, 333]

function getSum (...args: number[]) {
  return args.reduce((a, c) => a + c, 0)
}
getSum(1, 2)

3.5 对象

const foo: object = [] // {} function
const obj1: { foo: number } = { foo: 1 }

3.6 interface接口

// 接口
interface Post {
  name: string
  age: number
  title?: string // 可有可无
  readonly nickname?: string // 只读属性
}
function say (obj: Post) {
  console.log(obj.name);
  console.log(obj.age);
}
say({
  name: 'sz',
  age: 18
})

interface cash {
  // key可以是任意字符串,是用来约束健值的,声明其类型
  [key: string]: string
}
const cash: cash = {}
cash.a = '12'
cash.b = '12'

3.7 class类

class Person {
  public name: string // 公众属性
  private age: number // 私有属性
  protected readonly gender: boolean // 只读

  constructor (name: string, age: number) {
    this.name = name
    this.age = age
    this.gender = true
  }

  sayHi (msg: string) {
    console.log(`I am a ${this.name}, ${msg}`);
  }
}

class Student extends Person {
  constructor (name: string, age: number) {
    super(name, age)
  }
}

const p1 = new Person('zs', 18)
console.log(p1.name);
// console.log(p1.age); // 报错
// console.log(p1.gender); // 报错

// 类
interface Eat {
  eat (food: string): void
}
interface Run {
  run (food: string): void
}


class Person implements Eat, Run {
  eat (food: string): void {
    console.log(food);
  }
  run (distance: string): void {
    console.log(distance);
  }
}

class Dog implements Eat, Run {
  eat (food: string): void {
    console.log(food);
  }
  run (distance: string): void {
    console.log(distance);
  }
}

// 抽象类
abstract class Animal {
  eat (food: string): void {
    console.log(food);
  }
  abstract run (distance: string): void
}

class Dog1 extends Animal {
  run(distance: string): void {
    console.log(distance);
  }
  
}

3.8 范型

// 声明的函数民航后面<T>,不确定类型:T
function ceateArr<T> (length: number, value: T): T[] {
  const res = Array<T>(length).fill(value)
  return res
}

ceateArr(3, 100)
ceateArr(3, '100')

const cetAA = <T>(length: number, value: T): T[] => {
  const res = Array<T>(length).fill(value)
  return res
}
cetAA(12, 12)
cetAA(12, '12')

4. js性能优化

V8引擎常用的GC回收算法

// 性能优化
// gc回收机制
/**
 * 1.引用计数方法
 * 核心思想:设置引用数,判断当前引用计数是否为0
 * 引用计数器,引用关系改变时修改引用数值,引用计数为0时,回收
 * 优点:发现垃圾时立即回收,最大程度减少程序暂停
 * 缺点:无法回收循环引用的对象,时间开销大
 */

/**
 * 2.标记清除算法
 * 核心思想:分标记和清除两个阶段完成
 * 遍历所有对象找标记活动对象
 * 遍历所有对象清除没有标记对象
 * 回收相应空间
 * 优点:解决了无法回收循环引用的对象的问题
 * 缺点:造成了空间碎片化,不会立即回收
 */

/**
 * 2.标记整理算法
 * 核心思想:分标记和清除两个阶段完成
 * 遍历所有对象找标记活动对象
 * 遍历所有对象清除没有标记对象
 * 回收之前先进行整理,调整位置
 * 回收相应空间
 * 优点:解决了无法回收循环引用的对象的问题
 * 缺点:减少了空间碎片化
 */
 
/**
 * V8垃圾回收策略:
 * 1. 采用分代回收思想
 * 2. 内存分为新生代和老生代
 * 3. 针对不同对象采用不同算法
 */

/**
 * 常用的回收算法:
 * 1. 分代回收
 * 2. 空间复制
 * 3. 标记清除
 * 4. 标记整理
 * 5. 标记增量
 */

/**
 * V8内存分配:
 * 1. 内存一分为二
 * 2. 小空间用来存储新生代对象,即存活时间较短的对象(64位系统32M | 32位系统16M)
 * 
 * 新生代对象回收机制:
 * 1. 采用复制算法+标记整理
 * 2. 新生代内存分为两个同等大小空间
 * 3. 使用空间From,用于存储活动对象,空闲空间To
 * 4. 标记整理之后,将活动对象拷贝至空闲空间To
 * 5. From与To交换空间达到内存释放
 * 
 * 细节说明:
 * 1. 拷贝过程可能出现晋升(将新生代对象移动到老生代)
 * 2. 一轮过后依然存活的新生代对象
 * 3. To空间使用达到25%,触发晋升
 */

/**
 * 老生代回收:
 * 64位系统1.4G,32位系统700M
 * 老生代对象就是存活时间较长的对象
 * 
 * 回收机制:
 * 1. 标记清除,标记整理,增量标记,这些算法实现
 * 2. 先试用标记清除,回收垃圾释放空间
 * 3. 使用标记整理优化内存空间
 * 4. 使用增量标记进行效率优化
 * 
 */