検索
ホームページウェブフロントエンドjsチュートリアルECMAScript の新機能を包括的に習得する

Todayjavascript コラムでは、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复制代码

上には 2 つのブロック レベル スコープがあり、どちらも変数 a を宣言していますが、外側のブロック レベル スコープは内側のブロック レベル スコープとは何の関係もないため、(1) の出力は次のようになります。外部変数。値 1、(2) はブロックレベルのスコープで定義されていない変数にアクセスするため、エラーが報告されます。

ブロックレベルのスコープを理解する別の例。

//for循环体内的定时器//在ES6之前,是没有块级作用域的,变量用var声明,直接挂载在全局作用域上for (var i = 0; i <h4 id="一時的なデッドゾーン">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 はグローバル オブジェクト (ウィンドウ、グローバル) からアクセスできません, self)

let a = 10;console.log(window.a);  //undefined复制代码

String

ES6 は、次のように文字列にいくつかの拡張を加えました。

テンプレート文字列

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世界复制代码

メソッド

String.prototype.includes()

文字列に指定された文字列が含まれているかどうかを判断し、ブール型を返します。

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

startsWith() および opensWith()

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);复制代码


2. オブジェクトとしての一意の属性値
会社名オブジェクトを生成し、各人の名前をキー値として使用したい場合、問題が発生します。誰かが同じ名前を持っています。Symbol はこの問題を解決できます

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

Symbol で定義されたプロパティは次の 2 つの方法でのみトラバースできることに注意してください。そうでない場合はプロパティを取得できません。

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

Set と Map


データ操作をより便利に実装するために、ES6 には Set と Map という 2 つの新しいデータ構造が追加されています。

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) {}复制代码

一般的なアプリケーション

Set データ構造には、実際のプロジェクトでの多くの応用シナリオがあります。

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 中国語 Web サイトの他の関連記事を参照してください。

声明
この記事はjuejinで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。
JavaScriptエンジン:実装の比較JavaScriptエンジン:実装の比較Apr 13, 2025 am 12:05 AM

さまざまなJavaScriptエンジンは、各エンジンの実装原則と最適化戦略が異なるため、JavaScriptコードを解析および実行するときに異なる効果をもたらします。 1。語彙分析:ソースコードを語彙ユニットに変換します。 2。文法分析:抽象的な構文ツリーを生成します。 3。最適化とコンパイル:JITコンパイラを介してマシンコードを生成します。 4。実行:マシンコードを実行します。 V8エンジンはインスタントコンピレーションと非表示クラスを通じて最適化され、Spidermonkeyはタイプ推論システムを使用して、同じコードで異なるパフォーマンスパフォーマンスをもたらします。

ブラウザを超えて:現実世界のJavaScriptブラウザを超えて:現実世界のJavaScriptApr 12, 2025 am 12:06 AM

現実世界におけるJavaScriptのアプリケーションには、サーバー側のプログラミング、モバイルアプリケーション開発、モノのインターネット制御が含まれます。 2。モバイルアプリケーションの開発は、ReactNativeを通じて実行され、クロスプラットフォームの展開をサポートします。 3.ハードウェアの相互作用に適したJohnny-Fiveライブラリを介したIoTデバイス制御に使用されます。

next.jsを使用してマルチテナントSaaSアプリケーションを構築する(バックエンド統合)next.jsを使用してマルチテナントSaaSアプリケーションを構築する(バックエンド統合)Apr 11, 2025 am 08:23 AM

私はあなたの日常的な技術ツールを使用して機能的なマルチテナントSaaSアプリケーション(EDTECHアプリ)を作成しましたが、あなたは同じことをすることができます。 まず、マルチテナントSaaSアプリケーションとは何ですか? マルチテナントSaaSアプリケーションを使用すると、Singの複数の顧客にサービスを提供できます

next.jsを使用してマルチテナントSaaSアプリケーションを構築する方法(フロントエンド統合)next.jsを使用してマルチテナントSaaSアプリケーションを構築する方法(フロントエンド統合)Apr 11, 2025 am 08:22 AM

この記事では、許可によって保護されたバックエンドとのフロントエンド統合を示し、next.jsを使用して機能的なedtech SaaSアプリケーションを構築します。 FrontEndはユーザーのアクセス許可を取得してUIの可視性を制御し、APIリクエストがロールベースに付着することを保証します

JavaScript:Web言語の汎用性の調査JavaScript:Web言語の汎用性の調査Apr 11, 2025 am 12:01 AM

JavaScriptは、現代のWeb開発のコア言語であり、その多様性と柔軟性に広く使用されています。 1)フロントエンド開発:DOM操作と最新のフレームワーク(React、Vue.JS、Angularなど)を通じて、動的なWebページとシングルページアプリケーションを構築します。 2)サーバー側の開発:node.jsは、非ブロッキングI/Oモデルを使用して、高い並行性とリアルタイムアプリケーションを処理します。 3)モバイルおよびデスクトップアプリケーション開発:クロスプラットフォーム開発は、反応および電子を通じて実現され、開発効率を向上させます。

JavaScriptの進化:現在の傾向と将来の見通しJavaScriptの進化:現在の傾向と将来の見通しApr 10, 2025 am 09:33 AM

JavaScriptの最新トレンドには、TypeScriptの台頭、最新のフレームワークとライブラリの人気、WebAssemblyの適用が含まれます。将来の見通しは、より強力なタイプシステム、サーバー側のJavaScriptの開発、人工知能と機械学習の拡大、およびIoTおよびEDGEコンピューティングの可能性をカバーしています。

javascriptの分解:それが何をするのか、なぜそれが重要なのかjavascriptの分解:それが何をするのか、なぜそれが重要なのかApr 09, 2025 am 12:07 AM

JavaScriptは現代のWeb開発の基礎であり、その主な機能には、イベント駆動型のプログラミング、動的コンテンツ生成、非同期プログラミングが含まれます。 1)イベント駆動型プログラミングにより、Webページはユーザー操作に応じて動的に変更できます。 2)動的コンテンツ生成により、条件に応じてページコンテンツを調整できます。 3)非同期プログラミングにより、ユーザーインターフェイスがブロックされないようにします。 JavaScriptは、Webインタラクション、シングルページアプリケーション、サーバー側の開発で広く使用されており、ユーザーエクスペリエンスとクロスプラットフォーム開発の柔軟性を大幅に改善しています。

pythonまたはjavascriptの方がいいですか?pythonまたはjavascriptの方がいいですか?Apr 06, 2025 am 12:14 AM

Pythonはデータサイエンスや機械学習により適していますが、JavaScriptはフロントエンドとフルスタックの開発により適しています。 1. Pythonは、簡潔な構文とリッチライブラリエコシステムで知られており、データ分析とWeb開発に適しています。 2。JavaScriptは、フロントエンド開発の中核です。 node.jsはサーバー側のプログラミングをサポートしており、フルスタック開発に適しています。

See all articles

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

Dreamweaver Mac版

Dreamweaver Mac版

ビジュアル Web 開発ツール

WebStorm Mac版

WebStorm Mac版

便利なJavaScript開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

mPDF

mPDF

mPDF は、UTF-8 でエンコードされた HTML から PDF ファイルを生成できる PHP ライブラリです。オリジナルの作者である Ian Back は、Web サイトから「オンザフライ」で PDF ファイルを出力し、さまざまな言語を処理するために mPDF を作成しました。 HTML2FPDF などのオリジナルのスクリプトよりも遅く、Unicode フォントを使用すると生成されるファイルが大きくなりますが、CSS スタイルなどをサポートし、多くの機能強化が施されています。 RTL (アラビア語とヘブライ語) や CJK (中国語、日本語、韓国語) を含むほぼすべての言語をサポートします。ネストされたブロックレベル要素 (P、DIV など) をサポートします。