第1集丨ES6 江湖——概述

news/2024/4/26 6:13:55/文章来源:https://blog.csdn.net/DUQGQG/article/details/131697445

目录

  • 前言:ES6简介
    • 1.1 ECMAScript 和 JavaScript 的关系
    • 1.2 ES6 与 ECMAScript 2015 之间的关系
    • 1.3 语法提案的批准流程
    • 1.4 ECMAScript 的历史
  • 1. let、const 和 块级 作用域
    • 1.1 块级作用域
    • 1.2 不具备变量提升(hoisting)
    • 1.3 const
    • 1.4 const 优于let
  • 2. 箭头函数(Arrow Functions)
    • 2.1 简洁函数体
    • 2.2 正常函数体
    • 2.3 this
  • 3. 函数参数默认值
  • 4. Spread / Rest 操作符
  • 5. 对象词法扩展
  • 6. 二进制和八进制字面量
  • 7. 对象和数组解构
  • 8. 对象超类
  • 9. 模板语法和分隔符
  • 10. for...of VS for...in
  • 11. Map 和 WeakMap
    • 11.1 Map
    • 11.2 WeakMap
  • 12. Set 和 WeakSet
    • 12.1 set
    • 12.2 WeakSet
  • 13. 类
    • 13.1 class和 static
    • 13.2 extends
  • 14. Symbol
  • 15. 迭代器(Iterators)
  • 16. Generators
  • 17. Promises
  • 16. 数组
    • 16.1 使用扩展运算符(...)拷贝数组。
    • 16.2 Array.from
  • 17. 函数
    • 17.1 IIFE箭头函数形式
    • 17.2 匿名函数用箭头函数替代
    • 17.3 参数方式
    • 17.4 使用rest运算符

前言:ES6简介

ES6,全称ECMAScript 6.0,是 JavaScript 的下一个版本标准,2015.06 发版。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。

ECMAScript 是JavaScript语言的国际标准,JavaScriptECMAScript的实现。

ES6 经过持续几年的磨砺,它已成为 JS 有史以来最实质的升级,特性涵盖范围甚广, 小到受欢迎的语法糖,例如箭头函数(arrow functions)和简单的字符串插值(string interpolation),大到烧脑的新概念,例如代理(proxies)和生成器(generators);它将彻底改变程序员们编写JS代码的方式。

ES6 主要是为了解决 ES5 的先天不足,比如 JavaScript 里并没有类的概念,但是目前浏览器的 JavaScript 是 ES5 版本,大多数高版本的浏览器也支持 ES6,不过只实现了 ES6 的部分特性和功能。

1.1 ECMAScript 和 JavaScript 的关系

要讲清 ECMAScriptJavaScript 之间的关系,就需要回顾历史。1996 年 11 月,JavaScript 的创造者 Netscape 公司,决定将 JavaScript 提交给标准化组织 ECMA(European computer manufactures association,欧洲计算机制造联合会),希望这种语言能够成为国际标准。次年,ECMA 发布 262 号标准文件(ECMA-262)的第一版,规定了浏览器脚本语言的标准,并将这种语言称为 ECMAScript,这个版本就是 1.0版。

该标准从一开始就是针对 JavaScript 语言制定的,但是之所以不叫 JavaScript,有两个原因。一是商标,Java 是 Sun 公司的商标,根据授权协议,只有 Netscape 公司可以合法地使用 JavaScript 这个名字,且 JavaScript 本身也已经被 Netscape 公司注册为商标。二是想体现这门语言的制定者是 ECMA,不是 Netscape,这样有利于保证这门语言的开放性和中立性。

因此,ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现(另外的 ECMAScript 方言还有 JScriptActionScript)。日常场合,这两个词是可以互换的。

1.2 ES6 与 ECMAScript 2015 之间的关系

ECMAScript 2015(简称 ES2015),在2011年,发布了ECMAScript 5.1 版本,之后就开始制定 6.0 版了。因此,ES6 这个词的原意,就是指 JavaScript 语言的下一个版本。

但是,因为这个版本引入的语法功能太多,而且制定过程当中,还有很多组织和个人不断提交新功能。事情很快就变得清楚了,不可能在一个版本里面包括所有将要引入的功能。常规的做法是先发布 6.0 版,过一段时间再发 6.1 版,然后是 6.2 版、6.3 版等等。

但是,标准的制定者不想这样做。他们想让标准的升级成为常规流程:任何人在任何时候,都可以向标准委员会提交新语法的提案,然后标准委员会每个月开一次会,评估这些提案是否可以接受,需要哪些改进。如果经过多次会议以后,一个提案足够成熟了,就可以正式进入标准了。这就是说,标准的版本升级成为了一个不断滚动的流程,每个月都会有变动。

标准委员会最终决定,标准在每年的 6月份正式发布一次,作为当年的正式版本。接下来的时间,就在这个版本的基础上做改动,直到下一年的 6 月份,草案就自然变成了新一年的版本。这样一来,就不需要以前的版本号了,只要用年份标记就可以了。

ES6 的第一个版本,就这样在 2015 年 6 月发布了,正式名称就是《ECMAScript 2015 标准》(简称 ES2015)。2016 年 6 月,小幅修订的《ECMAScript 2016 标准》(简称 ES2016)如期发布,这个版本可以看作是 ES6.1 版,因为两者的差异非常小(只新增了数组实例的includes方法和指数运算符),基本上是同一个标准。根据计划,2017 年 6 月发布 ES2017 标准。

因此,ES6 既是一个历史名词,也是一个泛指,含义是 5.1 版以后的 JavaScript 的下一代标准,涵盖了 ES2015、ES2016、ES2017 等等,而 ES2015 则是正式名称,特指该年发布的正式版本的语言标准。本书中提到 ES6 的地方,一般是指 ES2015 标准,但有时也是泛指“下一代 JavaScript 语言”。

1.3 语法提案的批准流程

任何人都可以向标准委员会(又称 TC39 委员会)提案,要求修改语言标准。

一种新的语法从提案到变成正式标准,需要经历五个阶段。每个阶段的变动都需要由 TC39 委员会批准。

  • Stage 0 - Strawman(展示阶段)
  • Stage 1 - Proposal(征求意见阶段)
  • Stage 2 - Draft(草案阶段)
  • Stage 3 - Candidate(候选人阶段)
  • Stage 4 - Finished(定案阶段)

一个提案只要能进入 Stage 2,就差不多肯定会包括在以后的正式标准里面。

1.4 ECMAScript 的历史

ES6 从开始制定到最后发布,整整用了15 年。

前面提到,ECMAScript 1.01997 年发布的,接下来的两年,连续发布了 ECMAScript 2.0(1998 年 6 月)和 ECMAScript 3.0(1999 年 12 月)。3.0 版是一个巨大的成功,在业界得到广泛支持,成为通行标准,奠定了 JavaScript 语言的基本语法,以后的版本完全继承。直到今天,初学者一开始学习 JavaScript,其实就是在学 3.0 版的语法。

2000 年,ECMAScript 4.0 开始酝酿。这个版本最后没有通过,但是它的大部分内容被 ES6 继承了。因此,ES6 制定的起点其实是 2000 年

为什么 ES4 没有通过呢? 因为这个版本太激进了,对 ES3 做了彻底升级,导致标准委员会的一些成员不愿意接受。ECMA 的第 39 号技术专家委员会(Technical Committee 39,简称 TC39)负责制订 ECMAScript 标准,成员包括 Microsoft、Mozilla、Google 等大公司。

2007 年 10 月,ECMAScript 4.0 版草案发布,本来预计次年 8 月发布正式版本。但是,各方对于是否通过这个标准,发生了严重分歧。以 Yahoo、Microsoft、Google 为首的大公司,反对 JavaScript 的大幅升级,主张小幅改动;以 JavaScript 创造者 Brendan Eich 为首的 Mozilla 公司,则坚持当前的草案。

2008 年 7 月,由于对于下一个版本应该包括哪些功能,各方分歧太大,争论过于激烈,ECMA 开会决定,中止 ECMAScript 4.0 的开发,将其中涉及现有功能改善的一小部分,发布为 ECMAScript 3.1,而将其他激进的设想扩大范围,放入以后的版本,由于会议的气氛,该版本的项目代号起名为 Harmony(和谐)。会后不久,ECMAScript 3.1 就改名为 ECMAScript 5

2009 年 12 月,ECMAScript 5.0 版正式发布。Harmony 项目则一分为二,一些较为可行的设想定名为 JavaScript.next 继续开发,后来演变成 ECMAScript 6;一些不是很成熟的设想,则被视为 JavaScript.next.next,在更远的将来再考虑推出。TC39 委员会的总体考虑是,ES5 与 ES3 基本保持兼容,较大的语法修正和新功能加入,将由 JavaScript.next 完成。当时,JavaScript.next 指的是 ES6,第六版发布以后,就指 ES7。TC39 的判断是,ES5 会在 2013 年的年中成为 JavaScript 开发的主流标准,并在此后五年中一直保持这个位置。

2011 年 6 月,ECMAScript 5.1 版发布,并且成为 ISO 国际标准(ISO/IEC 16262:2011)。

2013 年 3 月,ECMAScript 6 草案冻结,不再添加新功能。新的功能设想将被放到 ECMAScript 7。

2013 年 12 月,ECMAScript 6 草案发布。然后是 12 个月的讨论期,听取各方反馈。

2015 年 6 月,ECMAScript 6 正式通过,成为国际标准。从 2000 年算起,这时已经过去了 15 年。

总结来说

ES6 是 ECMAScript 标准十余年来变动最大的一个版本,为其添加了许多新的语法特性。

  • 1997 年 ECMAScript 1.0 诞生。
  • 1998 年 6 月 ECMAScript 2.0 诞生,包含一些小的更改,用于同步独立的 ISO 国际标准。
  • 1999 年 12 月 ECMAScript 3.0诞生,它是一个巨大的成功,在业界得到了广泛的支持,它奠定了 JS 的基本语法,被其后版本完全继承。直到今天,我们一开始学习 JS ,其实就是在学 3.0 版的语法。
  • 2000 年的 ECMAScript 4.0 是当下 ES6 的前身,但由于这个版本太过激烈,对 ES 3 做了彻底升级,所以暂时被"和谐"了。
  • 2009 年 12 月,ECMAScript 5.0 版正式发布。ECMA 专家组预计 ECMAScript 的第五个版本会在 2013 年中期到 2018 年作为主流的开发标准。2011年6月,ES 5.1 版发布,并且成为 ISO 国际标准。
  • 2013 年,ES6 草案冻结,不再添加新的功能,新的功能将被放到 ES7 中;
  • 2015年6月, ES6 正式通过,成为国际标准。

1. let、const 和 块级 作用域

ES6 提出了两个新的声明变量的命令:letconst 。其中,let 完全可以取代var ,因为两者语义相同,而且let没有副作用。因为let具有块级作用域,所声明的变量,只在其所在的代码块内有效。

有几个点需要注意:

  • let 关键词声明的变量不具备变量提升(hoisting)特性
  • letconst 声明只在最靠近的一个块中(花括号内)有效
  • 当使用常量 const 声明时,请使用大写变量,如:CAPITAL_CASING
  • const在声明时必须被赋值

1.1 块级作用域

ES5只有全局作用域函数作用域,没有块级作用域,这带来很多不合理的场景。

块级作用域{ ...}:就是有大括号的就算,例如 if 、for 等。

ES6以前没有块级作用域的时候,是怎么做的呢?

  • 用匿名函数立即执行(IIFE)的方式来模拟。因为在函数里面是局部作用域
if (true) {//var y = 10;let x = 'hello';
}
for (let i = 0; i < 10; i++) {console.log(i);
}
// 10 
//alert(y)
// Uncaught ReferenceError: x is not defined
alert(x)    

上面代码如果用 var 替代 let ,实际上就声明了两个全局变量,这显然不是本意。变量应该只在其声明的代码块内有效, var 命令做不到这一点。

1.2 不具备变量提升(hoisting)

var命令会发生“变量提升”现象,即变量可以在声明之前使用,值为undefined。这种现象多多少少是有些奇怪的,按照一般的逻辑,变量应该在声明语句之后才可以使用。

为了纠正这种现象,let命令改变了语法行为,它所声明的变量一定要在声明后使用,否则报错。

// var 的情况
console.log(foo); // 输出undefined
var foo = 2;
// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;

1.3 const

同样在块级作用域有效的另一个变量声明方式是 const,它可以声明一个常量。ES6 中,const 声明的常量类似于指针,它指向某个引用,也就是说这个「常量」并非一成不变的,如:

{const ARR = [5,6];ARR.push(7);console.log(ARR); // [5,6,7]ARR = 10; // TypeError
}

上面例子说明:当常量的值为对象得时候,不变得是其指针,但是 所指对象得本身是可以改变得。

1.4 const 优于let

letconst之间,建议优先使用const ,尤其是在全局环境,不应该设置变量,只应设置常量。所有的函数都应该设置为常量

const 优于 let 有几个原因。

  • 一个是 const 可以提醒阅读程序的人,这个变量不应该改变;
  • 另一个是 const 比较符合函数式编程思想,运算不改变值,只是新建值,而且这样也有利于将来的分布式运算;防止了无意间修改变量值所导致的错误。
  • 最后一个原因是 JavaScript 编译器会对 const 进行优化,所以多使用 const ,有利于提高程序的运行效率,也就是说 let 和 const 的本质区别,其实是编译器内部的处理不同。
// bad
var a = 1, b = 2, c = 3;
// good
const a = 1;
const b = 2;
const c = 3;
// best
const [a, b, c] = [1, 2, 3];

2. 箭头函数(Arrow Functions)

ES6 中,箭头函数就是函数的一种简写形式,使用括号包裹参数,跟随一个 =>,紧接着是函数体。

2.1 简洁函数体

var getPrice = function() {return 4.55;
};// Implementation with Arrow Function
var getPrice = () => 4.55;

2.2 正常函数体

需要注意的是,上面例子中的 getPrice 箭头函数采用了简洁函数体,它不需要 return 语句,下面这个例子使用的是正常函数体:

let arr = ['apple', 'banana', 'orange'];let breakfast = arr.map(fruit => {return fruit + 's';
});console.log(breakfast); // apples bananas oranges

2.3 this

当然,箭头函数不仅仅是让代码变得简洁,函数中 this 总是绑定总是指向对象自身。具体可以看看下面几个例子:

function Person() {this.age = 0;setInterval(function growUp() {// 在非严格模式下,growUp() 函数的 this 指向 window 对象this.age++;}, 1000);
}
var person = new Person();

我们经常需要使用一个变量来保存 this,然后在 growUp 函数中引用:

function Person() {var self = this;self.age = 0;setInterval(function growUp() {self.age++;}, 1000);
}

而使用箭头函数可以省却这个麻烦:

function Person(){this.age = 0;setInterval(() => {// |this| 指向 person 对象this.age++;}, 1000);
}var person = new Person();

3. 函数参数默认值

ES6 中允许你对函数参数设置默认值:

let getFinalPrice = (price, tax=0.7) => price + price * tax;
getFinalPrice(500); // 850

4. Spread / Rest 操作符

Spread / Rest 操作符指的是 ...,具体是 Spread 还是 Rest 需要看上下文语境。

  • 当被用于迭代器中时,它是一个 Spread 操作符:
function foo(x,y,z) {console.log(x,y,z);
}let arr = [1,2,3];
foo(...arr); // 1 2 3
  • 当被用于函数传参时,是一个 Rest 操作符:
function foo(...args) {console.log(args);
}
foo( 1, 2, 3, 4, 5); // [1, 2, 3, 4, 5]

5. 对象词法扩展

ES6 允许声明在对象字面量时使用简写语法,来初始化属性变量和函数的定义方法,并且允许在对象属性中进行计算操作

function getCar(make, model, value) {return {// 简写变量make,  // 等同于 make: makemodel, // 等同于 model: modelvalue, // 等同于 value: value// 属性可以使用表达式计算值['make' + make]: true,// 忽略 `function` 关键词简写对象函数depreciate() {this.value -= 2500;}};
}let car = getCar('Barret', 'Lee', 40000);// output: {
//     make: 'Barret',
//     model:'Lee',
//     value: 40000,
//     makeBarret: true,
//     depreciate: [Function: depreciate]
// }

6. 二进制和八进制字面量

ES6 支持二进制和八进制的字面量,通过在数字前面添加 0o 或者0O 即可将其转换为八进制值;二进制使用 0b 或者 0B

let oValue = 0o10;
console.log(oValue); // 8let bValue = 0b10; // 
console.log(bValue); // 2

7. 对象和数组解构

解构可以避免在对象赋值时产生中间变量:

function foo() {return [1,2,3];
}
let arr = foo(); // [1,2,3]let [a, b, c] = foo();
console.log(a, b, c); // 1 2 3function bar() {return {x: 4,y: 5,z: 6};
}
let {x: x, y: y, z: z} = bar();
console.log(x, y, z); // 4 5 6

8. 对象超类

ES6 允许在对象中使用 super 方法:

var parent = {foo() {console.log("Hello from the Parent");}
}var child = {foo() {super.foo();console.log("Hello from the Child");}
}Object.setPrototypeOf(child, parent);
child.foo(); // Hello from the Parent// Hello from the Child

9. 模板语法和分隔符

ES6 中有一种十分简洁的方法组装一堆字符串和变量。

  • ${ ... } 用来渲染一个变量,
  • ` 作为分隔符

静态字符串一律使用单引号或反引号,不使用双引号。 动态字符串使用反引号。

let user = 'Barret';
console.log(`Hi ${user}!`); // Hi Barret!

10. for…of VS for…in

  • for…of 用于遍历一个迭代器,如数组:
let nicknames = ['di', 'boo', 'punkeye'];
nicknames.size = 3;
for (let nickname of nicknames) {console.log(nickname);
}
// 结果: di, boo, punkeye
  • for…in 用来遍历对象中的属性:
let nicknames = ['di', 'boo', 'punkeye'];
nicknames.size = 3;
for (let nickname in nicknames) {console.log(nickname);
}//Result: 0, 1, 2, size

11. Map 和 WeakMap

ES6 中两种新的数据结构集:MapWeakMap。事实上每个对象都可以看作是一个 Map

11.1 Map

一个对象由多个 key-val 对构成,在 Map 中,任何类型都可以作为对象的 key,如:

var myMap = new Map();var keyString = "a string",keyObj = {},keyFunc = function () {};// 设置值
myMap.set(keyString, "value 与 'a string' 关联");
myMap.set(keyObj, "value 与 keyObj 关联");
myMap.set(keyFunc, "value 与 keyFunc 关联");myMap.size; // 3// 获取值
myMap.get(keyString);    // "value 与 'a string' 关联"
myMap.get(keyObj);       // "value 与 keyObj 关联"
myMap.get(keyFunc);      // "value 与 keyFunc 关联"

11.2 WeakMap

WeakMap 就是一个 Map,只不过它的所有 key 都是弱引用,意思就是 WeakMap 中的东西垃圾回收时不考虑,使用它不用担心内存泄漏问题。

另一个需要注意的点是,WeakMap 的所有 key 必须是对象。它只有四个方法 delete(key),has(key),get(key) 和set(key, val)

let w = new WeakMap();
w.set('a', 'b'); 
// Uncaught TypeError: Invalid value used as weak map keyvar o1 = {},o2 = function(){},o3 = window;w.set(o1, 37);
w.set(o2, "azerty");
w.set(o3, undefined);w.get(o3); // undefined, because that is the set valuew.has(o1); // true
w.delete(o1);
w.has(o1); // false

12. Set 和 WeakSet

12.1 set

Set 对象是一组不重复的值,重复的值将被忽略,值类型可以是原始类型和引用类型:

let mySet = new Set([1, 1, 2, 2, 3, 3]);
mySet.size; // 3
mySet.has(1); // true
mySet.add('strings');
mySet.add({ a: 1, b:2 });

可以通过 forEachfor...of 来遍历 Set 对象:

mySet.forEach((item) => {console.log(item);// 1// 2// 3// 'strings'// Object { a: 1, b: 2 }
});for (let value of mySet) {console.log(value);// 1// 2// 3// 'strings'// Object { a: 1, b: 2 }
}

Set 同样有 delete()clear() 方法。

12.2 WeakSet

类似于 WeakMapWeakSet 对象可以让你在一个集合中保存对象的弱引用,在 WeakSet 中的对象只允许出现一次:

var ws = new WeakSet();
var obj = {};
var foo = {};ws.add(window);
ws.add(obj);ws.has(window); // true
ws.has(foo);    // false, foo 没有添加成功ws.delete(window); // 从结合中删除 window 对象
ws.has(window);    // false, window 对象已经被删除

13. 类

13.1 class和 static

ES6 中有class 语法。值得注意是,这里的 class 不是新的对象继承模型,它只是原型链的语法糖表现形式。

函数中使用 static 关键词定义构造函数的的方法和属性(可理解为静态属性和方法):

class Task {constructor() {console.log("task instantiated!");}showId() {console.log(23);}static loadAll() {console.log("Loading all tasks..");}
}console.log(typeof Task); // function
let task = new Task(); // "task instantiated!"
task.showId(); // 23
Task.loadAll(); // "Loading all tasks.."

13.2 extends

类中的继承和超集:

class Car {constructor() {console.log("Creating a new car");}
}class Porsche extends Car {constructor() {super();console.log("Creating Porsche");}
}let c = new Porsche();
// Creating a new car
// Creating Porsche

extends 允许一个子类继承父类,需要注意的是,子类的constructor 函数中需要执行 super() 函数。

当然,你也可以在子类方法中调用父类的方法,如super.parentMethodName()

有几点值得注意的是:

  • 类的声明不会提升(hoisting),如果你要使用某个 Class,那你必须在使用之前定义它,否则会抛出一个 ReferenceError 的错误
  • 在类中定义函数不需要使用 function 关键词

14. Symbol

Symbol 是一种新的数据类型,它的值是唯一的,不可变的。ES6 中提出 symbol 的目的是为了生成一个唯一的标识符,不过你访问不到这个标识符:

var sym = Symbol( "some optional description" );
console.log(typeof sym); // symbol

注意,这里 Symbol 前面不能使用 new操作符。

  • 如果它被用作一个对象的属性,那么这个属性会是不可枚举的:
var o = {val: 10,[ Symbol("random") ]: "I'm a symbol",
};console.log(Object.getOwnPropertyNames(o)); // val
  • 如果要获取对象 symbol 属性,需要使用Object.getOwnPropertySymbols(o)

15. 迭代器(Iterators)

迭代器允许每次访问数据集合的一个元素,当指针指向数据集合最后一个元素时,迭代器便会退出。它提供了 next() 函数来遍历一个序列,这个方法返回一个包含 donevalue 属性的对象。

ES6 中可以通过 Symbol.iterator 给对象设置默认的遍历器,无论什么时候对象需要被遍历,执行它的 @@iterator 方法便可以返回一个用于获取值的迭代器。

数组默认就是一个迭代器:

var arr = [11,12,13];
var itr = arr[Symbol.iterator]();itr.next(); // { value: 11, done: false }
itr.next(); // { value: 12, done: false }
itr.next(); // { value: 13, done: false }itr.next(); // { value: undefined, done: true }

你可以通过[Symbol.iterator]()自定义一个对象的迭代器。

16. Generators

Generator 函数是 ES6 的新特性,它允许一个函数返回的可遍历对象生成多个值。

在使用中你会看到 * 语法和一个新的关键词 yield:

function *infiniteNumbers() {var n = 1;while (true){yield n++;}
}var numbers = infiniteNumbers(); // returns an iterable objectnumbers.next(); // { value: 1, done: false }
numbers.next(); // { value: 2, done: false }
numbers.next(); // { value: 3, done: false }

每次执行 yield 时,返回的值变为迭代器的下一个值。

17. Promises

ES6 对 Promise 有了原生的支持,一个 Promise 是一个等待被异步执行的对象,当它执行完成后,其状态会变成 resolved 或者rejected

var p = new Promise(function(resolve, reject) {  if (/* condition */) {// fulfilled successfullyresolve(/* value */);  } else {// error, rejectedreject(/* reason */);  }
});

每一个 Promise 都有一个 .then 方法,这个方法接受两个参数,第一个是处理 resolved 状态的回调,一个是处理 rejected 状态的回调:

p.then((val) => console.log("Promise Resolved", val),(err) => console.log("Promise Rejected", err));

16. 数组

16.1 使用扩展运算符(…)拷贝数组。

// bad
const len = items.length;
const itemsCopy = [];
let i;
for (i = 0; i < len; i++) {itemsCopy[i] = items[i];
}
// good
const itemsCopy = [...items];

16.2 Array.from

使用 Array.from 方法,将类似数组的对象转为数组

const foo = document.querySelectorAll('.foo');
const nodes = Array.from(foo);

17. 函数

17.1 IIFE箭头函数形式

立即执行函数可以写成箭头函数的形式。

(() => {console.log('Welcome to the Internet.');
})();

17.2 匿名函数用箭头函数替代

那些使用匿名函数当作参数的场合,尽量用箭头函数代替。因为这样更简洁,而且绑定了 this

// bad
[1, 2, 3].map(function (x) {return x * x;
});
// good
[1, 2, 3].map((x) => {return x * x;
});
// best
[1, 2, 3].map(x => x * x);

箭头函数取代Function.prototype.bind,不应再用 self/_this/that 绑定 this

// bad
const self = this;
const boundMethod = function(...params) {return method.apply(self, params);
}
// acceptable
const boundMethod = method.bind(this);
// best
const boundMethod = (...params) => method.apply(this, params);

简单的、单行的、不会复用的函数,建议采用箭头函数。如果函数体较为复杂,行数较多,还是应该采用传统的函数写法。

17.3 参数方式

所有配置项都应该集中在一个对象,放在最后一个参数,布尔值不可以直接作为参数。

// bad
function divide(a, b, option = false ) {
}
// good
function divide(a, b, { option = false } = {}) {
}

17.4 使用rest运算符

不要在函数体内使用 arguments 变量,使用 rest 运算符(...)代替。因为 rest 运算符显式表明你想要获取参数,而且 arguments 是一个类似数组的对象,而 rest 运算符可以提供一个真正的数组。

// bad
function concatenateAll() {const args = Array.prototype.slice.call(arguments);return args.join('');
}
// good
function concatenateAll(...args) {return args.join('');
}

使用默认值语法设置函数参数的默认值。

// bad
function handleThings(opts) {opts = opts || {};
}
// good
function handleThings(opts = {}) {// ...
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_331159.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

StringBuffer类 StringBuilder 类

StringBuffer类 介绍 StringBuffer是一个容器&#xff0c;代表可变的字符序列&#xff0c;可以对字符串内容进行增删。 StringBuffer是可变长度的。 实现了序列化接口&#xff0c;可实现串行化&#xff08;可以将内容保存至文件或者网络传输&#xff09;&#xff1a; Serial…

关于Java的网络编程

网络的一些了解 网络通信协议 链路层&#xff1a;链路层是用于定义物理传输通道&#xff0c;通常是对某些网络连接设备的驱动协议&#xff0c;例如针对光纤、网线提供的驱动。网络层&#xff1a;网络层是整个TCP/IP协议的核心&#xff0c;它主要用于将传输的数据进行分组&…

华为申请注册盘古大模型商标;京东推出言犀大模型,率先布局产业应用

7月14日科技新闻早知道&#xff0c;一分钟速览。 1.华为申请注册盘古大模型商标&#xff1a; 据天眼查 App 显示&#xff0c;7 月 7 日&#xff0c;华为技术有限公司申请注册“华为云盘古”、“Huawei Cloud Pangu Models”文字及图形商标&#xff0c;国际分类为网站服务、社…

文心一言 VS 讯飞星火 VS chatgpt (59)-- 算法导论6.4 3题

文心一言 VS 讯飞星火 VS chatgpt &#xff08;59&#xff09;-- 算法导论6.4 3题 三、对于一个按升序排列的包含 n 个元素的有序数组 A 来说&#xff0c;HEAPSORT 的时间复杂度是多少?如果 A 是降序呢? 文心一言&#xff1a; 对于一个按升序排列的包含 n 个元素的有序数组…

C++图形开发(14):游戏完善——无限空中起跳解决

文章目录 1.问题描述2.如何解决&#xff1f;3.整段代码 1.问题描述 在游玩过程中&#xff0c;我们肯定发现了之前所给出的游戏源码中的一个小bug&#xff1a; 小球可以空中无限起跳&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&…

【Linux操作系统】多线程抢票逻辑——学习互斥量(锁)函数接口

文章目录 1.进程线程间的互斥相关背景概念2.联系代码学习同步互斥问题3.互斥量&#xff08;锁&#xff09;的函数接口3.1初始化互斥量3.2销毁互斥量3.3互斥量加锁和解锁3.4改进多线程抢票代码 1.进程线程间的互斥相关背景概念 临界资源&#xff1a;多线程执行流共享的资源就叫…

在UI中使用SpriteMask裁减任意shader的粒子效果

前言 由于我们需要在Mask中对粒子效果进行裁减。但是我们的的特效同事不愿意每个shader都去添加Stencil。所以使用SpriteMask方式进行裁减。 使用步骤 1. 添加SpriteMask Component 更具你需要的Mask形状设置精灵图片。又因为实际是精灵&#xff0c;并不属于UI系统&#xff…

静态库 的制作与使用

文章目录 重要命令程序组成静态库制作流程静态库的使用 gcc main.c -o APP -I(i) 头文件路径 -l(L) 库名字&#xff08;xxx) -L 库路径 重要命令 gcc -o -I(大写i) -l(小写L) -L cp&#xff1a;复制文件 -r 递归&#xff0c;用于复制文件夹所有文件 mv&#xff1a;移动文件 程…

【ArcGIS Pro二次开发】(46):要素类从上到下、从左到右排序

要素类经过编辑之后&#xff0c;【OBJECTID】字段会变得不规律。应部分网友要求&#xff0c;做了这个从上到下、从左到右排序的工具。 不过后来在ArcGIS Pro中发现了一个【排序】工具&#xff0c;已经可以完美实现这个功能需求&#xff0c;发现自己做了个白工。 不过做了不能白…

【统计函数3】——excel常见函数

相关数据资料来源于网易 函数一览&#xff1a; rank、rand、randbetween、floor、int rank函数: 求某单元格在某区域内的排名 RANK(数值,引用区域,降序0/升序1)范围多指定&#xff1a; 分开的范围之间可用逗号隔开&#xff0c;最后再用一个小括号括起来。F4可以快速锁定行和列。…

20230714----重返学习-DOM-diff算法-构建工具-包管理工具-Vite基本使用-Vue3新特性

day-112-one-hundred-and-twelve-20230714-DOM-diff算法-构建工具-包管理工具-Vite基本使用-Vue3新特性 DOM-diff算法 vue2中diff算法 同级比对&#xff0c;跨级比对性能差。而且采用的方式是递归比对&#xff0c;更差一点。根节点只能有一个&#xff0c;比对的时候会从根节…

MySql5.6版本开启慢SQL功能

文章目录 1引言1.1目的1.2注意点说明1.3 操作步骤1.3.1 临时生效操作步骤1.3.2 永久生效操作步骤1.3.3 按日期生成日志文件1.3.4 执行成功后验证功能是否开启 1.4 慢SQL日志记录内容介绍1.5 Shell脚本 1引言 1.1目的 开启 MySQL 的慢查询日志&#xff08;Slow Query Log&…

【Distributed】分布式Ceph存储系统

文章目录 一、存储基础1. 单机存储设备1.1 DAS1.2 NAS1.3 SAN1.4 单机存储的问题1.5 商业存储解决方案 2. 分布式存储&#xff08;软件定义的存储 SDS &#xff09;分布式存储的类型 3. Ceph 简介4. Ceph 优势5. Ceph 架构6. Ceph 核心组件7. OSD 存储后端7.1 Filestore7.2 Blu…

微服务架构+创建微服务工程(商品/订单)

目录 1.微服务架构 1.1.单体应用架构 1.2.微服务应用 1.3 微服务架构的优势 1.4.微服务架构的缺点(挑战) 1.5. SpringCloud与微服务关系 1.6.SpringBoot和SpringCloud关系 2. 创建微服务工程 2.1.数据库 2.2.搭建父工程 2.2 创建公共模块 2.3.商品系统 2.4.订单微…

在Linux中使用curl访问EasyCVR页面,返回报错Unauthorized是什么原因?

EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安防视频监控的能力&#xff0c;比如&#xff1a;视…

检测到错误页面web应用服务器版本信息泄露

详细描述 Web服务器未能正确处理异常请求导致Web服务器版本信息泄露&#xff0c;攻击者收集到服务器信息后可进行进一步针对性攻击。 解决办法 临时修复建议如下&#xff1a; 1、关闭web服务器错误提示。 2、关闭运行平台的错误提示。 3、建立错误机制&#xff0c;不要把真实…

力扣 198.打家劫舍【中等】

198.打家劫舍 1 题目2 思路3 代码4 结果 1 题目 题目来源&#xff1a;力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/house-robber 题目&#xff1a;你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃…

优化产品知识库的 SEO 技巧

在当今数字化的商业环境中&#xff0c;为产品知识库进行搜索引擎优化&#xff08;SEO&#xff09;是至关重要的。随着用户越来越倾向于通过搜索引擎获取信息&#xff0c;优化产品知识库可以帮助你的企业在竞争激烈的市场中脱颖而出。 通过改进SEO&#xff0c;你可以帮助用户找到…

K8S V1.25.11安装以及踩坑之旅

说明&#xff1a; 系统&#xff1a;centos9 stream K8S版本&#xff1a;V1.25.11&#xff08;不要装最新的&#xff0c;后续的可视化界面安装的是个坑&#xff0c;基本上不支持&#xff0c;时间&#xff1a;2023年7月14日&#xff09; Docker版本&#xff1a;20.10.16 (不要…

4.postgresql--rollup,grouping sets,cube

PostgreSQL ROLLUP 是group by 的子句&#xff0c;是生成多个分组集合的快捷功能。与Cube子句的差异是&#xff0c;rollup 不生成基于特定列所有可能的分组集合&#xff0c;生成分组集合为其子集。 ROLLUP假设输入列之间存在层次结构&#xff0c;从而生成有意义的所有分组集合…