ECMAScript의 새로운 기능에 대한 포괄적인 숙달

coldplay.xixi
풀어 주다: 2020-10-22 18:34:05
앞으로
2021명이 탐색했습니다.

오늘의 javascript 칼럼에서는 ECMAScript의 새로운 기능을 소개하겠습니다.

ECMAScript의 새로운 기능에 대한 포괄적인 숙달

ES6가 JavaScript의 차세대 표준이 된 후 표준 위원회(TC39)는 매년 새로운 버전의 ES를 출시할 예정입니다. 새로운 기능을 사용하면 개발 효율성이 크게 향상될 것입니다. ES의 새로운 기능을 완전히 이해해 봅시다~

Let 및 Const

이전에는 var를 사용하여 변수를 선언했으며, 이는
let을 사용하여 변수를 선언하는 새로운 방법을 제공합니다. const는 상수를 선언하는 데 사용됩니다.

사용 방법

const TAG = "我是常量";let a;
a = 2;console.log(TAG, "a=" + a); //我是常量   a=2复制代码
로그인 후 복사

4가지 기능

1. 블록 수준 범위에서만 유효합니다.

let 및 const는 JavaScript에 새 블록 수준 범위를 추가합니다. 일반적으로 {}로 래핑된 코드 범위는 블록 수준입니다. 범위, 선언된 변수 또는 상수는 블록 수준 범위 내에서만 유효하며 외부에서는 액세스할 수 없습니다.

if (true) { //外层块级作用域
  let a = 1;  const A = 1;  if (true) {   //内层块级作用域
    let a = 2;
  }  console.log(a,A); //(1)输出:1 , 1}console.log(a); //(2)Uncaught ReferenceError: a is not defined复制代码
로그인 후 복사

위에 두 개의 블록 수준 범위가 있는데 둘 다 변수 a를 선언하지만 외부 블록 수준 범위는 내부 블록 수준 범위와 아무 관련이 없으므로 (1)의 출력은 외부 변수 값입니다. 1, (2) 블록 수준 범위에서 정의되지 않은 변수에 접근하면 오류가 보고됩니다.

블록 범위를 이해하는 또 다른 예입니다.

//for循环体内的定时器//在ES6之前,是没有块级作用域的,变量用var声明,直接挂载在全局作用域上for (var i = 0; i <h4 data-id="heading-4">2. 임시 데드존</h4><p>은 변수와 상수가 선언되기 전에는 사용할 수 없습니다. <br>let 및 const 명령을 사용하면 블록이 닫힌 범위를 형성하게 됩니다. 선언 전에 사용하면 이를 구문상 "임시 데드 존"(TDZ)이라고 합니다. </p><pre class="brush:php;toolbar:false">if (true) {
  tmp = "abc"; // ReferenceError
  let tmp; 
}复制代码
로그인 후 복사

3. 반복적으로 선언할 수 없습니다.

let a = 1;let a = 2;//报错  SyntaxError: Identifier 'a' has already been declared const B=1;const B=2;//报错  SyntaxError: Identifier 'B' has already been declared 复制代码
로그인 후 복사

4. 최상위 객체에 속하지 않는 변수

let 선언은 전역 객체(window, global, self)에서 접근할 수 없습니다.

let a = 10;console.log(window.a);  //undefined复制代码
로그인 후 복사

String

ES6에는 다음과 같이 strings 에 몇 가지 확장을 만들었습니다.

Template string

ES6에는 문자열을 정의하는 템플릿 문자열(字符串) 메서드가 추가되었습니다. 이 메서드는 과거에 문자열이 너무 길어 래핑해야 했던 문제를 해결하는 데 사용됩니다. , 또는 문자열에 변수나 표현식이 있는 경우 다음은 구체적인 사용 시나리오입니다.

//一、打印长字符串且换行,直接在模板字符串中保留格式即可let welcome=`
  你好
    欢迎来到ES6
      ——谢谢
`console.log(welcome);/*
    输出结果为:
  你好
    欢迎来到ES6
      ——谢谢
*///二、字符串中有变量或者表达式,直接在模板字符串中使用${变量/表达式}即可let type = "ES6";let name1 = "mango";let name2 = "和goman";let welcome = `欢迎${name1 + name2}来到${type}世界`;  

console.log(welcome);   //learn1.js?c1a0:7 欢迎mango和goman来到ES6世界复制代码
로그인 후 복사

method

String.prototype.includes()

는 문자열에 지정된 문자열이 포함되어 있는지 확인하고 부울 유형을 반환합니다.

const str = "ECMAScript"console.log(str.includes("EC")); //true 找不到返回false  复制代码
로그인 후 복사

startsWith() 및 endWith()

startsWith()는 문자열이 지정된 문자열로 시작하고 부울 유형을 반환하는지 확인하는 데 사용됩니다.
endsWith()는 문자열이 지정된 문자열로 끝나는지 확인하고 부울 유형을 반환하는 데 사용됩니다.

const str = "ECMAScript"console.log(str.startsWith("ECM")); //true console.log(str.endsWith("Script")); //true 复制代码
로그인 후 복사

String.prototype.repeat()

원래 문자열을 n번 반복하여 새 문자열을 얻습니다.

const str = "ECMAScript";console.log(str.repeat(3)); //ECMAScriptECMAScriptECMAScript复制代码
로그인 후 복사

Number

ES6는 점차 전역 메서드를 축소하기 시작하여 언어를 점차 모듈식으로 만들었습니다. 값은 Number 객체로 전송되고 기능적 동작은 변경되지 않습니다.

//将目标转换为整数//ES5parseInt("5.6") //5//ES6Number.parseInt("5.6")  //5//将目标转换为浮点数//ES5parseFloat("12.45str")  //12.45//ES6Number.parseFloat("12.45str")   //12.45复制代码
로그인 후 복사

또한 개발을 용이하게 하기 위해 Number에는 몇 가지 메서드와 속성도 추가되었습니다.

一、判断一个数值是否是整数Number.isInteger(25) // trueNumber.isInteger(25.1) // false二、获取JavaScript最大安全值和最小安全值Number.MAX_SAFE_INTEGER=9007199254740991Number.MIN_SAFE_INTEGER=-9007199254740991三、判断一个数值是否是在安全范围Number.isSafeInteger(9007199254740992)  //false复制代码
로그인 후 복사

Symbol

고유한 값을 표현하기 위해 새로운 기본 데이터 유형이 도입되었습니다.

선언 방법

let sym = Symbol();let sym2 = Symbol();console.log(sym == sym2); //false   生成的值是独一无二的,所以不相等console.log(typeof sym);  //symbol  typeof查看值的类型为symbollet symWithDesc = Symbol("name"); //Symbol()括号内可以添加描述console.log(symWithDesc.toString()); //输出:Symbol(name)   打印描述需要转换成字符串复制代码
로그인 후 복사

프로젝트에 적용


1. 마법 문자열 제거
다양한 처리 기능을 수행하기 위해 클릭 메뉴를 만들어야 하는 경우 일반적으로 다음과 같이 구현합니다.

const clickMenu = function (menu) {  switch (menu) {    case "home":      break;    case "me":      break;
  }
};

clickMenu("home")复制代码
로그인 후 복사

"home"은 코드와 강한 결합을 이루는 문자열이 마법 문자열입니다. 프로젝트에서는 마법 문자열을 제거하기 위해 최선을 다해야 합니다.

const MENU_TYPE = {  home: Symbol(),  me: Symbol(),
};const clickMenu = function () {  switch (menu) {    case MENU_TYPE.home:      break;    case MENU_TYPE.me:      break;
  }
};

clickMenu(MENU_TYPE.home);复制代码
로그인 후 복사


II, 객체의 고유한 속성값으로
회사명 객체를 생성하고, 각 사람의 이름을 키 값으로 사용한다면, 누군가가 동일한 이름을 가지고 있고, 기호가 있으면 문제가 발생합니다. 이 문제를 해결할 수 있습니다

const scores = {
  [Symbol("张三")]: {    age: 22,
  },
  [Symbol("李四")]: {    age: 21,
  },
  [Symbol("张三")]: {    age: 20,
  },
};复制代码
로그인 후 복사

참고: Symbol로 정의된 속성은 다음 두 가지 방법으로만 탐색할 수 있습니다. 그렇지 않으면 속성을 얻을 수 없습니다.

for (let key of Object.getOwnPropertySymbols(scores)) {  console.log(key, key);
}for (let key of Reflect.ownKeys(scores)) {  console.log(key, scores[key]);
}复制代码
로그인 후 복사

Set and Map


데이터 작업을 보다 편리하게 구현하기 위해 ES6에는 Set과 Map이라는 두 가지 새로운 데이터 구조가 추가되었습니다.

Set

Set은 배열과 유사한 데이터 구조이지만 구성원의 값이 고유합니다.

New

월을 저장하기 위한 새로운 Set 데이터 구조를 생성하세요. 빈 Set 인스턴스를 정의하거나 배열 형식의 기본 데이터를 가질 수 있습니다.

let monthSets = new Set();let monthSets2 = new Set(["一月","二月","三月"]);复制代码
로그인 후 복사

기본 사용법

//添加数据monthSets.add("一月");
monthSets.add("二月").add("三月");console.log(monthSets); //Set(3) {"一月", "二月", "三月"}//遍历集合Set//forEach():使用回调函数遍历每个成员monthSets.forEach((item) => console.log(item)); //一月 二月  三月//for...of:直接遍历每个成员for (const item of monthSets) {  console.log(item);    //一月 二月  三月}//删除数据monthSets.delete("二月");console.log(monthSets); // Set(2) {"一月", "三月"}monthSets.clear(); //console.log(monthSets); // Set(0) {}复制代码
로그인 후 복사

일반 응용

세트 데이터 구조에는 실제 프로젝트에서 많은 응용 시나리오가 있습니다.

let monthSets = new Set(["一月", "二月", "三月"]);//一、快速判断数据元素是否存在monthSets.has("一月"); //true//二、统计数据元素个数monthSets.size; //3console.log(monthSets.size); //3//三、数组去重let arr = [1, 2, 3, 2, 3, 4, 5];let set = new Set(arr);console.log(set); // {1, 2, 3, 4, 5}//四、合并去重let arr = [1, 2, 3];let arr2 = [2, 3, 4];let set = new Set([...arr, ...arr2]);console.log(set); // {1, 2, 3, 4}//五、取数组交集let arr1 = [1, 2, 3];let arr2 = [2, 3, 4];let set1 = new Set(arr1);let set2 = new Set(arr2);let resultSet = new Set(arr1.filter((item) => set2.has(item)));console.log(Array.from(resultSet)); // [2, 3]//六、取数组差级let arr1 = [1, 2, 3];let arr2 = [2, 3, 4];let set1 = new Set(arr1);let set2 = new Set(arr2);let arr3 = arr1.filter((item) => !set2.has(item));let arr4 = arr2.filter((item) => !set1.has(item));console.log([...arr3, ...arr4]);  //[1, 4]复制代码
로그인 후 복사

WeakSet

WeakSet与Set类似,也是不重复的值的集合,但WeakSet的成员只能是对象。WeakSet引用的对象都是弱引用,如果其他对象不再引用该对象,那么垃圾回收机制就会自动回收这些对象所占用的内存,不考虑该对象还存在于WeakSet之中。
React源码中有很多地方使用到了WeakSet,例如在react-reconciler/src/ReactFiberHotReloading.new.js中。

export function markFailedErrorBoundaryForHotReloading(fiber: Fiber) {  if (__DEV__) {    if (resolveFamily === null) {      // Hot reloading is disabled.
      return;
    }    if (typeof WeakSet !== 'function') {      return;
    }    if (failedBoundaries === null) {
      failedBoundaries = new WeakSet();
    }
    failedBoundaries.add(fiber);
  }
}复制代码
로그인 후 복사

Map

Map是一种键值对集合,与对象类似,但Object只支持“字符串:值”,而Map支持“各种类型的值:值”,map给我们提供了更合适的“键值对”数据结构。

基本使用

//定义let map = new Map();//添加数据let address = { address: "江苏" };
map.set("name", "ES6");
map.set(27, "年龄信息");
map.set(address, "地址信息");console.log(map); //{"name" => "ES6", 27 => "年龄信息", {…} => "地址信息"}//获取数据let name = map.get("name");let age = map.get(27);let addressObj = map.get(address);console.log(name, age, addressObj);//获取成员数量console.log(map.size);  //3//判断是否指定key成员console.log(map.has("name")); //true复制代码
로그인 후 복사

Map的遍历

map通常可以用forEach和for...of的方式进行遍历。

//定义let map = new Map();

map.set("id", 1);
map.set("name", "mango");
map.set("address", {  province: "江苏",  city: "南京",
});


map.forEach((key, value) => console.log(key, value));for (const [key, value] of map) {  console.log(key, value);
}//输出  id 1      name mango      address {province: "江苏", city: "南京"}复制代码
로그인 후 복사

WeakMap

WeakMap与Map类似,也是用来生成键值对的集合。但WeakMap只接受对象作为键名,并且键名所指向的对象,属于弱引用对象。

数组的扩展

ES6对数组进行了很多的扩展,具体如下

扩展运算符

扩展运算符是三个点(...),将一个数组转为用逗号分隔的参数序列,通常用在函数参数中。
假如我们需要一个求和函数,并且支持传入任意数量的值。

function sum(...params) {  let sum = arr.reduce(function (prev, cur) {    return prev + cur;
  });  return sum;
}let arr = [1, 2, 3, 4, 5];console.log(sum(arr)); //输出 15复制代码
로그인 후 복사

Array.from()

Array.from()方法从一个类似数组或可迭代对象创建一个新的浅拷贝的数组实例,通常有以下四种实用场景。

//一、克隆一个数组let num = [1, 2, 3];let newNum = Array.from(num);console.log(newNum, num === newNum);  //[1, 2, 3] false//二、使用指定值,初始化一个数组//给定长度为10,默认值是数组2和对象{key:1}let length = 4;let defaultValue = 2;let defaultObj = { key: 1 };let arrValue = Array.from({ length }, (item,index) => defaultValue);let arrObj = Array.from({ length }, (item,index) => defaultObj);console.log(arrValue); // [2, 2, 2, 2]console.log(JSON.stringify(arrObj)); //[{"key":1},{"key":1},{"key":1},{"key":1}]//三、生成值范围数组function range(end) {  return Array.from({ length: end }, (item, index) => index);
}let arr = range(4);console.log(arr); // [0, 1, 2, 3]//四、数组去重,结合set使用let arr = [1, 1, 2, 3, 3];let set = new Set(arr);console.log(Array.from(set));复制代码
로그인 후 복사

创建数组

如何创建一个数组,有下面几种常用方式

//一、数组字面量const arr1 = [];//二、构造函数const arr2 = Array(3);  //[null,null,null]const arr3 = Array("3");  //["3"]//这时想要用构造函数创建一个数字为7的数组,发现上面方式是无法满足的,而ES6提供了Array.of()能满足我们的需求const arr3 = Array.of(7);  //[7]复制代码
로그인 후 복사

数组查找


find()方法返回数组中满足提供的测试函数的第一个元素的值,若没有找到对应元素返回undefined
findIndex()方法返回数组中满足提供的测试函数的第一个元素的索引,若没有找到对应元素则返回-1。

假如我们想要在一个成绩数组中,找到达到及格分数的最低分。

const score = [34, 23, 66, 12, 90, 88, 77, 40];const passMin = score.find((value) => value > 60);console.log(passMin); //66const pass = score.findIndex((value) => value > 60);console.log(pass); //2复制代码
로그인 후 복사

数组遍历

ES6新增 for...of 数组遍历方式

const score = [34, 23, 66, 12,];for (let value of score) {  console.log(value); //  34, 23, 66, 12}复制代码
로그인 후 복사

函数的扩展


ES6对函数进行了很多的扩展,具体如下

函数参数设置默认值

ES6允许为函数的参数设置默认值,即可以直接写在参数定义的后面。

//参数b设置了默认值为2,在方法调用的时候并没有传值,所以b直接使用默认值function sum(a, b = 2) {  return a + b;
}console.log(sum(1)); //3复制代码
로그인 후 복사

Rest参数

ES6引入reset参数,形式为...变量名,可以用来获取传递给函数的多余参数。

function sum(a, ...values) {  console.log(a, values); //1   [2, 3, 4, 5]}

sum(1, 2, 3, 4, 5);复制代码
로그인 후 복사

name和length属性

name属性返回函数名,length属性返回没有指定默认值的参数个数。

function sum(a, b, c, d = 1) {  console.log(a, values); //1   [2, 3, 4, 5]}console.log(sum.name);  //sumconsole.log(sum.length);  //3复制代码
로그인 후 복사

箭头函数

ES6允许使用箭头(=>)的方式定义函数,有下面几种箭头函数实现形式。
想要实现一个加法函数,ES5的形式如下

function sum(a, b) {  return a + b;
}复制代码
로그인 후 복사

而如果使用箭头函数实现的话,则如下

sumArrow = (a, b) => {  return a + b;
};复制代码
로그인 후 복사

上面是箭头函数的基本变现形式,不同的场景还有不同的实现形式。

//对于上面的sumArrow函//一、如果只有一个参数,可以省略括号sumArrow = a => {  return a;
};

二、如果返回值是表达式,可以省略return和{}
sumArrow = a => a;

三、如果返回值是字面量对象,一定要用小括号包起来
sumArrow = () => ({ a: 1, b: 2 });复制代码
로그인 후 복사

箭头函数与普通函数除了实现方式不同外,还有个不同的点就是对this的处理方式。

//普通函数let math = {  name: "mathName",  sum: function (a, b) {    console.log(this.name); //math
    return a + b;
  },
};

math.sum();//箭头函数globalThis.name = "globalName";let math = {  name: "mathName",  sum: (a, b) => {    console.log(this.name); //globalName
    return a + b;
  },
};

math.sum();复制代码
로그인 후 복사

从上面示例可以看到,箭头函数和普通函数最终打印的this.name不一致。对于普通函数,this指向的是调用sum方法的math对象,所以this.name打印的是“mathName”。而对于箭头函数,this指向的是定义sum方法的全局对象,所以this.name打印的是“globalName”。

在后续的开发过程中,我们将会经常使用到箭头函数,在使用的过程中,我们需要有以下几点注意

  1. 箭头函数中this指向定义时所在的对象,而不是调用时所在的对象
  2. 不可以当作构造函数
  3. 不可以使用yield命令,不能作用generator函数

解构赋值


解构赋值是一种表达式,可以将属性和值从对象和数组中取出,赋值给其他变量。

如何使用

对象解构赋值

假如我们拿到一个对象,需要获取指定的属性值。则解构赋值让我们无需通过调用属性的方式赋值,而是通过指定一个与对象结构相同模板的方式,获取想要的属性值。

const people = {  name: "ES6",  age: 27,  sex: "male",
};//如果通过调用属性赋值,则需要这么做let name = people.name;let age = people.age;let sex = people.sex;console.log(name, age, sex); //ES6 27 male//而使用解构赋值的方式,代码会更加的清晰简单const { name, age } = People;console.log(name, age); //ES6 27 male复制代码
로그인 후 복사

除了上面这种基本用法,还有其他使用方式

const people = {  name: "ES6",  age: 27,  sex: "male",
};// 一、属性顺序不需保持一致,名称相同即可const { age, name, sex } = people;console.log(name, age, sex);  //ES6 27 male//二、取值时,重新定义变量名const { age: newAge, name: newName, sex: newSex } = people;console.log(name, age, sex); //Uncaught ReferenceError: age is not definedconsole.log(newName, newAge, newSex); //ES6 27 male//三、赋值过程中设置默认值const { nickName = "昵称", age } = people;console.log(nickName, age); //昵称 27//四、reset运算符。只获取想要的属性,其他属性都放在新的变量里。const { name, ...peopleParams } = people;console.log(name, peopleParams); //ES6 {age: 27, sex: "male"}//五、嵌套对象取值const people = {  name: "ES6",  address: {    province: "江苏",
  },
};const { address: { province }} = people;console.log(province); //江苏复制代码
로그인 후 복사

数组解构赋值

假如我们拿到一个数组,需要获取指定的元素值。

const [a, b, c] = [1, 2, 3];console.log(a, b, c);   //1 2 3复制代码
로그인 후 복사

除了上面这种基本用法,还有其他使用方式

//一、待解构的除了是数组,还可以是任意可遍历的对象const [a, b, c] = new Set([1, 2, 3]);console.log(a, b, c); //1 2 3//二、被赋值的变量还可以是对象的属性,不局限于单纯的变量const num = {};
[num.a, num.b, num.c] = [1, 2, 3];console.log(num); //{a: 1, b: 2, c: 3}//三、解构赋值在循环体中的应用const num = {  a: 10,  b: 20,  c: 30,
};for (const [key, value] of Object.entries(num)) {  console.log(key, value); //a 10    b 20    c 30}//四、跳过赋值元素const [a, , c] = [1, 2, 3]; //存在空位的数组叫稀疏数组console.log(a, c);  //1 3//五、rest 参数const [a,...other] = [1, 2, 3];console.log(a, other);  //1    [2, 3]//六、赋值过程中设置默认值const [a, , , d = 10] = [1, 2, 3];console.log(d); //10复制代码
로그인 후 복사

字符串解构赋值

字符串解构赋值可以当成数组解构赋值

const [a, b, c, d] = "ECMAScript2015";console.log(a, b, c, d); //E C M A复制代码
로그인 후 복사

对象的扩展


ES6对对象进行了很多的扩展,具体如下

属性的简洁表示法

从ES6开始,如果对象的属性名和属性值相同,则有简写的方式。

let province = "江苏";const address = {
  province, //等同于 province: province
  city: "南京",
};复制代码
로그인 후 복사

属性名表达式

从ES6开始,可以使用变量或表达式定义对象的属性。

let key = "province";const address = {
  [key]: "省份",  city: "南京",
};console.log(address); //{province: "省份", city: "南京"}复制代码
로그인 후 복사

Object.is()

判断两个值是否是同一个值。在Object.is()之前,有“==”和“===”两种方式判断值是否相等,但这两个方式都有一定缺陷,如下

//== 在判断相等前会对不是同一类型的变量进行强制转换,最终导致“”与false相等console.log("" == false);   //true//=== 会将-0与+0视为相等,而将Number.NaN与NaN视为不相等console.log(-0 === +0); //trueconsole.log(Number.NaN === NaN);    //false复制代码
로그인 후 복사

所以,需要一种运算,在所有场景下,只要两个值是一样的,那么就应该相等,在实际项目开发过程中,推荐使用Object.is()来判断值相等。

console.log(Object.is(-0, +0)); //falseconsole.log(Object.is(Number.NaN, NaN)); //truelet a = { value: 1 };let b = { value: 1 };console.log(Object.is(a, b)); //false  对象都是同一个引用才相等复制代码
로그인 후 복사

Object.assign()


用于将所有可枚举属性的值从一个或多个源对象复制到目标对象,它将返回目标对象。

语法:

Object.assign(target, ...sources)     参数说明: target:目标对象 sources:源对象 返回值:合并后的目标对象

const target = { a: 1,};const source = { b: "B", c: "C" };const assignObj = Object.assign(target, source);console.log(assignObj); //{a: 1, b: "B", c: "C"}  //其他应用//一、如果目标对象与源对象属性具有相同值,则源对象属性值会覆盖目标对象属性值const target = { a: 1,b: 2};const source = { b: "B", c: "C" };const assignObj = Object.assign(target, source);console.log(assignObj); //{a: 1, b: "B", c: "C"}    //目标对象的b属性值被覆盖//二、源对象可以有多个值const target = { a: 1 };const source1 = { b: "B", c: "C" };const source2 = { d: "D", e: "E" };const assignObj = Object.assign(target, source1, source2);console.log(assignObj); //{a: 1, b: "B", c: "C", d: "D", e: "E"}复制代码
로그인 후 복사

对象遍历

假如我们想要循环遍历一个对象的键与值,则可以使用下面几种方式进行遍历

const score = {  name: "mango",  age: "25",  score: 80,
};//for...infor (let key in score) {  console.log(key, score[key]); // 分别输出:name mango 、 age 25 、score 80}//Object.keys()用来获取所有key组成的数组Object.keys(scoreObj).forEach(key => {  console.log(key, scoreObj[key]) //分别输出:name mango 、 age 25 、score 80})//Object.getOwnPropertyNames()用来获取所有key组成的数组Object.getOwnPropertyNames(scoreObj).forEach(key => {  console.log(key, scoreObj[key]) //分别输出:name mango 、 age 25 、score 80})//Reflect.ownKeys()用来获取所有key组成的数组Reflect.ownKeys(scoreObj).forEach(key => {  console.log(key, scoreObj[key]) //分别输出:name mango 、 age 25 、score 80})复制代码
로그인 후 복사

Class

JavaScript是一种基于对象的语言,我们遇到的所有东西几乎都是对象,但ES6之前是没有class的,而在ES6版本中正式引入了class,让JavaScript成为了一种真正的面向对象语言,我们可以像下面这样在JavaScript中进行面向对象编程。

//通过class关键字定义类class People{  
  //类的构造函数
  constructor(name, age) {    this.name = name;    this.age = age;
  }  //实例方法
  getName() {    return this.name;
  }  //静态方法
  static say() {    console.log("Hello ES6");
  }
}//继承class Student extends People {  constructor(name, age) {    super(name, age);
  }
}//对象创建与调用let student = new Student("mango", "27");
student.getName();
Student.say();复制代码
로그인 후 복사

通过上面的代码,我们具体说明下JavaScript中进行面向对象编程。

类的声明

通过class关键字声明类,支持构造函数construct做对象初始化。

class People{  
  constructor() {    //初始化
  }
}复制代码
로그인 후 복사

属性

Class对象中有两种对象属性,分别是实例属性和静态属性。实例属性必须定义在类的方法里,而静态属性必须定义在类的外面。

class People{  constructor() {    //定义实例属性
    this.name = "";    this.age = 0;
  }
}

People.desc="类描述";  //定义的静态属性//访问People people=new People();console.log(people.name);console.log(People.name);复制代码
로그인 후 복사

类中定义的属性,默认都是可读可写的,但是如果这时候我们想指定属性不可被修改该如何实现呢?那么便要用到set和get了,set和get可以定义一个属性,但是如果只有get而没有set,则属性不可以进行修改。

class People {  get sex() {    return "男";
  }
}let people = new People();console.log(people.sex);
people.sex="女" //Uncaught TypeError: Cannot set property sex of #<people> which has only a getter复制代码</people>
로그인 후 복사

方法

Class对象中有三种方法,分别是构造方法、实例方法还有静态方法。

class People {  //构造方法
  constructor(name, age) {    this.nameA = name;    this.age = age;
  }  //实例方法
  getName() {    return this.nameA;
  }  //静态方法
  static say() {    console.log("Hello " + People.desc);
  }
}

People.desc = "类描述";let people = new People("mango", "27");let name = people.getName();console.log(name); //mangoPeople.say(); //Hello 类描述复制代码
로그인 후 복사

继承

继承是面向对象语言很重要的一大特征,ES6新加入了extends和super关键字来实现继承。

class People {  constructor(name) {    this.name = name;
  }

  getName() {    return this.name;
  }
}//继承class Student extends People {  constructor(name, age) {    super(name, age);
  }
}//Student类继承了People类,student对象中super调用了父类的构造函数,并传递了name参数,因为继承的特性,student也拥有了父类的getName()方法let student = new Student("ES6");console.log(student.getName());复制代码
로그인 후 복사


通过以上对class的学习,我们得知道其实class并不是新引入的数据类型,其实class只是一种语法糖,它的实质完全可以看作构造函数的另一种写法。

class People {  constructor(name) {    this.name = name;
  }

  getName() {    return this.name;
  }
}console.log(typeof People); //functionconsole.log(People.prototype);  //{constructor: ƒ, getName: ƒ}复制代码
로그인 후 복사

JS是单线程的

异步编程其实就是处理异步任务,在进行异步编程之前,我们需要了解JavaScript是单线程的,在同一时间只能做一件事。

JavaScript之所以设计成单线程,是与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动以及操作DOM。这决定了它只能是单线程,否则会带来很多复杂的同步问题。例如,如果JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器就不晓得以哪个线程为准。所以,为了避免复杂,从一诞生,JavaScript就是单线程的。

单线程就意味着,所有任务都需要排队,前一个任务结束,后一个任务才会执行。那么如果前一个任务很长的话,那么后面一个任务不是就一直需要等待了吗?于是乎,JS将所有任务分成了两类,同步和异步。
同步:只有前一个任务执行完毕,才能执行后一个任务
异步:当同步任务执行到某个需要耗时执行的运行环境API时,就会触发异步任务,此时运行环境(浏览器或Node)就会单独开线程去处理这些异步任务。

JavaScript运行原理

下面是JavaScript运行原理图,同步任务在JS主线程完成,异步任务则新开一个线程

ECMAScript의 새로운 기능에 대한 포괄적인 숙달

疑问:不是说JavaScript是单线程的吗,那为什么又新开了一条线程处理任务呢,这不是多线程方式吗?
有这个疑问不奇怪,我们需要清楚JavaScript单线程其实说的是JavaScript引擎是单线程的,开发者只能通过单线程的方式进行JavaScript开发,而新开了一条线程处理任务是底层执行环境决定的,JavaScript执行环境是多线程。


在实际项目中,异步编程使用场景极其之多,请求个接口数据、创建个定时器、缓存个数据都离不开异步编程的身影,为了更好的处理异步任务,ES6给我们提供两种新的方式,分别是Promise和Generator。

Promise

Promise 是一个代理对象,代表了一个异步任务的最终成功或者失败状态。Promise允许你为异步任务的成功或失败分别设置对应的处理方法,以类似同步的方式便捷的进行异步编程。

一个Promise有三种状态:

  • pending:初始状态,既不是成功,也不是失败状态。
  • fulfilled:成功状态,代表着任务执行完成
  • rejected:失败状态,代表着任务执行失败

基本使用

创建Promise对象

const promise = new Promise(function (resolve, reject) {    let result=执行异步任务;    if(result){        //如果异步任务成功完成
        resolve()
    }else{        //如果异步任务执行失败
        reject();
    }
});复制代码
로그인 후 복사

创建Promise对象需要传递一个executor参数,executor是带有resolve和reject两个参数的函数,这两个参数是JavaScript引擎提供的两个函数,Promise构造函数执行会立即调用executor函数。

  • 当Promise中的异步任务执行成功时,调用resolve(),将Promise对象的状态从pending改为fulfilled(未完成到成功)
  • 当Promise中的异步任务执行失败时,调用reject(),将Promise对象的状态从pending改为rejected(未完成到失败)


ECMAScript의 새로운 기능에 대한 포괄적인 숙달

如何使用

实例好promise对象后,我们可以使用下面的语法来对异步任务完成后的状态进行处理。

promise.then(onFulfilled,onRejected)

promise.then(  (result) => {    console.log("异步任务处理成功,执行相应方法");
  },  (error) => {    console.log("异步任务处理失败,执行相应方法");
  }
);复制代码
로그인 후 복사

但在具体的项目开发中,我们通常都是使用已经存在的Promise对象,下面我们就通过使用常用的promise对象fetch获取接口数据。

案例实现

我们需要调用接口,获取一个用户列表数据

fetch("http://jsonplaceholder.typicode.com/users")
  .then(function (response) {    return response.json();
  })
  .then(function (res) {    console.log(res);   // [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
  });复制代码
로그인 후 복사

response是一个包含响应结果的Response对象,它只是一个HTTP响应,而不是真正的JSON。为了获取JSON的内容,需要使用json()方法获取一个Promise对象,然后再使用then获取JSON数据。

其他使用

Promise.prototype.catch()

Promise提供了catch()方法,用来捕获异步操作过程中遇到的错误异常,使用场景如下

const CatchPromise = new Promise(function (resolve, reject) {
  reject(new Error("error msg"));
});

CatchPromise.then().catch((e) => {  console.error(e); //Error: error msg});复制代码
로그인 후 복사

在Promise对象中,除了可以使用reject(new Error())的方式触发异常,还可以使用throw new Error()的方式触发异常,但不建议使用throw new Error()的方式,因为这种方式不会改变Promise的状态。

Promise.prototype.all()

Promise.all()用于处理多个异步任务,例如处理多张图片上传。Promise.all()接受一个promise对象数组作为参数,执行完毕返回一个Promise对象。

Promise.all()的状态变化:
传入的promise对象数组全部变为fulfill状态则返回成功,调用resolve()
传入的promise对象数组有一个变为reject状态则返回失败,调用reject()

const promise1 = new Promise(function (resolve, reject) {  setTimeout(function () {
    resolve("promise1");
  }, 2000);
});const promise2 = new Promise(function (resolve, reject) {  setTimeout(function () {
    resolve("promise2");
  }, 1000);
});const promise3 = new Promise(function (resolve, reject) {  setTimeout(function () {
    resolve("promise3");
  }, 3000);
});const promiseAll = Promise.all([promise1, promise2, promise3]);
promiseAll.then(function (results) {  console.log(results); // ["promise1", "promise2", "promise3"]});复制代码
로그인 후 복사

Promise.prototype.race()

Promise.race()也是用于处理多个异步任务,与Promise.all()一样,Promise.race()接受一个promise对象数组作为参数,执行完毕返回一个Promise对象。

Promise.race()的状态变化:
传入的promise对象数组有一个变为resolve状态则返回成功,调用resolve()
传入的promise对象数组有一个变为reject状态则返回失败,调用reject()

const promise1 = new Promise(function (resolve, reject) {  setTimeout(function () {
    reject("promise1");
  }, 2000);
});const promise2 = new Promise(function (resolve, reject) {  setTimeout(function () {
    resolve("promise2");
  }, 1000);
});const promise3 = new Promise(function (resolve, reject) {  setTimeout(function () {
    resolve("promise3");
  }, 3000);
});const promiseAll = Promise.race([promise1, promise2, promise3]);
promiseAll.then(function (results) {  console.log(results); // promise2});复制代码
로그인 후 복사

Generator函数

Generator函数是用来处理异步任务的函数,函数内部包裹的就是异步任务的处理。Generator不同于普通函数,当执行到异步任务,可以暂停,直到异步任务执行完毕再继续往下执行,类似同步的方法进行异步编程。

如何使用

Generator函数在使用上具体有以下特点

  • :定义generator函数时,function后面需要加上星号,例如function generatorFuncName()
  • yield:需要暂停去执行异步任务时,需要在代码前面加上yield标识,例如yield fetch()
  • next():调用generator函数后获得的是一个指针对象,调用指针的next方法,可以用来分阶段逐步执行generator函数。


那么具体如何使用generator函数实现异步编程呢,在学习Promise的时候,我们实现了一个获取一个用户列表数据的案例,下面我们看看如何使用generator函数实现吧。

function* loadUsers() {  const API = "http://jsonplaceholder.typicode.com/users";  console.log("等待数据请求");  yield fetch(API); //暂停,开始执行异步任务
  console.log("数据请求完成");  console.log("继续其他逻辑操作");
}const generator = loadUsers();const promise = generator.next().value;console.log(promise);

promise
  .then(function (response) {    return response.json();
  })
  .then(function (result) {    console.log(result); //[{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
    generator.next(); //打印:数据请求完成  继续其他逻辑操作。异步任务执行完毕后调用next(),继续执行generator函数中后续代码
  });复制代码
로그인 후 복사

Proxy

Proxy翻译过来叫代理,Proxy可以通过自定义行为来改变对象的基本操作,例如属性赋值、查找、枚举、函数调用等。

基本语法

const p=new Proxy(target,handler); 参数说明: target:需要使用Proxy包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至是另外一个代理) handler:代理目标对象基本操作的对象 返回值: p:target被代理后不可以直接访问,而只能访问

Proxy使用场景

设置对象属性的读写权限

let people = {  name: "mango",  //设置属性不可读
  age: 27,  //设置属性不可被修改};let peopleHandler = {  //设置属性值读取的捕捉器
  get: function (target, prop, receiver) {    if (Object.is(prop, "name")) {      return prop + "属性不可读";
    }
  },  //设置属性值操作的捕捉器
  set: function (target, prop, value) {    if (Object.is(prop, "age")) {      throw new Error(prop + "属性不可写");
    }
  },
};let peopleProxy = new Proxy(people, peopleHandler);
peopleProxy.age = 10; // Uncaught Error: age属性不可写console.log(peopleProxy.name); //输出:name属性不可读复制代码
로그인 후 복사

给接口返回的对象中字段为null的属性设置默认值

let people = {  address: null,
};let peopleHandler = {  //设置属性值读取的捕捉器
  get: function (target, prop, receiver) {    return target[prop] ?? "默认值";  //空值合并操作符
  },
};let peopleProxy = new Proxy(people, peopleHandler);console.log(peopleProxy.address); //输出:默认值复制代码
로그인 후 복사

拦截函数调用

//handler.apply()用于拦截函数的调用function sum(a, b) {  console.log(a + b);  return a + b;
}////对于sum方法,关注的是处理数据相加的逻辑//通过代理则可以处理在调用方法时候,对参数的校验,数据打点等const sumProxy = new Proxy(sum, {  apply: function (target, thisArg, argumentsList) {    console.log("调用了方法", "打点");
  },
});

sumProxy(1, 2);复制代码
로그인 후 복사

**

Module


ES6在语言标准上,通过Module实现了模块功能,现阶段几乎取代之前用来实现JavaScript模块化的CommonJS和AMD规范,成为了浏览器环境和node环境通用的模块化解决方案。
Module实现的模块化属于“编译时加载”,即在编译时就完成了模块之间的加载,通过这种“编译时加载”的方式,使得在不运行代码的情况下就可以通过词法分析、语法分析等对程序代码进行扫描,以验证代码的规范性、安全性和可维护性,让静态分析成为了可能。

如何使用


Module实现的模块化功能主要有两个命令构成:

  • export:导出命令,用于提供模块的对外接口
  • import:导入命令,用于引入其他模块提供的接口


一个模块就是一个独立的文件,该文件内的所有变量,外部无法获取,如果想要外部获取模块内的某些变量,就必须使用export关键字导出变量,在需要引入该导出变量的的模块中必须使用import关键字引入变量。

export

下面举例说明export命令导出对外接口的几种方式,在ExportDemo.js文件中,

  • 导出变量
//方法一export let a = 1;//方法二let b = 2;export { b };复制代码
로그인 후 복사
  • 导出函数
//方法一export function test(){    console.log("name");
}//方法二let test2=function test(){    console.log("name");
}export {test2 as newName}复制代码
로그인 후 복사

注意在方法二中,使用了as关键字,as关键字可以在导出时重命名对外的接口名。

  • 导出类
//方法一export class People {
  say() {      console.log("Hello Module");
  }
}//方法二export { People };复制代码
로그인 후 복사

import

使用export导出了模块中的对外接口后,其他JS文件就可以通过import关键字加载这个模块,使用如下。

//大括号中的变量名必须与被导出对外接口名一致import { a, b, test as newTest, People } from "./ExportDemo";//导入a和b的变量console.log(a, b);//导入test方法,同样可以使用as关键字在导入的时候重命名newTest();//导入People类let people = new People();
people.say();复制代码
로그인 후 복사


其他使用方式**
在日常开发过程中,Module模块化还有下面几种常见的使用方式。

  • 使用*指定一个对象,加载模块中的所有导出
//import { a, b, test, People } from "./ExportDemo";//上面的导入方式可以改写成下面方式import * as ExportModule from "./ExportModule";//使用的使用,加上前缀即可ExportModule.test()复制代码
로그인 후 복사
  • 通过export default,为模块指定默认导出名
//导出const People = {  say: function () {    console.log("Hello Module");
  },
};export default People;//导入import People from "./ExportModule";

People.say(); //Hello Module复制代码
로그인 후 복사
  • export和import一起使用,处理先输入后输出的情况
//假如有a、b、c三个文件模块,//c文件模块如下 c.jslet people={name:"mango",age:27};let address="南京";export { people, address };//有下面几种使用场景//一、在b中导入c中的people和address,并导出给a使用export {people,address} from 'c'//二、在b中整体导入c,并导出给a使用export * from 'c'//三、在b中导入people,并作为b的导出名称【具名接口改为默认接口】export {people as default} from 'c'//当c的导出方式为export default的时候,并可以使用【默认接口改为具名接口】export {default as NewPeople} from 'c'复制代码
로그인 후 복사

相关免费学习推荐:javascript(视频)

위 내용은 ECMAScript의 새로운 기능에 대한 포괄적인 숙달의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:juejin.im
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿