块做用域javascript
ES6引入块做用域java
考虑到环境致使的行为差别太大,应该避免在块级做用域内声明函数。若是确实须要,也应该写成函数表达式,而不是函数声明语句。node
letjquery
constes6
const
实际上保证的,并非变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动,所以值类型变量不可变,而引用类型变量能够更改实体数据内容。顶层对象编程
let、const、class命令声明的全局变量,不属于顶层对象的属性数组
ES6 容许按照必定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构实质是模式匹配promise
等号右边的对象须要具有 Iterator 接口浏览器
// 基本模式匹配 let [a, b, , c] = [1, 2, 3]; // a = 1; b = 2; c = undefined; // 复杂模式匹配 [a, b, c] = [1, [2], 3]; // a = 1; b = [2]; c = 3; [a, [b], c] = [1, [2], 3]; // a = 1; b = 2; c = 3; [a, [b], c] = [1, [2, 3], 4]; // a = 1; b = 2; c = 4; [a, [b], c] = [1, 2, 3]; // Uncaught TypeError: undefined is not a function // 与rest参数结合使用 let [head, ...tail] = [1, 2, 3, 4]; // head = 1; tail = [2, 3, 4] // 设置默认值,ES6 内部使用严格相等运算符(===),判断一个位置是否有值。因此,只有当一个数组成员严格等于undefined,默认值才会生效。 let [foo = 1] = []; // foo = 1; let [foo = 1] = [null]; // foo = null; // 默认值是表达式的状况,表达式惰性求值 function f() { console.log('aaa'); } let [x = f()] = [1]; // 默认值为变量的状况,变量必须已声明 let [x = 1, y = x] = []; let [x = y, y = 1] = []; // ReferenceError: y is not defined // 解构字符串 const [a, b, c, d, e] = 'hello'; // a = 'h'; b = 'e'; c = 'l'; d = 'l'; e = 'o'
let { foo, bar } = { foo: "aaa", bar: "bbb" }; // foo = 'aaa'; bar = 'bbb'; // 匹配的模式: 变量 let { foo: f, bar: b } = { foo: "aaa", bar: "bbb" }; // foo = undefined; f = "aaa"; b = "bbb" // 默认值 let { baz = {} } = { foo: "aaa", bar: "bbb" }; // baz = {}; let {x: y = 3} = {x: 5}; // y = 5; // 混合解构 let { items: [{name}] } = { id: '1', items: [{name: 'joke'}, {name: 'tony'}] }; // name = 'joke' let [{name, habbits: [habbit]}] = [{id: '1', name: 'kelly', habbits: ['piano']}, {id: '2', name: 'tony'}]; // name = 'kelly'; habbit = 'piano' // 嵌套赋值 let obj = {}; let arr = []; ({ foo: obj.prop, bar: arr[0] } = { foo: 123, bar: true }); obj // {prop:123} arr // [true] // 在直接使用赋值表达式时,须要使用圆括号包住赋值表达式,大括号写在行首会被解释为代码块 let x; {x} = {x: 1}; // SyntaxError: syntax error ({x} = {x: 1}); // x = 1; const arr = [1,2,3,4] let {0 : first, [arr.length - 1] : last, length} = arr; // first = 1; last = 4; length = 4
// 函数参数的解构赋值 function move({x, y} = { x: 0, y: 0 }) { return [x, y]; } move({x: 3, y: 8}); // [3, 8] move({x: 3}); // [3, undefined] move({}); // [undefined, undefined] move(); // [0, 0]
${name}
缓存
扩展方法
startsWith
let s = 'Hello world!'; s.startsWith('world') // true s.startsWith('world', 6) // true, 第二个参数针对第n个位置直到结束
endsWith
let s = 'Hello world!'; s.endsWith('world') // true s.endsWith('Hello', 5) // true, 第二参数针对前n个字符
includes
let s = 'Hello world!'; s.includes('world') // true s.includes('Hello', 6) // false, 第二参数针对第n个位置直到结束
repeat
'hello'.repeat(2) // hellohello,repeat重复字符串操做
padStart, padEnd
// 补全字符串方法,接受两个参数,第一个参数指定字符串长度,第二个参数指定补全内容 'x'.padStart(5, 'ab') // ababx 'x'.padEnd(3, 'a') // xaa
扩展方法
默认值
箭头函数
扩展方法
flat
[1, 2, [3, [4, 5]]].flat() // [1, 2, 3, [4, 5]],单层拉平 [1, 2, [3, [4, 5]]].flat(2) // [1, 2, 3, 4, 5],两层拉平 [1, [2, [3]]].flat(Infinity) // [1, 2, 3]
属性名表达式
let userName = 'joe'; let firstName = 'curly'; let lastName = 'water';
[userName]: 23, [firstName + lastName]: 25
};
- 方法的name属性 - 属性遍历 - for...in - 遍历原型链,遍历自身和继承的可枚举属性(不含Symbol属性) - Object.keys - 返回一个数组,包括自身的全部可枚举属性(不含 Symbol 属性)的键名 - Object.getOwnPropertyNames - 返回一个数组,包含对象自身的全部属性(不含 Symbol 属性)的键名 - Object.getOwnPropertySymbols - 返回一个数组,包含对象自身的全部Symbol属性的键名 - Reflect.ownKeys - 返回一个数组,包含对象自身的全部键名 - super关键字 - 指向当前对象的原型对象,只能用在简写的对象方法中 - 解构 - 扩展运算符 - 扩展方法 - Object.is() - 严格相等,解决全等运算符`NaN`不等于自身,以及`+0`等于`-0`等问题 - Object.assign() - 用于对象的合并,将源对象(source)的全部可枚举属性,复制到目标对象,浅拷贝 - Object.getOwnPropertyDescriptors() - 返回对象属性的描述对象 - Object.setPrototypeOf() - 设置原型对象 - Object.getPrototypeOf() - 读取原型对象 - Object.keys() - 返回一个数组,包括自身的全部可枚举属性(不含 Symbol 属性)的键名 - Object.values() - 返回一个数组,包括自身的全部可枚举属性(不含 Symbol 属性)的键值 - Object.entries() - 方法返回一个数组,成员是参数对象自身的(不含继承的)全部可遍历(enumerable)属性的键值对数组 - Object.fromEntries() - Object.entries的逆操做 # Symbol ES6新加入的类型值,表示独一无二的值
let s = Symbol();
- `Symbol`函数的参数只是表示对当前 Symbol 值的描述 - 不能与其余类型的值进行运算 - 做为属性名,不能使用点运算符
let mySymbol = Symbol();
// 第一种写法
let a = {};
a[mySymbol] = 'Hello!';
// 第二种写法
let a = {
[mySymbol]: 'Hello!'
};
// 第三种写法
let a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });
// 以上写法都获得一样结果
a[mySymbol] // "Hello!"
- 能够用于定义一组常量,保证这组常量的值都是不相等的
const log = {};
log.levels = {
DEBUG: Symbol('debug'), INFO: Symbol('info'), WARN: Symbol('warn')
};
console.log(log.levels.DEBUG, 'debug message');
console.log(log.levels.INFO, 'info message');
# Set 和 Map **Set** ES6 提供了新的数据结构 Set。它相似于数组,可是成员的值都是惟一的,没有重复的值。 `Set`自己是一个构造函数,用来生成 Set 数据结构。
const s = new Set([1, 2, 3]);
s.add(4);
s.delete(4);
s.has(4);
s.clear();
s.size;
s.keys();
s.values();
s.entries();
s.forEach((value, key) => console.log(key + ":" + value));
**WeakSet** WeakSet 结构与 Set 相似,也是不重复的值的集合。 WeakSet 的成员只能是对象。 WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,若是其余对象都再也不引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。 **Map** ES6 提供了 Map 数据结构。它相似于对象,也是键值对的集合,可是“键”的范围不限于字符串,各类类型的值(包括对象)均可以看成键。
const map = new Map();
map.set('foo', true);
map.set('bar', false);
map.set([1], false);
map.size // 2
map.get('foo');
map.has('foo');
map.delete('foo');
map.clear();
for (let key of map.keys()) {
console.log(key);
}
for (let value of map.values()) {
console.log(value);
}
for (let item of map.entries()) {
console.log(item[0], item[1]);
}
// 或者
for (let [key, value] of map.entries()) {
console.log(key, value);
}
// 等同于使用map.entries()
for (let [key, value] of map) {
console.log(key, value);
}
**WeakMap** `WeakMap`结构与`Map`结构相似,也是用于生成键值对的集合。 `WeakMap`只接受对象做为键名(`null`除外) `WeakMap`的键名所指向的对象,不计入垃圾回收机制。 # Proxy Proxy 是一个构造函数,能够理解成,在目标对象以前架设一层“拦截”,外界对该对象的访问,都必须先经过这层拦截,所以提供了一种机制,能够对外界的访问进行过滤和改写。 # Reflect 保存Object对象的一些属于语言内部的方法,好比说`defineProperty`/`get`/`apply` 好处在于:让`Object`操做都变成函数行为,在Proxy中能够获取对象的默认行为 > - Reflect.apply(target, thisArg, args) > - Reflect.construct(target, args) > - Reflect.get(target, name, receiver) > - Reflect.set(target, name, value, receiver) > - Reflect.defineProperty(target, name, desc) > - Reflect.deleteProperty(target, name) > - Reflect.has(target, name) > - Reflect.ownKeys(target) > - Reflect.isExtensible(target) > - Reflect.preventExtensions(target) > - Reflect.getOwnPropertyDescriptor(target, name) > - Reflect.getPrototypeOf(target) > - Reflect.setPrototypeOf(target, prototype) # Promise 所谓`Promise`,简单说就是一个容器,里面保存着某个将来才会结束的事件(一般是一个异步操做)的结果。 `Promise`对象是一个构造函数,用来生成`Promise`实例。 状态:`pending`(进行中)、`fulfilled`(已成功)和`rejected`(已失败) - `Promise` 新建后就会当即执行。 - 调用`resolve`或`reject`并不会终结 `Promise` 的参数函数的执行。 - `then`方法返回的是一个新的`Promise`实例 - `catch`方法会捕获状态肯定前的全部错误,包括在then回调函数中的错误 - `Promise` 会吃掉错误,不会对后续代码执行产生影响 - `finally`方法,无论 `Promise` 对象最后状态如何,都会执行的操做 - `Promise.all`方法用于将多个 `Promise` 实例,包装成一个新的 `Promise` 实例。多个`Promise`实例都改变状态,才会调用新`Promise`实例的回调 - 若是做为参数的 `Promise` 实例,本身定义了`catch`方法,那么它一旦被`rejected`,并不会触发`Promise.all()`的`catch`方法。 - `Promise.race`方法用于将多个 `Promise` 实例,包装成一个新的 `Promise` 实例。第一个`Promise`实例都改变状态,进入新`Promise`实例的回调 - `Promise.resolve(reason)`方法也会返回一个新的 `Promise` 实例,该实例的状态为`fulfilled`。 - `Promise.reject(reason)`方法也会返回一个新的 `Promise` 实例,该实例的状态为`rejected`。
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/ 异步操做成功 /){
resolve(value);
} else {
reject(error);
}
});
promise
.then(function(value) { console.log(value) }, function (err) {console.log(err);})
.catch(function(error) { console.log(error) });
# Iterator Iterator(遍历器)是一种接口,为各类不一样的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就能够完成遍历操做。
var it = makeIterator(['a', 'b']);
it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: undefined, done: true }
function makeIterator(array) {
var nextIndex = 0;
return {
next: function() { return nextIndex < array.length ? {value: array[nextIndex++], done: false} : {value: undefined, done: true}; }
};
}
ES6 规定,默认的 Iterator 接口部署在数据结构的`Symbol.iterator`属性,或者说,一个数据结构只要具备`Symbol.iterator`属性,就能够认为是“可遍历的”(iterable) 原生具有Iterator接口的数据结构 - Array - Map - Set - String - TypedArray - 函数的 arguments 对象 - NodeList 对象 `for...of`循环调用遍历器接口,做为遍历全部数据结构的统一的方法。 `for...in`循环主要是为遍历对象而设计的。 `forEach`没法跳出循环 `for...of`可跳出循环,严格按照顺序遍历 # Generator Generator 函数是一个状态机,封装了多个内部状态。 执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,仍是一个遍历器对象生成函数。返回的遍历器对象,能够依次遍历 Generator 函数内部的每个状态。
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
hw.next()
// { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }
- `yield`表达式后面的表达式,只有当调用`next`方法、内部指针指向该语句时才会执行 - 只有调用`next`方法时,Genarator函数才会执行 - `yield`只能被Genarator函数包裹,普通函数不行 - `yield*`表达式,用来在一个 Generator 函数里面执行另外一个 Generator 函数 # async Generator 函数的语法糖 `async`表示函数里有异步操做,`await`表示紧跟在后面的表达式须要等待结果。 `await`命令后面能够是`Promise`对象 `async`函数的返回值是 Promise 对象 # Class
class MyClass {
constructor() {
this.name = name;
}
get prop() {
return 'getter';
}
set prop(value) {
console.log('setter: '+value);
}
}
- 类和模块的内部,默认就是严格模式,因此不须要使用`use strict`指定运行模式 - 类不存在变量提高
new Foo(); // Error
class Foo {
print () { console.log('Hello'); }
}
- name 属性 - 可使用Generator实现Symbol.iterator遍历器 - 类的方法内部若是含有`this`,它默认指向类的实例。可是若是单独提取方法出来用,容易报错
class Logger {
printName(name = 'there') { this.print(`Hello ${name}`); } print(text) { console.log(text); }
}
const logger = new Logger();
const { printName } = logger;
printName(); // TypeError: Cannot read property 'print' of undefined
解决方法
class Logger {
constructor() { this.printName = this.printName.bind(this); } // ...
}
class Logger {
prinitName () { this.print(`Hello ${name}`) } // ...
}
- 若是在一个方法前,加上`static`关键字,就表示该方法不会被实例继承,而是直接经过类来调用,这就称为“静态方法”。
class Foo {
static classMethod() { return 'hello'; }
}
Foo.classMethod() // 'hello'
var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function
若是静态方法包含`this`关键字,这个`this`指的是类,而不是实例。 父类的静态方法,能够被子类继承。 - 实例属性除了在`constructor()`方法里面定义,也能够直接写在类的最顶层。
class IncreasingCounter {
_count = 0; get value() { console.log('Getting the current value!'); return this._count; } increment() { this._count++; }
}
- 静态属性
class Foo {
static prop = 1;
}
- 私有属性 - 使用_约定 - 结合Symbol使用避免被覆盖 - 使用#表明私有属性
class IncreasingCounter { #count = 0; get value() { console.log('Getting the current value!'); return this.#count; } increment() { this.#count++; } } ```
new
命令做用于的那个构造函数。this
对象,必须先经过父类的构造函数完成塑造,获得与父类一样的实例属性和方法,而后再对其进行加工,加上子类本身的实例属性和方法。若是不调用super
方法,子类就得不到this
对象。修饰器是一个对类进行处理的函数
@testable class MyTestableClass { // ... } function testable(target) { target.isTestable = true; } MyTestableClass.isTestable // true
function testable(isTestable) { return function(target) { target.isTestable = isTestable; } } @testable(true) class MyTestableClass {} MyTestableClass.isTestable // true @testable(false) class MyClass {} MyClass.isTestable // false
修饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。这意味着,修饰器能在编译阶段运行代码。也就是说,修饰器本质就是编译时执行的函数。
@connect(mapStateToProps, mapDispatchToProps) export default class MyReactComponent extends React.Component {}
修饰对象属性
class Person { @readonly name() { return `${this.first} ${this.last}` } } function readonly(target, name, descriptor){ // target要修改的对象 // name要修饰的属性名 // descriptor要修饰的属性的描述对象 descriptor.writable = false; return descriptor; }
修饰器只能用于类和类的方法,不能用于函数,由于存在函数提高。
CommonJS规范
接口:
// moduleA.js module.exports = function( value ){ return value * 2; } // moduleB.js var multiplyBy2 = require('./moduleA'); var result = multiplyBy2(4);
require
命令第一次加载该脚本,就会执行整个脚本,而后在内存生成一个对象缓存。AMD规范
接口:
define('myModule', ['jquery'], function($) { // $ 是 jquery 模块的输出 $('body').text('hello world'); }); require(['myModule'], function(myModule) {}); // 未使用模块名,类CommonJS使用 define(function(require, exports, module) {})
ES6 模块
export
命令显式指定输出的代码(并不是输出对象),再经过import
动态引用。接口:
export
命令用于规定模块的对外接口import
命令用于输入接口export default
规定模块默认接口,本质是输出一个叫default的变量,因此在模块中只能惟一存在,而且不可更改,不能跟声明语句export
其余模块,export和import的复合写法,实际上并无导入当前模块,只是转发import
命令会被 JavaScript 引擎静态分析,先于模块内的其余语句执行,import
和export
命令只能在模块的顶层,不能在代码块之中import()
函数完成动态加载,返回一个Promise对象// Module1 var m = 1; export { m n as N }; // 输出一个default变量,将变量m的值赋给变量default export default m; // Module2 import {m, N} from "Module1"; // 导入m和N接口 import { m as M } from "Module1"; // 导入m接口,重命名为M import module1 from "Module1"; // 导入默认接口 import * as module1 from "Module1"; // 导入全部接口 export * from "Module1"; // 再输出,export *命令会忽略Module1模块的default方法。
加载规则
浏览器对于带有type="module"
的<script>
,都是异步加载,不会形成堵塞浏览器,即等到整个页面渲染完,再执行模块脚本,等同于打开了<script>
标签的defer
属性。
外部ES6模块脚本特性:
use strict
。import
命令加载其余模块(.js
后缀不可省略,须要提供绝对 URL 或相对 URL),也可使用export
命令输出对外接口。this
关键字返回undefined
,而不是指向window
。也就是说,在模块顶层使用this
关键字,是无心义的。在let
和const
之间,建议优先使用const
const
进行优化,因此多使用const
,有利于提升程序的运行效率静态字符串一概使用单引号或反引号,不使用双引号。动态字符串使用反引号。
const a = 'apple'; let b = "banana"; b = "batman";
优先使用解构赋值
若是函数返回多个值,优先使用对象的解构赋值,而不是数组的解构赋值。这样便于之后添加返回值,以及更改返回值的顺序。
对象
Object.assign
方法。数组
使用扩展运算符拷贝数组
使用Array.from将类数组对象转为数组
函数
全部配置项都应该集中在一个对象,放在最后一个参数
rest运算符代替arguments变量
使用默认值语法设置函数参数的默认值。
Map
注意区分 Object 和 Map,若是只是须要key: value
的数据结构,使用 Map 结构。由于 Map 有内建的遍历机制。
模块
若是模块只有一个输出值,就使用export default
,若是模块有多个输出值,就不使用export default
,export default
与普通的export
不要同时使用。
若是模块默认输出一个函数,函数名的首字母应该小写。
function makeStyleGuide() { } export default makeStyleGuide;
若是模块默认输出一个对象,对象名的首字母应该大写。
const StyleGuide = { es6: { } }; export default StyleGuide;