目录
Set 和 Map 数据结构
Set 数据结构
基本用法
size属性
Set 数据结构方法
add()
delete()
has()
clear()
Array.from方法可以将 Set 结构转为数组。
遍历操作
WeakSet数据结构
Map数据结构
Map数据结构的属性和方法
Proxy
Reflect
同步与异步
Promise 对象
Promise对象_方法
Generator 函数的语法
Async 函数
Fetch API
Fetch API POST请求注意事项
Fetch网络请求应用
封装Fetch网络请求
Class 的基本语法
类的由来
constructor 方法
类的实例
取值函数(getter)和存值函数(setter)
静态方法
静态属性
私有方法和私有属性
Class 的继承
super 关键字
Module 的语法
export 命令
import 命令
模块的整体加载
export default 命令
const s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
for (let i of s) {
console.log(i);
}
// 2 3 5 4
const set = new Set([1, 2, 3, 4, 4]);
[...set]
// [1, 2, 3, 4]
// 去除数组的重复成员
[...new Set(array)]
[...new Set('ababbc')].join('')
// "abc"
var mySet = new Set();
mySet.add("5")
mySet.add(5)
console.log(mySet); // Set(2) {'5', 5}
const items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
items.size // 5
var mySet = new Set();
mySet.add("5")
console.log(mySet);
var mySet = new Set();
mySet.add("5")
var flag = mySet.delete("5");
console.log(flag); // true
var mySet = new Set();
mySet.add("5")
var flag = mySet.has("5");
console.log(flag); // true
var mySet = new Set();
mySet.add("5")
mySet.clear();
console.log(mySet); // Set(0) {size: 0}
const items = new Set([1, 2, 3, 4, 5]);
const array = Array.from(items);
这就提供了去除数组重复成员的另一种方法。
function dedupe(array) {
return Array.from(new Set(array));
}
dedupe([1, 1, 2, 3]) // [1, 2, 3]
let set = new Set(['red', 'green', 'blue']);
for (let item of set.keys()) {
console.log(item); // red green blue
}
for (let item of set.values()) {
console.log(item); // red green blue
}
for (let item of set.entries()) {
console.log(item); // ['red', 'red'] ['green', 'green'] ['blue', 'blue']
}
let set = new Set([1, 4, 9]);
set.forEach((value) => console.log(value))
const ws = new WeakSet();
ws.add(1)
// TypeError: Invalid value used in weak set
在初始化 WeakSet 的时候,无论是数组还是对象,都是将他们的成员放入到 WeakSet 中的
const ws = new WeakSet([{name: 'iwen'}]);//再套一层
console.log(ws);
const ws = new WeakSet([[10,20]]);
console.log(ws);
let user1 = {
"name1": "iwen"
}
let user2 = {
"name2": "itbaizhan"
}
let weakSet = new WeakSet([user1])
console.log(weakSet.add(user2)) // WeakSet
{{…}, {…}}
console.log(weakSet.has(user2)) // true
console.log(weakSet.delete(user2)) // true
console.log(weakSet.has(user2)) // false
var user = {
"name":"iwen",
age:20
}
const data = {};
const element = document.getElementById('myDiv');
data[element] = 'metadata';
data['[object HTMLDivElement]'] // "metadata"
const map = new Map();
map.set(['a'], 555);
map.get(['a']) // undefined
const map = new Map();
const k1 = ['a'];
const k2 = ['a'];
map.set(k1, 111).set(k2, 222);
map.get(k1) // 111
map.get(k2) // 222
const map = new Map();
// const k1 = ['a'];
// const k2 = ['a'];
map.set(['a'], 111).set(['a'], 222);
console.log(map.get(['a']));
// undefined
console.log(map.get(['a']));
// undefined
const map = new Map();
map.set(['hello'], 10);
var h = ["hello"];
map.set(h, 20)
console.log(map.get(h));//20
console.log(map.get(['hello']));//undefined
const map = new Map();
map.set('itbaizhan', true);
map.set('sxt', false);
map.size // 2
const map = new Map();
map.set('itbaizhan', true);
map.set('sxt', false);
console.log(map);
const map = new Map();
map.set('itbaizhan', true).set("sxt",15)
console.log(map);
const map = new Map();
map.set('itbaizhan', true).set("sxt",15)
console.log(map.get("itbaizhan"));
console.log(map.get("it"));
const map = new Map();
map.set('itbaizhan', true).set("sxt",15)
console.log(map.has("itbaizhan"));
const map = new Map();
map.set('itbaizhan', true).set("sxt",15)
map.delete("itbaizhan")
console.log(map);
const map = new Map();
map.set('itbaizhan', true).set("sxt",15)
map.clear()
console.log(map);
const map = new Map();
map.set('itbaizhan', true).set("sxt",15)
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]);
}
map.forEach(function (value, key, map) {
console.log(key,value);
});
var user = {};
var obj = new Proxy(user, {
get: function (target, propKey) {
return "不给你看"
},
set: function (target, propKey, value) {
if (propKey === "age") {
if (!Number.isInteger(value)) {
throw new TypeError('The age is not an integer');
}
if (value > 200) {
throw new RangeError('The age seems invalid');
}
}
target[propKey] = value;
}
})
obj.age = 20
obj.name = "iwen"
console.log(obj.age);//不给你看
const duck = {
name: 'Maurice',
color: 'white',
greeting: function() {
console.log(`Quaaaack! My name is
${this.name}`);
}
}
Reflect.has(duck, 'color');
// true
Reflect.has(duck, 'haircut');
// false
const duck = {
name: 'Maurice',
color: 'white',
greeting: function () {
console.log(`Quaaaack! My name is
${this.name}`);
}
}
console.log(Reflect.ownKeys(duck));//['name', 'color', 'greeting']
const duck = {
name: 'Maurice',
color: 'white',
greeting: function () {
console.log(`Quaaaack! My name is ${this.name}`);
}
}
Reflect.set(duck, 'eyes', 'black');
console.log(duck);//{name: 'Maurice', color: 'white', eyes: 'black', greeting: ƒ}
var user = {}
var obj = new Proxy(user, {
set: function (target, name, value) {
var success = Reflect.set(target,name, value);
if (success) {
console.log('property ' + name +' on ' + target + ' set to ' + value);
}
return success;
}
});
obj.name = "iwen"
console.log(obj.name);
//property name on [object Object] set to iwen
//iwen
举例:你去商城买东西,你看上了一款手机,能和店家说你一个这款手机,他就去仓库拿货,你得在店里等着,不能离开,这叫做同步。现在你买手机直接去京东下单,下单完成后你就可用做其他时间(追剧、打王者、 lol )等货到了去签收就 ok 了 . 这就叫异步
for (var i = 0; i < 10000; i++) {
if (i == 9999){
console.log("循环结束了~~")
}
}
console.log("ok")
// 循环结束了~~
// ok
while(1){
}
console.log("ok")
var n = 0;
setTimeout(function () {
n++;
console.log(n);
}, 1000);
console.log(n);
// 0
// 1
Document
const promise = new Promise(function(resolve,
reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
promise.then(function(value) {
// success
}, function(error) {
// failure
});
Document
getJSON("/posts.json").then(function(json) {
return json.post;
}).then(function(post) {
// ...
},function (err){
console.log("rejected: ", err);
});
getJSON("http://iwenwiki.com/api/blueberrypa
i/getChengpinDetails.php").then(
data =>{
console.log(data)
throw new Error('test');
},
error =>{
console.log("error"+error);
}
).catch(err =>{
console.log("catch"+err);
})
getJSON("http://iwenwiki.com/api/blueberryp
ai/getChengpinDetails.php").then(
data =>{
console.log(data)
},
error =>{
console.log("error"+error);
}
).catch(err =>{
console.log("catch"+err);
}).finally(() =>{
console.log("成功失败都会执行");
})
var url1 =
"http://iwenwiki.com/api/musicimg/1.png"
var url2 =
"http://iwenwiki.com/api/musicimg/2.png"
var promise1 = new Promise(function
(resolve, reject) {
var image = new Image();
image.src = url1;
image.onload = function () {
resolve(image)
}
image.onerror = function () {
reject(new Error('Could not load image at ' + url1))
}
})
var promise2 = new Promise(function
(resolve, reject) {
var image = new Image();
image.src = url2;
image.onload = function () {
resolve(image)
}
image.onerror = function () {
reject(new Error('Could not load image at ' + url2))
}
})
Promise.all([promise1,promise2]).then(data =>{
console.log(data);
}).catch(error =>{
console.log(error);
})
$.getJSON("http://localhost/generator/list.php",function(data){
$.getJSON("http://iwenwiki.com/api/generator/id.php",{id:data[0]},function(data){
$.getJSON("http://iwenwiki.com/api/generator/name.php",{name:data.name},function(data){
console.log(data);
})
})
})
function getIds(){
return new Promise(function(resolve,reject){
$.getJSON("http://iwenwiki.com/generator/list.php",function(data){
resolve(data)
},function(error){
reject(error)
})
})
}
getIds().then(data =>{
return new Promise((resolve,reject) =>{
$.getJSON("http://iwenwiki.com/api/generator/id.php", { id: data[0] },function(data){
resolve(data)
},function(error){
reject(error)
})
})
}).then(data =>{
return new Promise((resolve,reject) =>{
$.getJSON("http://iwenwiki.com/api/generator/name.php", { name: data.name
},function(data){
resolve(data)
},function(error){
reject(error)
})
})
}).then(data =>{
console.log(data);
})
Document
function * foo(x, y) { ··· }
function *foo(x, y) { ··· }
function* foo(x, y) { ··· }
function*foo(x, y) { ··· }
function* foo() {
yield 1
yield 2
yield 3
yield 4
yield 5
return 6
}
for (let i of foo()) {
console.log(i); // 1 2 3 4 5 没有6
}
function print(){
setTimeout(() =>{
console.log("定时器");
},1000)
console.log("Hello");
}
print()
function timeout(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function asyncPrint(value, ms) {
await timeout(ms);
console.log(value);
}
asyncPrint('hello world', 50);
Document
fetch(url)
.then(...)
.catch(...)
fetch('http://iwenwiki.com/api/blueberrypai/g
etChengpinDetails.php')
.then(response => response.json())
.then(json => console.log(json))
.catch(err => console.log('Request
Failed', err));
// fetch默认是get请求方式 post请求方式
fetch("http://iwenwiki.com/api/blueberrypai/login.php", {
method: "POST",
headers: {
"Content-type": "application/x-www-form-urlencoded; charset=UTF-8",
},
// post请求的参数
// 接受的参数类型其实和后台也有关系,但是大部分情况下是字符串形式
body: "[email protected]&password=iwen123&verification_code=crfvw"
}).then(res => {
return res.json();
}).then(data => {
console.log(data);
}).catch(error => {
console.log(error);
})
Document
fetch.js
function formator(data){
var dataStr = ""
Object.keys(data).forEach(key => {
dataStr += key + '=' + data[key] + '&';
})
if (dataStr !== '') {
dataStr = dataStr.substring(0, dataStr.length - 1);
}
return dataStr
}
async function ajax(url = "",type = "GET",data = {}){
// GET请求 url?username=iwen&password=123
if(type === "GET"){
var dataStr = formator(data);
url = url +"?"+ dataStr;
}
let requestConfig = {
method:type,
headers: {
"Content-type": "application/x-www-form-urlencoded; charset=UTF-8"
}
}
// POST请求
if(type === "POST"){
requestConfig.body = formator(data);
// 拦截对象,为对象增加新的属性
// 在ES6中,我们不推荐直接为一个对象增加新属性的时候使用赋值方式
// Object.defineProperty(requestConfig,"body",{
// value:formator(data)
// })
}
let response = await fetch(url,requestConfig)
response = await response.json();
return response;
}
JavaScript 语言中,生成实例对象的传统方法是通过构造函数。下面是一个例子。
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function () {
return '(' + this.x + ', ' + this.y + ')';
};
var p = new Point(1, 2);
上面这种写法跟传统的面向对象语言(比如 C++ 和 Java)差异很大,很容易让新学习这门语言的程序员感到困惑。
ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。
基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用 ES6 的class改写,就是下面这样。
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
上面代码定义了一个“类”,可以看到里面有一个constructor方法,这就是构造方法,而this关键字则代表实例对象。也就是说,ES5 的构造函数Point,对应 ES6 的Point类的构造方法。
Point类除了构造方法,还定义了一个toString方法。注意,定义“类”的方法的时候,前面不需要加上function这个关键字,直接把函数定义放进去了就可以了。另外,方法之间不需要逗号分隔,加了会报错。
ES6 的类,完全可以看作构造函数的另一种写法。
class Point {
// ...
}
typeof Point // "function"
Point === Point.prototype.constructor // true
上面代码表明,类的数据类型就是函数,类本身就指向构造函数。
使用的时候,也是直接对类使用new命令,跟构造函数的用法完全一致
class Bar {
doStuff() {
console.log('stuff');
}
}
var b = new Bar();
b.doStuff() // "stuff"
构造函数的prototype属性,在 ES6 的“类”上面继续存在。事实上,类的所有方法都定义在类的prototype属性上面。
class Point {
constructor() {
// ...
}
toString() {
// ...
}
toValue() {
// ...
}
}
// 等同于
Point.prototype = {
constructor() {},
toString() {},
toValue() {},
};
constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。
class Point {
}
// 等同于
class Point {
constructor() {}
}
上面代码中,定义了一个空的类Point,JavaScript 引擎会自动为它添加一个空的constructor方法。
constructor方法默认返回实例对象(即this),完全可以指定返回另外一个对象。
类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行。
class Foo {
constructor() {
return Object.create(null);
}
}
Foo()
// TypeError: Class constructor Foo cannot be invoked without 'new'
生成类的实例的写法,与 ES5 完全一样,也是使用new命令。前面说过,如果忘记加上new,像函数那样调用Class,将会报错。
class Point {
// ...
}
// 报错
var point = Point(2, 3);
// 正确
var point = new Point(2, 3);
//定义类
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
var point = new Point(2, 3);
point.toString() // (2, 3)
与 ES5 一样,在“类”的内部可以使用get和set关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。
class MyClass {
constructor() {
// ...
}
get prop() {
return 'getter';
}
set prop(value) {
console.log('setter: '+value);
}
}
let inst = new MyClass();
inst.prop = 123;
// setter: 123
inst.prop
// 'getter'
类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
class Foo {
static classMethod() {
return 'hello';
}
}
Foo.classMethod() // 'hello'
var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function
上面代码中,Foo类的classMethod方法前有static关键字,表明该方法是一个静态方法,可以直接在Foo类上调用(Foo.classMethod()),而不是在Foo类的实例上调用。如果在实例上调用静态方法,会抛出一个错误,表示不存在该方法。
静态属性指的是 Class 本身的属性,即Class.propName,而不是定义在实例对象(this)上的属性。
class Foo {
}
Foo.prop = 1;
Foo.prop // 1
私有方法和私有属性,是只能在类的内部访问的方法和属性,外部不能访问。这是常见需求,有利于代码的封装,但 ES6 不提供,只能通过变通方法模拟实现。
一种做法是在命名上加以区别
class Widget {
// 公有方法
foo (baz) {
this._bar(baz);
}
// 私有方法
_bar(baz) {
return this.snaf = baz;
}
// ...
}
Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多
class Point {
}
class ColorPoint extends Point {
}
上面代码定义了一个ColorPoint类,该类通过extends关键字,继承了Point类的所有属性和方法。但是由于没有部署任何代码,所以这两个类完全一样,等于复制了一个Point类
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); // 调用父类的constructor(x, y)
this.color = color;
}
toString() {
return this.color + ' ' + super.toString(); // 调用父类的toString()
}
}
上面代码中,constructor方法和toString方法之中,都出现了super关键字,它在这里表示父类的构造函数,用来新建父类的this对象。
子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。
class Point { /* ... */ }
class ColorPoint extends Point {
constructor() {
}
}
let cp = new ColorPoint(); // ReferenceError
上面代码中,ColorPoint继承了父类Point,但是它的构造函数没有调用super方法,导致新建实例时报错。
另一个需要注意的地方是,在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,基于父类实例,只有super方法才能调用父类实例
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
class ColorPoint extends Point {
constructor(x, y, color) {
this.color = color; // ReferenceError
super(x, y);
this.color = color; // 正确
}
}
super这个关键字,既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同。
第一种情况,super作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数。
class A {}
class B extends A {
constructor() {
super();
}
}
上面代码中,子类B的构造函数之中的super(),代表调用父类的构造函数。这是必须的,否则 JavaScript 引擎会报错。
第二种情况,super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类
class A {
p() {
return 2;
}
}
class B extends A {
constructor() {
super();
console.log(super.p()); // 2
}
}
let b = new B();
历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能,比如 Ruby 的require、Python 的import,甚至就连 CSS 都有@import,但是 JavaScript 任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍。
在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。
ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。
// CommonJS模块
let { stat, exists, readfile } = require('fs');
// 等同于
let _fs = require('fs');
let stat = _fs.stat;
let exists = _fs.exists;
let readfile = _fs.readfile;
ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。
// ES6模块
import { stat, exists, readFile } from 'fs';
模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。
一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量。下面是一个 JS 文件,里面使用export命令输出变量
// profile.js
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;
export的写法,除了像上面这样,还有另外一种。
// profile.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export { firstName, lastName, year };
export命令除了输出变量,还可以输出函数或类(class)。
export function multiply(x, y) {
return x * y;
};
使用export命令定义了模块的对外接口以后,其他 JS 文件就可以通过import命令加载这个模块
// main.js
import { firstName, lastName, year } from './profile.js';
function setName(element) {
element.textContent = firstName + ' ' + lastName;
}
除了指定加载某个输出值,还可以使用整体加载,即用星号(*)指定一个对象,所有输出值都加载在这个对象上面。
下面是一个circle.js文件,它输出两个方法area和circumference
// circle.js
export function area(radius) {
return Math.PI * radius * radius;
}
export function circumference(radius) {
return 2 * Math.PI * radius;
}
现在,加载这个模块
// main.js
import { area, circumference } from './circle';
console.log('圆面积:' + area(4));
console.log('圆周长:' + circumference(14));
上面写法是逐一指定要加载的方法,整体加载的写法如下
import * as circle from './circle';
console.log('圆面积:' + circle.area(4));
console.log('圆周长:' + circle.circumference(14));
从前面的例子可以看出,使用import命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载。但是,用户肯定希望快速上手,未必愿意阅读文档,去了解模块有哪些属性和方法。
为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default命令,为模块指定默认输出。
// export-default.js
export default function () {
console.log('foo');
}
上面代码是一个模块文件export-default.js,它的默认输出是一个函数。
其他模块加载该模块时,import命令可以为该匿名函数指定任意名字。
// import-default.js
import customName from './export-default';
customName(); // 'foo'