ES6基础笔记-阮一峰

12 篇文章 0 订阅
9 篇文章 0 订阅

// 数组的扩展

let arr=[3,5,7];

let arr2=Array.from(arr,(x)=>x*5);//新数组

console.log(arr2);

for (const item of arr) {//for of 遍历数组元素

console.log(item);

}

for (const elem of arr2.values()) {

console.log(elem);//键值

}

//Array.prototype.flat()拉平数组

// 对象的扩展 for in 遍历对象

var myObject={

  a:1,

  b:2,

  c:3

}

console.log(Object.keys(myObject));

for (var key in myObject) {

console.log(key);//a b c

}

const proto = {

foo: 'hello'

};

const proto2 = {

foo: 'hello'

};

const obj = {// super指向当前对象的原形对象

foo: 'world',

find() {

return super.foo;

}

};

Object.setPrototypeOf(obj, proto);//设置对象obj的原型对象

console.log(obj.find()) // "hello"

// 对象扩展运算符

let aobj={a:3,b:4};

let n={...aobj,c:8,d:'n有值'};

console.log(n);

//es2020链判断运算符 n?.d n == null ? undefined : n.d

//es2020运算符左侧的值为null或undefined时,才会返回右侧的值。?? 双引号

// 对象新增方法

console.log("两个对象是否严格相等:"+Object.is(proto,obj),Object.is(1,2),Object.is(NaN,NaN),Object.is(proto,proto2));//false,true,true,false 是否完全相等

console.log(Object.assign(proto,myObject));//对象合并 浅拷贝 遇到同名属性是“替换”不是“添加”,如果处理的是数组会被当作为对象

// symbol原始数据,表示独一无二的值 第七种数据类型,undefined、null、Boolean、String、Number、Object

// symbol值作为对象属性名时,不能用点运算符 a[Symbol()] = 'Hello!';

//消除魔术字符串 魔术字符串指的是,在代码之中多次出现、与代码形成强耦合的某一个具体的字符串或者数值

// set map数据结构 set方法:add delete has clear

const set =new Set([1,2,3,8,8]);//set成员的值都是唯一

arr.forEach(item=>set.add(item));

console.log([...set]);//数组去重 1 2 3 8 5 7 去重方法二:Array.from(set);

let aba=[...new Set('ababbc')].join('');//字符串去重

// set遍历方法:keys() values() entries() forEach() 由于Set结构没有键名,所以keys和values值一样

// for of,forEach直接遍历

let objAba=arr.map(x=>x*10);

console.log(objAba)

console.log(set)

// map 键值对”的数据结构,Map 比 Object 更合适

// map.set map.get

const map0 = new Map()

.set(1, 'a')

.set(2, 'b')

.set(3, 'c');

const map2 = new Map(

[...map0].map(([k, v]) => [k * 2, '_' + v])

);

console.log(map2);//{2 => "_a", 4 => "_b", 6 => "_c"}

// prox拦截

var handler = {

get: function(target, name) {//拦截对象属性的读取 可接受三个参数:目标对象、属性名、proxy实例

if (name === 'prototype') {

return Object.prototype;

}

return 'Hello, ' + name;

},

apply: function(target, thisBinding, args) {//拦截 Proxy 实例作为函数调用的操作

return args[0];

},

construct: function(target, args) {//拦截 Proxy 实例作为构造函数调用的操作

return {value: args[1]};

}

};

var fproxy = new Proxy(function(x, y) {

return x + y;

}, handler);

console.log(fproxy(1, 2),new fproxy(1, 2),fproxy.prototype);

// 在 Proxy 代理的情况下,目标对象内部的this关键字会指向 Proxy 代理

// promise异步编程的一种解决方案 容器保存着未来才会结束的事件的结果

// then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行

// 如果异步操作抛出错误,状态就会变为rejected,就会调用catch方法指定的回调函数,处理这个错误。另外,then方法指定的回调函数,如果运行中抛出错误,也会被catch方法捕获。

// 一般来说,不要在then方法里面定义 Reject 状态的回调函数(即then的第二个参数),总是使用catch方法。建议总是使用catch方法,而不使用then方法的第二个参数。

// Promise 内部的错误不会影响到 Promise 外部的代码

// 让同步函数同步执行,异步函数异步执行,并且让它们具有统一的 API 呢?回答是可以的,并且还有两种写法。

// 第一种写法是用async函数来写。

/* const f = () => console.log('now');

(async () => f())().then().catch();

console.log('next');

// 第二种写法是使用new Promise()。*/

const ff = () => console.log('now');

(() => new Promise(

resolve => resolve(ff())

) )();

console.log('next');

// 鉴于这是一个很常见的需求,所以现在有一个提案,提供Promise.try方法替代上面的写法。

// const f = () => console.log('now');

// Promise.try(f);

// console.log('next');

//事实上,Promise.try就是模拟try代码块,就像promise.catch模拟的是catch代码块。

// Promise.try(() => database.users.get({id: userId})).then().catch();

// Iterator遍历器

// 对于类似数组的对象(存在数值键名和length属性),部署 Iterator 接口,有一个简便方法,就是Symbol.iterator方法直接引用数组的 Iterator 接口。

// NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];或者NodeList.prototype[Symbol.iterator] = [][Symbol.iterator];

// 调用 Iterator 接口的场合:解构赋值;扩展运算符;yield*;for...of,Array.from(),Map(), Set(), WeakMap(), WeakSet(),Promise.all(),Promise.race(),字符串的 Iterator 接口,字符串是一个类似数组的对象

// for in 读取键名, for of读取键值。

// 数组内置的forEach循环,无法中途跳出循环;for in为遍历对象而设计,不适用于遍历数组。for of可以与break、continue和return配合使用

// 当用for of遍历对象时需要将对象的简明生成一个数组,然后遍历这个数组:

for (var key of Object.keys(obj));

// 或者使用Generator函数将对象重新包装一下

function* entries(obj) { for (let key of Object.keys(obj)) { yield [key, obj[key]];} }

for (let [key, value] of entries(obj)) {console.log(key, '->', value);}

// Generator异步编程解决方案

// Generator 函数是一个状态机,封装了多个内部状态。function关键字与函数名之间有一个星号;函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。

// yield表达式如果用在另一个表达式之中,必须放在圆括号里面:console.log('Hello' + (yield 123));

function* foo(x) {

var y = 2 * (yield (x + 1));

var z = yield (y / 3);

return (x + y + z);

}

var a = foo(5);

a.next() ;//x:5 返回x+1是6 由于next方法的参数表示上一个yield表达式的返回值,所以在第一次使用next方法时,传递参数是无效的。

a.next(12); //上一表达式是12,y=2*12=24,返回y/3即24/3=8;

a.next(13) ;//上一表达式是13,z=13,return返回5+24+13=42

// for...of 循环 一旦next方法的返回对象的done属性为true,for...of循环就会中止,且不包含该返回对象

function* objectEntries(obj) { //加上遍历器接口

let propKeys = Reflect.ownKeys(obj);

for (let propKey of propKeys) {

yield [propKey, obj[propKey]];

}

}

let jane = { first: 'Jane', last: 'Doe' };

for (let [key, value] of objectEntries(jane)) {

console.log(`${key}: ${value}`);

}

// 另一种写法是,将 Generator 函数加到对象的Symbol.iterator属性上面

function* objectEntries2() {

let propKeys = Object.keys(this);

for (let propKey of propKeys) {

yield [propKey, this[propKey]];

}

}

let jane2 = { first: 'Jane', last: 'Doe' };

jane2[Symbol.iterator] = objectEntries2;

for (let [key, value] of jane2) {

console.log(`${key}: ${value}`);

}

// throw方法被捕获以后,会附带执行下一条yield表达式。也就是说,会附带执行一次next方法。

// Generator 执行过程中抛出错误,且没有被内部捕获,就不会再执行下去了。如果此后还调用next方法,将返回一个value属性等于undefined、done属性等于true的对象

function* g() {

yield 1;

console.log('throwing an exception');

throw new Error('generator broke!');

yield 2;

yield 3;

}

function log(generator) {

var v;

console.log('starting generator');

try {

v = generator.next();

console.log('第一次运行next方法', v);

} catch (err) {

console.log('捕捉错误', v);

}

try {

v = generator.next();//'throwing an exception'

console.log('第二次运行next方法', v);

} catch (err) {//generator broke!

console.log('捕捉错误', v);

}

try {

v = generator.next();

console.log('第三次运行next方法', v);

} catch (err) {

console.log('捕捉错误', v);

}

console.log('caller done');

}

log(g());//然后第三次运行的时候,Generator 函数就已经结束了,不再执行下去了。

// Generator 函数返回的遍历器对象return方法,返回给定的值,并且终结遍历 Generator函数,如果return方法调用时,不提供参数,则返回值的value属性为undefined。

// g.return('foo') // { value: "foo", done: true }

function* numbers () {

yield 1;

try {

yield 2;

yield 3;

} finally {//如果 Generator 函数内部有try...finally代码块,且正在执行try代码块,那么return方法会导致立刻进入finally代码块,执行完以后,整个函数才会结束。

yield 4;

yield 5;

}

yield 6;

}

var g = numbers();

g.next() // { value: 1, done: false }

g.next() // { value: 2, done: false }

g.return(7) // { value: 4, done: false }

g.next() // { value: 5, done: false }

g.next() // { value: 7, done: true }然后等到finally代码块执行完,再返回return()方法指定的返回值

// throw()是将yield表达式替换成一个throw语句。return()是将yield表达式替换成一个return语句。

// 如果yield表达式后面跟的是一个遍历器对象,需要在yield表达式后面加上星号,表明它返回的是一个遍历器对象。这被称为yield*表达式。

function* foo() {

yield 2;

yield 3;

return "foo";//如果被代理的 Generator 函数有return语句,那么就可以向代理它的 Generator 函数返回数据。

}

function* bar() {

yield 1;

var v = yield* foo();

console.log("v: " + v);

yield 4;

}

var it = bar();

it.next()

// {value: 1, done: false}

it.next()

// {value: 2, done: false}

it.next()

// {value: 3, done: false}

it.next();

// "v: foo"

// {value: 4, done: false}

it.next()

// {value: undefined, done: true}

// 上面代码在第四次调用next方法的时候,屏幕上会有输出,这是因为函数foo的return语句,向函数bar提供了返回值。

function* genFuncWithReturn() {

yield 'a';

yield 'b';

return 'The result';

}

function* logReturned(genObj) {

let result = yield* genObj;

console.log(result);

}

// 第一次是扩展运算符遍历函数logReturned返回的遍历器对象

// 第二次是yield*语句遍历函数genFuncWithReturn返回的遍历器对象

// 最终表现为扩展运算符遍历函数genFuncWithReturn返回的遍历器对象:[a,b];return语句的返回值The result,会返回给函数logReturned内部的result变量,因此会有终端输出

console.log([...logReturned(genFuncWithReturn())]);//The result,[a,b];

// 让 Generator 函数返回一个正常的对象实例,既可以用next方法,又可以获得正常的this,例

function* Fthis() {

this.a = 1;

yield this.b = 2;

yield this.c = 3;

}

var objthis = {};

var fthis = Fthis.call(obj);//使用call方法绑定 Generator 函数内部的this

fthis.next(); // Object {value: 2, done: false}

fthis.next(); // Object {value: 3, done: false}

fthis.next(); // Object {value: undefined, done: true}

objthis.a // 1 //构造函数调用以后,这个空对象就是 Generator 函数的实例对象了

objthis.b // 2

objthis.c // 3

// 下面将fthis和objthis统一 objthis换成Fthis.prototype

// function* Fthis() {

// this.a = 1;

// yield this.b = 2;

// yield this.c = 3;

// }

// var fthis = Fthis.call(Fthis.prototype);

// fthis.next(); // Object {value: 2, done: false}

// fthis.next(); // Object {value: 3, done: false}

// fthis.next(); // Object {value: undefined, done: true}

// fthis.a // 1

// fthis.b // 2

// fthis.c // 3

// 再将F改成构造函数,就可以对它执行new命令了。

// function* gen() {

// this.a = 1;

// yield this.b = 2;

// yield this.c = 3;

// }

// function F() {

// return gen.call(gen.prototype);

// }

// var f = new F();

// f.next(); // Object {value: 2, done: false}

// f.next(); // Object {value: 3, done: false}

// f.next(); // Object {value: undefined, done: true}

// f.a // 1

// f.b // 2

// f.c // 3

// 利用 Generator 函数,可以在任意对象上部署 Iterator 接口。在任意对象上部署next方法

function* iterEntries(obj) {

let keys = Object.keys(obj);

for (let i=0; i < keys.length; i++) {

let key = keys[i];

yield [key, obj[key]];

}

}

let myObj = { foo: 3, bar: 7 };

for (let [key, value] of iterEntries(myObj)) {

console.log(key, value);

}

// async函数

// 并发发出远程请求:

async function logInOrder(urls) {

// 并发读取远程URL

const textPromises = urls.map(async url => {

const response = await fetch(url);

return response.text();

});

// 按次序输出

for (const textPromise of textPromises) {

console.log(await textPromise);

}

}

// 类

// ES5 的构造函数,对应 ES6 的类的构造方法constructor.

// 类的所有方法都定义在类的prototype属性上面,在类的实例上面调用方法,其实就是调用原型上的方法。

// Object.assign方法向类添加多个方法:Object.assign(Point.prototype, {toString(){},toValue(){} });

// prototype对象的constructor属性,直接指向“类”的本身

// 与 ES5 一样,类的所有实例共享一个原型对象,使用 Object.getPrototypeOf 方法来获取实例对象的原型。__proto__属性改写原型,必须相当谨慎

// 在“类”的内部使用get和set关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为;存值函数和取值函数是设置在属性的 Descriptor 对象上的。

// class可以写成表达式的形式,类名(name)只能在class内部引用.

class Foo {

constructor(...args) {

this.args = args;

}

* [Symbol.iterator]() {//Generator 函数

for (let arg of this.args) {

yield arg;

}

}

}

for (let x of new Foo('hello', 'world')) {

console.log(x);// hello // world

}

//this指向 类的方法内部如果含有this,它默认指向类的实例

class Objthis {

constructor() {//this指向

this.getThis = () => this;

}

}

const myObjthis = new Objthis();

console.log(myObjthis.getThis() === myObjthis); // true

ps(#私有方法和私有属性,是只能在类的内部访问的方法和属性,外部不能访问。)

ps(in运算符判断某个对象是否有私有属性:

class C {
  #brand;

  static isC(obj) {
    if (#brand in obj) {
      // 私有属性 #brand 存在
      return true;
    } else {
      // 私有属性 #foo 不存在
      return false;
    }
  }
}

//方法前加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用.Foo.classMethod() // 'hello'; foo.classMethod() //TypeError

//如果静态方法包含this关键字,这个this指的是类,而不是实例。一旦单独使用该方法,很可能报错(this会指向该方法运行时所在的环境)。一个比较简单的解决方法是,在构造方法中绑定this:

class Logger {
  constructor() {
    this.printName = this.printName.bind(this);
  }

  // ...
}

// 静态方法也是可以从super对象(继承父类的构造函数)上调用的

class FooSuper {

static classMethod() {

return 'hello';

}

}

class BarSuper extends FooSuper {

static classMethod() {//super 调用父类的...

return super.classMethod() + ', too';

}

}

BarSuper.classMethod(); // "hello, too"

// 子类继承父类时,new.target会返回子类

//类的继承

1.Class 可以通过extends关键字实现继承,让子类继承父类的属性和方法。

2.ES6 规定,子类必须constructor()方法中调用super();如果子类没有定义constructor()方法,这个方法会默认添加,并且里面会调用super()。

// 在子类的构造函数中,只有调用super之后,才可以使用this关键字

// super虽然代表了父类A的构造函数,但是返回的是子类B的实例,即super内部的this指的是B的实例,

// 因此super()在这里相当于A.prototype.constructor.call(this)。

// super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。在子类的静态方法中通过super调用父类的方法时,方法内部的this指向当前的子类,而不是子类的实例。

// 由于super指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super调用的,

// 如果属性定义在父类的原型对象上,super就可以取到。

3.父类所有的属性和方法(包括静态),都会被子类继承,除了私有的属性和方法。如果父类定义了私有属性的读写方法,子类就可以通过这些方法,读写私有属性。

4.静态属性是通过浅拷贝实现继承的(子属性改变不影响父改变),如果父类的静态属性的值是一个对象,那么子类的静态属性也会指向这个对象(子属性改变,父也改变)

// 类的应用

// 子类的__proto__属性,表示构造函数的继承,总是指向父类。作为一个对象,B.__proto__ = A;

// 子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。作为一个构造函数,B.prototype.__proto__ = A.prototype;

// 通过子类实例的__proto__.__proto__属性,可以修改父类实例的行为

// Module的语法

// export使用大括号指定所要输出的一组变量 export { firstName, lastName, year };可以使用as关键字重命名。

// 第一组是使用export default时,对应的import语句不需要使用大括号;第二组是不使用export default时,对应的import语句需要使用大括号

// 第一组

// export default function crc32() { // 输出

// // ...

// }

// import crc32 from 'crc32'; // 输入

// // 第二组

// export function crc32() { // 输出

// // ...

// };

// import {crc32} from 'crc32'; // 输入

// export { each as forEach };暴露出forEach接口,默认指向each接口

// Module的加载实现

// 两种脚本异步加载的方式:defer先渲染后执行;async下载完就执行

// <script src="path/to/myModule.js" defer></script> defer要等到整个页面在内存中正常渲染结束(DOM 结构完全生成,以及其他脚本执行完成),才会执行

// <script src="path/to/myModule.js" async></script> async一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。

// 浏览器加载 ES6 模块 type="module"属性,是异步加载,等同于打开了<script>标签的defer

// 不同的脚本加载这个模块,得到的都是同一个实

// Node.js 遇到.mjs文件,就认为它是 ES6 模块,默认启用严格模式;如果不希望将后缀名改成.mjs,可以在项目的package.json文件中,指定type字段为module

// .mjs文件总是以 ES6 模块加载,.cjs文件总是以 CommonJS 模块加载,.js文件的加载取决于package.json里面type字段的设置。

// package.json文件有两个字段可以指定模块的入口文件:main和exports。比较简单的模块,可以只使用main字段,指定模块加载的入口文件。

// {"type": "module","main": "./src/index.js"}它的格式为 ES6 模块。如果没有type字段,index.js就会被解释为 CommonJS 模块

// 函数具有提升作用,在执行import {bar} from './b'时,函数foo就已经有定义了

// 编码风格

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

// 使用数组成员对变量赋值时,优先使用解构赋值。function getFullName({ firstName, lastName }) {}

// 如果函数返回多个值,优先使用对象的解构赋值,而不是数组的解构赋值。

// 单行定义的对象,最后一个成员不以逗号结尾。多行定义的对象,最后一个成员以逗号结尾。添加属性不可避免,要使用Object.assign方法:Object.assign(a, { x: 3 });

// 使用扩展运算符(...)拷贝数组。const itemsCopy = [...items];Array.from 方法,将类似数组的对象转为数组:const nodes = Array.from(document.querySelectorAll('.foo'));

// 简单的、单行的、不会复用的函数,建议采用箭头函数

// 所有配置项都应该集中在一个对象,放在最后一个参数function divide(a, b, { option = false } = {}) {}

// 不要在函数体内使用 arguments 变量,使用 rest 运算符(...)代替:function concatenateAll(...args) { return args.join('');}

// 使用默认值语法设置函数参数的默认值:function handleThings(opts = {}) {}

// 如果只是需要key: value的数据结构,使用 Map 结构。let map = new Map(arr); for of

// 总是用 Class,取代需要 prototype 的操作

// 如果模块只有一个输出值,就使用export default,如果模块有多个输出值,就不使用export default,export default与普通的export不要同时使用

// 不要在模块输入中使用通配符。因为这样可以确保你的模块之中,有一个默认输出(export default)。

// 异步编程风格

const asyncIterable = createAsyncIterable(['a', 'b']);

const asyncIterator = asyncIterable[Symbol.asyncIterator]();

const [{value: v1}, {value: v2}] = await Promise.all([

asyncIterator.next(), asyncIterator.next()

]);

console.log(v1, v2); // a b

// for...of循环用于遍历同步的 Iterator 接口。新引入的for await...of循环,则是用于遍历异步的 Iterator 接口。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值