ES6学习

67 阅读5分钟

ECMAScript 6.0简称ES6,是Javasript语言的下一代标准,在2015年6月正式发布,之后采取了年份命名,如ES2015ES6一般指ES2015,也可泛指“下一代JavaScript语言”。

各大浏览器对ES6的支持可以查看compat-table.github.io/compat-tabl…

语言和平台的关系

ECMASript是JavaScript的规格,JavaScript是ECMAScript的一种实现方式。

let和const命令

letconst只在代码块中生效,在同一代码块中不允许重复声明且不存在变量提升问题,推荐在日常编码中不使用var,主用const,配合使用let

解构赋值

1.数组的解构赋值

let [a,b,c] = [1,2,3];
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3


let [x, ...y] = [1, 2, 3];
console.log(x); // 1
console.log(y); // [2, 3]

2.对象的解构赋值

对象的解构需要同名属性才会给对应的变量赋值

let {foo, bar} = {foo: 'hello', bar: 'world'};
console.log(foo); // hello

对象的解构可以设置默认值

let {foo, bar="张三"} = {foo: 'hello'};
console.log(foo,bar); // hello

3.字符串的解构赋值

let [a,b,c,d,e,...f] = 'hello world';
console.log(a,b,c,d,e); // h
console.log(f); // h

4.函数参数的解构赋值

function sum(x,y){
  return x + y;
}
console.log(sum(1,2)); // 3

字符串的扩展

1.模版字符串

// 单行字符串
console.log(`In JavaScript '\n' is a line-feed.`);

// 多行字符串

console.log(`string text line 1
string text line 2`);

// 字符串中嵌入变量
let name = "Bob", time = "today";
console.log(`Hello ${name}, how are you ${time}?`);

2.字符串的扩展方法

  • includes():返回布尔值,表示是否找到了参数字符串。
  • startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
  • endsWith():返回布尔值 ,表示参数字符串是否在原字符串的尾部。
let str = "Hello World";
console.log(str.startsWith('He')); 
console.log(str.includes('lo'));
console.log(str.endsWith('ld'));

支持第二个参数,表示开始搜索的位置

// 支持第二个参数
console.log(str.startsWith('World', 6)); 
console.log(str.endsWith('Hello',5));
console.log(str.includes('lo', 3));

数值的扩展

console.log( parseInt(12.34));
console.log( parseFloat("12.34#"));
console.log(Number.parseInt(12.34));
console.log(Number.parseFloat("12.34#"));
console.log(Number.isNaN(NaN));
console.log(Number.isInteger(12.1));

bigInt大整数

const a = 2172141653n;
const b = 15346349309n;
console.log(a + b); // 17518590962n
console.log(a * b); // 33334444555566667777n
console.log(Number(a)+Number(b)); // 
console.log(Number(a)*Number(b)); // 普通数字计算会丢失精度

函数的扩展

ES6支持在形参中设置参数默认值

function log(x, y = 'World') {
  console.log(x, y);
}
log('Hello'); // Hello World
log('Hello', 'JavaScript'); // Hello JavaScript 
log('Hello', '');

剩余参数

// 剩余参数
function sum(a,...args) {
console.log(a,args); // [1, 2, 3]
}
sum(1, 2, 3);

箭头函数

简化定义函数的代码

const arr = [1, 2, 3, 4, 5];
// 箭头函数写法
arr.filter(item => item % 2);
// 原写法
arr.filter(function(item) {
  return item % 2;
});

箭头函数不会改变this的指向

const person = {
  name: '张三',
  age: 25,
  // greet() {
  //   console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
  // } 
  
  greet:()=>{
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
  }
}

console.log(person.name); // 张三
console.log(person['age']); // 25 
person.greet();

Class的继承

// 对象的继承
class Animal {
  eat(){
    console.log(`eating.`);
  }
}
class Dog extends Animal {
  constructor(name) {
    super(); // 调用父类的构造函数
    this.name = name;
  }
  bark() {
    console.log(`${this.name} is barking.`);
  }
}

const dog = new Dog('Tom');
dog.eat(); // eating.
dog.bark(); // Tom is barking.

对象的方法

Object.aissign复制对象

const a ={a:1};
const b ={b:2};
Object.assign(a, b); // 将 b 的属性复制到 a
console.log(a); // { a: 1, b: 2 }
Object.assign(a, b, {c:3}); // 可以传入多个对象
console.log(a); // { a: 1, b: 2, c: 3 }
const d = Object.assign({}, a, b); // 创建一个新的对象
console.log(d); // { a: 1, b: 2, c: 3 }

Object.is判断对象是否相等

// Object.is() 方法
console.log(Object.is('foo', 'foo')); // true
console.log(Object.is(a,d)); // false
console.log(Object.is(NaN, NaN)); // true
console.log(Object.is(+0, -0)); // false

Object.keys(), Object.values(), Object.entries() ,Object.fromEntries(),Object.hasOwn方法

//Object.keys(), Object.values(), Object.entries() ,Object.fromEntries() 方法
const obj = {
  a: 1,
  b: 2,
  c: 3,
};
Object.keys(obj).forEach((key) => {
  console.log(`${key}: ${obj[key]}`); // 输出每个键值对
});
Object.values(obj).forEach((key) => {
  console.log(`${key}`); // 输出每个键值对
});
Object.entries(obj).forEach(([key, value]) => {
  console.log(`${key}: ${value}`); // 输出每个键值对
});

console.log(
  Object.fromEntries([
    ["a", 1],
    ["b", 2],
    ["c", 3],
  ])
);
console.log(Object.hasOwn(obj, 'a')); // true

Proxy

为对象设置访问代理 ,外界对该对象的访问,都必须通过这层拦截,因此可以对外界的访问进行过滤和改写。

const person = {
  name: '张三',
  age: 25,
}
const personProxy = new Proxy(person, {
  get(target,property){
    return property in target ? target[property] : `Property ${property} does not exist`;
  },
  set(target,property,value){
    if (property === 'age' && typeof value !== 'number') {
      throw new TypeError('Age must be a number');
    }
    target[property] = value;
    return true; // 表示设置成功
  }
} )
console.log(personProxy.name); // 张三
console.log(personProxy.age=15); // 25

proxy对数组的监听

const list = []
const listProxy = new Proxy(list, {
  set(target, property, value) {
    if (property === target.length) {
      throw new Error('Cannot set length directly');
    }
    target[property] = value;
    console.log(value, 'added to list at index', property);
    
    return true; // 表示设置成功
  }
})
listProxy.push(1); // 添加元素
listProxy.push(2); // 添加元素

Reflect对象

统一了操作对象的API,让所有的Object操作变为函数行为。

Reflect.get(target, name, receiver) 获取值

var myObject = {
  foo: 1,
  bar: 2,
  get baz() {
    return this.foo + this.bar;
  },
}
console.log(Reflect.get(myObject, 'foo')); // 1
console.log(Reflect.get(myObject, 'bar')); 
console.log(Reflect.get(myObject, 'baz')); // 3

var myReceiverObject = {
  foo: 4,
  bar: 4,
};
// this 指向 receiver
console.log(Reflect.get(myObject, 'baz', myReceiverObject)); // 4

Reflect.set(target, name, receiver) 设置值

var myObject = {
  foo: 1,
  bar: 2,
  get baz() {
    return this.foo + this.bar;
  },
}
var myReceiverObject = {
  foo: 4,
  bar: 4,
};
console.log(Reflect.set(myObject, 'foo', 10)); // true
console.log(Reflect.get(myObject, 'foo')); // 10
console.log(Reflect.set(myObject, 'aa',myReceiverObject))
console.log(Reflect.get(myObject, 'aa')); // 10

Reflect.deleteProperty(target, name);

删除对象的属性

Reflect.deleteProperty(myObject, 'foo'); // true
console.log(Reflect.get(myObject, 'foo'));

promise

let promise = new Promise((resolve, reject) => {
  if (Math.random() < 0.5) {
    resolve("成功");
  } else {
    reject("失败");
  }
});
promise
  .then((value) => {
    console.log(value); // 成功
  })
  .catch((error) => {
    console.log(error);
  });
  • Promise.all()等待所有Promise成功,任一失败则整体失败 并行请求且需全部成功
  • Promise.race()以第一个完成的Promise结果为准(无论成功/失败) 请求超时控制
  • Promise.any()等待第一个成功的Promise(ES2021,全部失败时抛AggregateError) 多源数据择优
  • Promise.allSettled() 等待所有Promise完成,返回状态描述数组(无论成功/失败)

set数据类型

const set = new Set();
set.add(1).add(2).add(3);
console.log(set);

可以利用set数据特性用来数组去重

const set2 = new Set([1, 2, 3,4,2,3]);
console.log(set2);

Map数据类型

对象只能用字符串作为键,map可以用任意类型的值作为键

const map = new Map();
// 添加数据
map.set('name', '张三');
const o = { age: 18 };
map.set('age', o);
console.log(map);

symbol

适合作为对象的私有属性

const a = Symbol('a');
const b = Symbol('a');
console.log(a);
console.log(typeof a); // "symbol"
console.log(a === b); // false, 因为每个 Symbol 都是唯一的

for of循环

适合遍历所有数据结构

// for...of 循环可以遍历 Set 中的元素
const set = new Set([1,2,3,4,5,6,7,8,9,10]);
for (const item of set) {
  console.log(item); // 1 2 3 4 5 6 7 8 9 10
} 
// for...of 循环可以遍历 Map 中的键值对
const map = new Map([
  ['name', '张三'],
  ['age', 18]
]);
for (const [key, value] of map) {
  console.log(`${key}: ${value}`); // name: 张三, age: 18
}

for of可以使用break终止循环

for (const item of set) {
  if (item === 5) {
    continue; // 遇到 5 时跳过当前循环
  }
  if (item === 8) {
    break; // 遇到 8 时终止循环
  }
  console.log(item); // 1 2 3 4 5 6 7 8 9 10
}

Iterator可迭代接口

数组\对象\set\map都是可迭代对象

迭代器模式

特性:惰性执行

function * foo(params) {
  console.log('111');
  yield 100;
  console.log('222');
  yield 200;
  console.log('333');
  yield 300;  
  
}
const generator = foo();
console.log(generator.next());
console.log(generator.next());
console.log(generator.next());

叫号器例子

function * createIdMaker(){
  let id=1;
  while(true){
    yield id++;
  }
}
const idMaker = createIdMaker();
console.log(idMaker.next().value); // 1
console.log(idMaker.next().value); // 2   
console.log(idMaker.next().value);

Async/Await

Async/Await 是 ES2017 引入的语法糖,基于 Promise 实现,旨在以同步写法管理异步操作,彻底解决回调地狱问题。

async 声明函数:隐式返回 Promise 对象,若返回非 Promise 值,会被自动包装为 Promise.resolve(value)17

await 表达式:暂停当前 async 函数执行,等待右侧 Promise 完成,并返回其解决值(若拒绝则抛出错误)

async function fetchData() {
  try {
    // 动态加载 JSON 文件
    const data = await require('./data.json');
    return data.users; // 返回用户数据
  } catch (err) {
    console.error("读取失败:", err);
    return []; // 失败返回空数组
  }
}
fetchData().then(users => {
  console.log("用户数据:", users);
})

面试题

谈谈你对vue的理解?

vue是什么?

  • vue是基于javascript的渐进式框架,单页面应用

vue的核心特性:

  • 数据视图双向绑定(MVVM)(model view view-model )
  • 组件化思想
  • 响应式指令

说说你对双向绑定的理解?

什么是双向绑定?

我们可以先聊一下单向绑定,就是把model绑定到view,当使用JavaScript更新model的时候,view自动更新就是单向绑定。

当用户填表单的时候,此时会更新view,如果此时也可以自动更新model的状态,这时候可以认为model和view进行了双向绑定。

双向绑定的原理:

MVVM分为三部分:

数据层(Model):应用的数据及业务逻辑

视图层(View):应用的展示效果,各类UI组件

业务逻辑层(ViewModel):框架核心,将数据与视图关联起来

ViewModel的主要职责:

  • 数据变化后更新视图
  • 视图变化后更新数据

ViewModel的主要职责:

监听器(Observer):对所有属性进行监听

解析器(Compiler):对每个元素节点的指令进行扫描跟解析,根据指令模板替换数据,绑定相应的更新函数