1. 变量
var
声明的变量是function scope
也就是在函数中可以访问到,并不是在大括号里面声明的,外层就访问不到。let const
声明的变量都是block scope
块级作用域,也就是在大括号里面可以访问到。
let
和const
都不允许重复声明。他们两个都不可以在同一个作用域中进行重复声明,比如,在函数中声明的变量跟在函数外声明的同名的变量是不同的。let
定义的变量可以重新赋值,const
是不可以的。const
声明的对象,对象是一个引用类型的值,可以进行修改对象的属性。如果想要定义的对象的属性是不可以改变可以使用es5
中的Object.freeze()
这个方法:const jelly = Object.freeze(person)
let
跟const
都是存在暂时性死区的,也就是说;我们在声明前使用调用变量,会报RefrenceError
的错误;而var
声明的变量是没有的;这样,我们也可以养成习惯,在使用变量之前,进行声明。
2. 箭头函数
2.1 优点:
- 简明语法
- 可以隐式返回:也就是可以不使用
return
关键字进行返回;删除{}
大括号。 - 不绑定
this
:箭头函数没有自己的this
值,他的this
值是继承他的父级作用域的,this
值是定义的时候就绑定了。 - 箭头函数也是匿名函数,我们可以通过定义变量,等于箭头函数,当进行调用:
const gree = name => { alert (`hello ${name}`)}
一个函数在他独立运行的时候,也就是说他没有作为对象的方法去调用,也没用call
、apply
等方法进行绑定;这里的数组里面map
的回调函数就是一个独立运行的,所以他的this
值是在运行的时候动态绑定的,绑定到了window
对象上,所以不会获取到name
值。
const jelly = {
name: 'jiegiser',
bobbies: ['sleeping', 'coding', 'reading'],
print: function () {
this.hobbies.map(function (hobby) {
console.log(`${this.name} loves ${hobby}`)
})
}
}
2.2 箭头函数的局限性:
- 作为构造函数,一个方法需要绑定到对象:下面的代码。
Person
对象中使用箭头函数,并没有将this
绑定到它本身。所以,这里需要使用原始的函数。下面的给原型绑定方法,同样也是,this
指向的是父级的this
所以指向window
对象;
const Person = (name, points) => {
this.name = name;
this.points = points;
}
const jelly = new Person ('Jelly', 5);
Person.prototype.updatePoints = () => {
this.points ++;
console.count(this.points)
}
- 当你真的需要this的时候:下面代码的
this
指向的是调用addEventListener
方法的对象;箭头函数的this
是不会绑定的,所以这里的也还是window
对象。setTimeout
里面应该是使用箭头函数,来邦定this
值在该作用域。
const button = document.querySelector('.zoom');
button.addEventListener('click', () => {
this.classList.add('in');
setTimeout(() => {
this.classList.remove('in')
}, 2000);
})
- 需要使用arguments对象:箭头函数中是没有
arguments
对象的。
const sum = () => {
return Array.from(arguments).reduce((prevSum, value) => prevSum + value,0)
}
3. ES6 参数默认值
我们在函数中可以通过下面的方法进行设置默认参数值:
function add (a = 1, b = 5) {
return a + b;
}
当我们需要第一个参数使用默认值的时候,这样add(undeffined, 2)
这样,如果add(1)
这样调用的话,第二个参数使用默认值。
4. 模板字符串
es6
中的模板字符串是可以嵌套的,以及在${}
中可以直接调用方法。
还有一个标签模板字符串:
function highLight(string, user, topic) {
return 'templa'
}
const user = 'jie';
const topic = 'learn es6';
const sentence = highLight`${user} has commented on your topic ${topic}`
然后我们查看sentence
,他就会返回标签模板字符串中返回的东西;
对应标签模板字符串是有是三个参数的,string, user, topic
第一个参数是返回模板字符串中默认内容;返回值是一个数组,如果不是以模板字符串传入的变量开头的,那数组的第一个不死空字符串;如上面代码中,返回的是[" ",has commented on your topic
," "],然后后面的参数就是模板字符串中传入的变量。我们这里传入的是${user}
跟${topic}
,但是如果我们传入了很多的参数,这个时候我们可以使用es6
的剩余参数:...values
他是参数的数组,这样我们就获取到所有的参数。我们可以使用标签模板字符串,来返回我们想要的值。
function highLight(string, ...values) {
debugger;
return 'templa'
}
我们可以使用标签模板字符串,去处理我们输入的参数,比如在留言板等功能中,为了防止xss
攻击,我们可以通过模板字符串进行过滤用户输入的内容。可以使用第三方的包:DOMPurify
进行过滤。
5. 对象解构
我们有一个对象如下:
const Tom = {
name: 'tom',
age: 18,
family: {
mother: 'tom mother',
father: 'tom father'
}
}
我们每次访问的时候,需要进行Tom.name
这样进行访问,如果属性有很多的话,就比较麻烦,我们可以这样写:这样去访问他的属性
const Tom = {
name: 'tom',
age: 18,
family: {
mother: 'tom mother',
father: 'tom father'
}
}
const { name, age } =Tom
console.log(name)
console.log(age)
如果我们想要先声明变量,然后进行结构对象,可以如下面这样写:
let name = '';
({ name, age } = Tom);
对象解构也可以进行嵌套,如上面的代码,我们想要访问family
属性,我们可以这样写:
const Tom = {
name: 'tom',
age: 18,
family: {
mother: 'tom mother',
father: 'tom father'
}
}
const { father, mother } =Tom.family
console.log(mother)
console.log(father)
我们在解构的时候也可以进行重新命名,类似sql
中的as
;可以这样写:father: f
这样,将father
属性赋值给f
变量。
const Tom = {
name: 'tom',
age: 18,
family: {
mother: 'tom mother',
father: 'tom father'
}
}
const { father: f, mother } =Tom.family
console.log(mother)
console.log(father)
我们在对象解构的时候,也可以给解构后的变量赋予默认值,如果被解构的对象没有该属性为undefined
,就使用我们的默认值,如下代码:sister = 'haove no sister'
这样。
const Tom = {
name: 'tom',
age: 18,
family: {
mother: 'tom mother',
father: 'tom father'
}
}
const { father: f, mother, sister = 'haove no sister' } =Tom.family
console.log(mother)
console.log(father)
如下面代码,经常会在封装一些插件中,会使用默认值:
6. 数组解构
数组的解构基本跟对象的解构类似:下面代码是解构了数组的第一项,跟第二项
const number = ['one', 'two', 'three', 'four'];
const [one, two] = number;
console.log(one, two)
如果我们想回去第一个跟第三个:需要添加一个逗号进行分隔,流出第二个位置。
const number = ['one', 'two', 'three', 'four'];
const [one, , two] = number;
console.log(one, two)
如果想要获取到第一个值,跟后面所有的值,我们可以使用扩展运算符,将剩余项组成一个数组:
const number = ['one', 'two', 'three', 'four'];
const [one, ...other] = number;
console.log(one, other)
rest
参数只能使最后一个,也就是说解析后面所有的,不能解析某一部分:const [one, ...other, four] = number;
这样就会报错。
数组的结构也可以进行赋予默认值:同样相应的值为undefined
的时候,才会使用默认的值。
const number = ['one', 'two', 'three'];
const [one, two, three = 'three'] = number;
数组的解构的使用技巧可以用在交换两个变量的值:
let a = 10;
let b = 20;
[a,b] = [b,a];
7. for of 用法
forEach
循环不能终止循环,for in
循环,循环的是index
值,遍历的是变量的可枚举属性。即使是原型的属性也会进行循环出来。使用for of
是循环的属性值:在循环中,也可以使用break
进行中断循环。
const number = ['one', 'two', 'three', 'four'];
for (item of number) {
console.log(item)
}
for of
循环还可以用于遍历数组的遍历器属性:
const number = ['one', 'two', 'three', 'four'];
for ( let [index, fruit] of number.entries()){
console.log(index, number)
}
for of
不支持对象的循环。可以应用于字符串:
const number = 'sddsdsd;
for ( let code of number){
console.log(code)
}
for of
循环还可以用于nodeList
进行循环:
const lis = document.querySelectorAll('li')
for ( let li of lis) {
li.addEventListener('click', function () {
//执行方法
})
}
8. Array.from() Array.of()
8.1 Array.from()
这两个方法并不是数组原型的方法,我们需要通过Array.from() Array.of()
这种去调用,而不是声明一个数组点上这个方法。Array.from()
方法是将一个类素组对象转换为数组对象。类素组对象也就是拥有length
属性的对象,modeList
就是一个类数组对象。
const lis = document.querySelectorAll('li')
const lisArr = Array.form(lis)
const names = lisArray.map(li => li.textContent)
Array.from()
方法有两个参数,第一个参数是需要准换的类数组,第二个参数类似数组的map
方法,会给转化后的数组中的每一项执行该方法,于是,上面的代码可以简写为下面:
const lis = document.querySelectorAll('li')
const names = Array.form(lis, li => li.textContent)
也可以利用Array.from()
方法进行转换函数中的arguments
属性进行转化。
我们也可以利用Array.from()
方法将字符串也能转换为一个数组。
const number = 'sddsdsd;
console.log(Array.from(number))
8.2 Array.of()
Array.of()
方法就是根据传入的参数,返回一个由传入的参数组成的数组;
9. 数组的其他方法
9.1 .find()
查找是否有bananas
这个选项;返回的是找到的该选项,以对象返回;
const inventory = [
{name: 'apples', quantity: 2},
{name: 'bananas', quantity: 0},
{name: 'cherries', quantity: 5},
]
const bananas = inventory.find(fruit => {
if (fruit.name === 'bananas') {
return true
}
return false
})
9.2 .findIndex()
findIndex()
返回的是要查找的选项的索引值:
const inventory = [
{name: 'apples', quantity: 2},
{name: 'bananas', quantity: 0},
{name: 'cherries', quantity: 5},
]
const bananas = inventory.findIndex(fruit => fruit.name === 'bananas');//返回的是索引值1
9.3 .some()
返回的布尔值;如果有一部分满足测试函数,就返回true
const isEnough= inventory.some(fruit => fruit.quantity > 0);//返回true
9.4 .every()
返回的布尔值;如果所有的都满足测试函数,就返回true
const isAllenough= inventory.every(fruit => fruit.quantity > 0);//返回false
10. 剩余参数
function sum (...numbers) {
console.log(numbers)//一个数组[1, 2, 3, 4]
}
sum (1, 2, 3, 4)
11. 扩展运算符
我们如果想要两个数组,合并为一个数组,并且,在中间插入一个值,如下我们以前写的代码:
const youngers = ['george' , 'john', 'Thomas'];
const olders = ['James', 'Adrew', 'Martin'];
let members = [];
members = members.concat(youngers);
members.push('Mary');
members = members.concat(olders);
使用es6
可以这样写:
const youngers = ['george' , 'john', 'Thomas'];
const olders = ['James', 'Adrew', 'Martin'];
let members = [...youngers, 'Mary', ...olders];
可以通过扩展运算符将一个字符串变为以每一个字符变为数组中的一项:
[...'jiegiser']
//["j", "i", "e", "g", "i", "s", "e", "r"]
可以借助扩展运算符,放置我们将数组赋值给另一个数组,修改另一个数组中的数据,会将源数组的值进行改变:
const currentMembers = [...numbers];
我们可以使用扩展运算符将可遍历对象扩展到一个新的数组中:
const todos = [...document.querySelectorAll('li')]
比如我们需要删除一个数组中,对象属性id
为2的一项;代码如下:
const todos = [
{id: 1, name: 'Go to store', completed: false},
{id: 2, name: 'Wacth TV', completed: true},
{id: 3, name: 'Go Shopping', completed: false},
]
// 要删除的项的id为2
const id = 2;
const todoIndex = todos.findIndex(todo => todo.id === id)
const newTodos = [...todos.slice(0, todoIndex), ...todos.slice(todoIndex + 1)];
我们还可以将一个数组通过扩展运算符,追加到另一个数组后:
const fruit = ['apple', 'bananas', 'pear'];
const newFruit = ['orange', 'mongo'];
fruit.push(...newFruit)
还可以利用扩展运算符,将数组扩展为函数的参数:
const dateField = [2019, 6, 2];
const data = new Date(...dateField)
console.log(data);
12. 对象的计算属性
假如我们需要定义一个对象:我们希望我们的'user -1': 1
这个数组是每次加一,然后进行定义变量。
const userIds = {
'user -1': 1
'user-2': 2
}
我们可以这样写:
let id = 0
const userIds = {
[`user-${++id}`]: id,
[`user-${++id}`]: id,
[`user-${++id}`]: id,
}
如果我们想要定义一个对象,对象的键是一个数组,对应的每一个值是一个数组,我们可以这样写:
const keys = ['name', 'age', 'birthday'];
const values = ['jiegiser', 18, '0501'];
const jiegiser = {
[keys.shift()]: values.shift(),
[keys.shift()]: values.shift(),
[keys.shift()]: values.shift(),
}
13. ES6 的Promise对象
如下代码,then
里面是执行ajax
执行成功之后的回调函数,catch
是如果请求出现异常,执行的方法。这里是返回一个promise
对象,所以可以继续在外层使用.then()
方法。return axios.get(
https://api.github.com/users/${username}/repos);
let username;
const usersPromise = axios.get('https://api.github.com/users');
usersPromise
.then(response => {
username = response.data[0].login;
return axios.get(`https://api.github.com/users/${username}/repos`);
})
.then(response => {
console.log(response.data)
})
.catch(err => {
console.log(err)
})
一个promise
对象的实例:
const p = new Promise((resolve, reject) => {
//请求成功执行
resolve('success');
// 请求失败的执行
reject(Error('error'))
})
//请求成功执行的回调
p.then(data => {
console.log(data)
})
// 请求失败的执行的回调
.catch(err => {console.log(err)})
如果我们的页面中有多个Promise
对象,这些对象之间执行的顺序时不相关的,互不影响的,我们可以使用下面的方法,处理这些Promise
对象返回的结果:这里需要注意的是,.all()
方法返回的结果是对应的执行Promise
对象的结果,返回的是一个数组,我们可以使用对象解构,去得到不同的请求返回的结果。只有当.all()
方法里面的Promise
对象全部返回的是resolve
的时候,才会执行.then()
方法。否则执行.catch()
Promise
.all([userPromise, movePromise])
.then(response => {
const [users, movice] = response;
console.log(users);
console.log(movice);
})
.catch(err => {
console.log(err)
})
与.all()
方法相对的是一个·Promise.race()
方法,他同样也是处理多个Promise
实例,但是,他是处理的Promise
实例中,只要第一个Promise
实例是执行resolve
也就是回调成功,就会去执行.then()
方法。否则执行.catch()
14. Symbol
symbol
是生成一个唯一的标识符,如下代码:
const one = Symbol('one')
const two = Symbol('two')
console.log(one === two)//false
我们可以使用symbol
来给 对象定义一个相同属性名,但是值不同:
const classRoom = {
[Symbol('lily')]: {grade: 60, gender: 'female'},
[Symbol('nina')]: {grade: 80, gender: 'female'},
[Symbol('nina')]: {grade: 90, gender: 'female'},
}
symbol
类型的值是不能遍历的,如上面的属性,是不能通过for in
等进行遍历的。可以使用Object.getOwnPropertySymbols(classRoom)
进行遍历,获取到属性名。获取属性值:classrom[Symbol(lily)]这样进行获取,不能通过.的方法进行获取。前面定义symbol
属性的时候,是需要使用计算属性的方式进行定义。
const classRoom = {
[Symbol('lily')]: {grade: 60, gender: 'female'},
[Symbol('nina')]: {grade: 80, gender: 'female'},
[Symbol('nina')]: {grade: 90, gender: 'female'},
}
Object.getOwnPropertySymbols(classRoom)
15. ESLint
输入命令:npm install eslint -D
进行本地安装,然后输入命令进行初始化eslint
的配置:eslint --init
然后根据自己的需要进行配置,
输入eslint init.js
进行检测我们的文件的书写规范。
可以输入后面的报错信息到https://eslint.cn/docs/rules/
这里进行查看。
我们可以查看前面生成的eslint
的规则配置:可以看到我们的规则是继承了"extends": "eslint:recommended",
也就是https://eslint.cn/docs/rules/
这里面标绿色箭头的选项,我们可以在我们的rules
里面进行配置我们自己的规则。
module.exports = {
"env": {
"browser": true,
"commonjs": true,
"es6": true
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": 2015,
"sourceType": "module"
},
"rules": {
"indent": [
"error",
4
],
"linebreak-style": [
"error",
"windows"
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"always"
]
}
};
禁用eslint
,比如说我们写vue
的时候,是引入了vue
,这个时候eslint
会提示,,没有定义vue
,我们可以在代码顶部加一个:
/*global Vue*/ //把某一个变量当做全局对象使用
如果想要禁用一个规则,如下:
/*eslint-disable no-new*/ //禁用 no-new 规则
我们也可以在某一行,重新打开我们禁用的规则:
/*eslint-enable no-new*/ //打开 no-new 规则,关闭禁用no-new
还有检查html
中的js
书写,安装eslint-plugin-html
插件
16. import export
默认导出:一个模块只能有一个默认导出;
const apikey = 'abc123';
export default apikey;
默认导出的引入方式:
import api from './config.js'
console.log(api)
命名导出:这里的apikey
就是导出的名字,
export const apikey = 'abc123';
引入的时候,必须使用同样的名字,而且,需要使用{}
来包裹起来:
import { apikey } from './config'
可以引入多个:
import { apikey,a } from './config'
可以用下面的方式导出多个:
export { apikey, age, greet }
也可以在导出的时候,进行重命名:然后引入的时候,必须使用as
命名后的名字;
export { apikey as apk, age, greet }
当然,也可以在引入的时候重新命名:在模块使用的时候,就必须使用重命名的名字;
import { apikey as apk, a } from './config'
默认导出的,吗唉导入的时候,可以使用任意的命名,而命名导出,需要使用我们导出的名字,在导入的时候,进行导入。
一个第三方包:slug
,过滤用户名。还有md5
包。
引入命名导出以及默认导出的变量:User
默认导出的内容。
import User, { apikey as apk, a } from './config'
17. 使用 SystemJS 进行打包
一个非常简单的打包工具,不用进行类似webpack
繁琐的配置。我们可以使用jspm.io
进行加载npm
或者github
上面的包。在标签中引入systemJS
,然后进行配置:
<script>
System.config({transpiler: 'babel'})
System.import('./main.js')
</script>
在js
中引入第三方模块的方式也是不一样的,如下:意思就是在npm
里面查找我们引入的包,进行引入。
import { sum } from 'npm:lodash';
引入本地模块跟前面是一样的。
18. Class
18.1 Class基本用法
是特殊的函数,定义方式:
//第一种
class User {
}
//第二种
const User = class {
}
typeof User
打印出的结果是function
,函数是有函数提升的,而类是没有的。在声明之前使用,会报错。
这里需要注意的是,类里面定义函数,之间是不需要使用逗号隔开的,加了逗号会报错;
class User {
constructor (name, email) {
this.name = name;
this.email = email;
}
info () {
console.log(`I'm ${this.name}`)
}
}
const jiegiser = new User('jiegiser', 'jiegiser@163.com')
console.log(jiegiser)
静态方法的定义:静态方法--不能实例化调用,只能在原型对象调用,一般将原型对象里面的方法定义为静态方法
class User {
constructor (name, email) {
this.name = name;
this.email = email;
}
info () {
console.log(`I'm ${this.name}`)
}
//静态方法--不能实例化调用,只能在原型对象调用,一般将原型对象里面的方法定义为静态方法
static descript () {
console.log(`hi jj`)
}
}
const jiegiser = new User('jiegiser', 'jiegiser@163.com')
console.log(jiegiser)
定义get set
方法:
class User {
constructor (name, email) {
this.name = name;
this.email = email;
}
info () {
console.log(`I'm ${this.name}`);
}
//静态方法--不能实例化调用,只能在原型对象调用,一般将原型对象里面的方法定义为静态方法
static descript () {
console.log(`hi jj`);
}
set github (value) {
this.githubName = value;
}
get github () {
return `http://github.com/${this.githubName}`;
}
}
const jiegiser = new User('jiegiser', 'jiegiser@163.com')
console.log(jiegiser)
18.2 Class的扩展
在类的定义中,定义方法的时候,也可以使用计算属性的方式进行定义:
let methodName = 'info';
class User {
constructor (name, email) {
this.name = name;
this.email = email;
}
[methodName] () {
console.log(`I'm ${this.name}`);
}
//静态方法--不能实例化调用,只能在原型对象调用,一般将原型对象里面的方法定义为静态方法
static descript () {
console.log(`hi jj`);
}
set github (value) {
this.githubName = value;
}
get github () {
return `http://github.com/${this.githubName}`;
}
}
类必须要使用new
关键字进行调用。下面是类的继承:注意super(name);
进行调用父类构造函数。
class Animal {
constructor (name) {
this.name = name;
this.belly = [];
}
eat (food) {
this.belly.push(food)
}
}
class Dog extends Animal {
constructor (name, age) {
//在子类中调用父类构造函数
super(name);
this.name = name;
this.age = age;
}
bark () {
console.log(`Barl bark!`);
}
}
const lucky = new Dog('lucky', 2);
18.3 Class 进行扩展内建对象
我们可以通过class
扩展javascript
中的内建对象,比如扩展Array
对象:
class MyArray extends Array {
constructor () {
super();
}
}
const colors = new MyArray();
colors[0] = 'red';
console.log(colors.length);
colors.length = 0;
console.log(colors[0])
在子类中其实可以通过this
来访问父级Array
的一些属性跟方法的。这里就是调用了Array
的push
方法。
class movieCollaction {
constructor (name, ...items) {
super(...items)
this.name = name;
}
add (item) {
this.push(item)
}
toRated (limit = 10) {
return this.sort((a, b) => (a.scores > b.scores) ? -1 : 1).slice(0, limit);
}
}
const movies = new movieCollaction('favorite movies',
{ name: 'the croods', scored: 8.7},
{ name: 'the days', scored: 9.6},
{ name: 'the shawshank', scored: 9.4},
{ name: 'the summer', scored: 8.0},
);
19. Proxy
帮助我们重写对象上的默认方法。比如下面的的方法:
const personn = { name: 'jiegiser', age: 200};
// 第一个参数为要代理的对象,第二个参数就是一个对象,包含重写的方法。也就类似vue计算属性
const personProxy = new Proxy(personn, {
get(target, key) {
return target[key].toUpperCase();
},
set (target, key, value) {
if(typeof value === 'string'){
target[key] = value.trim();
}
}
})
personProxy.name = 'jie';
Proxy
的一个例子:对电话号码进行格式化输出
const phonerHandle = {
set(target, key, value) {
target[key] = value.match(/[0-9]/g).join('');
}
get(target, key) {
return target[key].replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3');
}
}
const pgoneNumber = new Proxy({}, phonerHandle)
20. Set
一种唯一的数组,也就是数组中的数据是唯一的,不会重复,不能通过索引进行获取。使用add
添加值,delete
删除某个值。检验某个值是否存在has
,移除所有的元素.clear()
。.values
是一个set
的一个遍历器。可以使用for of
进行循环遍历。
const colors = new Set();
colors.add('red')
colors.add('green')
colors.add('blue')
可以使用下面的的方法,进行遍历:
const colors = new Set();
colors.add('red')
colors.add('green')
colors.add('blue')
const iterator = colors.values();
iterator.next();
使用for of
for (let color of colors) {
console.log(color)
}
使用forEach
进行遍历;
colors.forEach((item, key, ownSet) => {
console.log(item,key,ownSet)
})
set
接收的是一个可遍历对象,我们也可以传入一个数组,进行初始化:
const fruits = new Set(['apple', 'banana', 'mongo'])
weakSet
跟set
非常相似,只不过,他存储的项只能是对象,不能存储字符串,而且不能通过for of
进行循环,他没有迭代器,也不能使用forEach
进行循环。他没有clear()
清除全部,他可以自己进行检测进行注销对应的数据。
21. Map
map
跟set
非常相似,不同的是,map
通过.set('key',value)
这种方法进行添加元素,他也相当于一个数组,数组中是一个个对象。对象的键可以是任意数据类型;
const people = new Map();
people.set('jelly', 23);
people.set('{}',3)
获取对应的值:
people.get('jellu')//传入键
获取里面j键值对的数量:
people.size()
删除某个项:
people.delete('jelly')
删除所有元素:
people.clear()
循环遍历元素:
forEach
进行循环;
people.forEach(function(value, key, map) => {
console.log(value, key, map)
})
使用for of
进行循环:
for ( let [key,value] of people) {
console.log(key, value)
}
初始化参数:
const fruits= new Map([['apple',6], ['banans', 5]])
wekMap
没有size
属性,不能循环;他的key
必须是对象;垃圾回收机制,会自动回收。