五、class的基本语法
1.类
(1)ES6通过class关键字,可定义类
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
说明:
- 定义了一个“类”,可以看到里面有一个constructor方法,这就是构造方法,而this关键字则为实例对象;
- Point类除了构造方法,还定义了一个toString方法。
注:
- 定义“类”的方法的时候,前面不需加function这个关键字;
- 方法之间不需逗号分隔
(2)ES6 的类,可看作构造函数的另一种写法
class Point {
// ...
}
typeof Point // "function"
Point === Point.prototype.constructor // true
说明:类的数据类型就是函数,类本身就指向构造函数
(3)使用时,直接对类使用new命令,跟构造函数的用法完全一致
class Ball {
doSport() {
console.log('soccer');
}
}
var b = new Ball();
b.doSport() // "soccer"
(4)构造函数的prototype属性,在 ES6 的“类”上继续存在
事实上,类的所有方法都定义在类的prototype属性上
class Point {
constructor() {
// ...
}
toString() {
// ...
}
toValue() {
// ...
}
}
// 等同于
Point.prototype = {
constructor() {},
toString() {},
toValue() {},
};
(5)在类的实例上调用方法,其实就是调用原型上的方法
class B {}
let b = new B();
b.constructor === B.prototype.constructor // true
说明:b是B类的实例,它的constructor方法就是B类原型的constructor方法。
(6)类的方法都定义在prototype对象上,所以类的新方法可添加在prototype对象上。Object.assign方法可方便地一次向类添加多个方法
class Point {
constructor(){
// ...
}
}
Object.assign(Point.prototype, {
toString(){},
toValue(){}
});
(7)prototype对象的constructor属性,直接指向“类”的本身
这与 ES5 的行为一致
Point.prototype.constructor === Point // true
(8)类的内部所有定义的方法,都是不可枚举的
这与 ES5 的行为不一致
class Point {
constructor(x, y) {
// ...
}
toString() {
// ...
}
}
Object.keys(Point.prototype)
// []
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]
2.constructor方法
(1)constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。
一个类必须有constructor方法,若无显式定义,一个空的constructor方法会被默认添加。
class Point {
}
// 等同于
class Point {
constructor() {}
}
说明:定义了一个空的类Point,JavaScript 引擎自动为它添加空的constructor方法。
(2)constructor方法默认返回实例对象(即this),完全可以指定返回另外一个对象
class Foo {
constructor() {
return Object.create(null);
}
}
new Foo() instanceof Foo
// false
说明:constructor函数返回一个全新的对象,结果导致实例对象不是Foo类的实例。
(3)类必须使用new调用,否则会报错
(4)es6中属性的定义是在constructor里面,即给属性初始化默认值的
3.类的实例
(1)生成类的实例,使用new命令
class Point {
// ...
}
// 报错
var point = Point(2, 3);
// 正确
var point = new Point(2, 3);
(2)实例的属性除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上)
//定义类
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)
point.hasOwnProperty('x') // true
point.hasOwnProperty('y') // true
point.hasOwnProperty('toString') // false
point.__proto__.hasOwnProperty('toString') // true
说明:x和y都是实例对象point自身的属性(因为定义在this变量上),所以hasOwnProperty返回true,而toString是原型对象的属性(因为定义在Point类上,类的所有方法都定义在类的prototype属性上),所以hasOwnProperty返回false。
注:hasOwnProperty()方法可以检测一个属性是存在于实例中,还是存在于原型中。这个方法只在给定属性存在于对象实例中,才会返回true
4.取值函数(getter)和存值函数(setter)
在“类”的内部可以使用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'
说明:prop属性有对应的存值和取值函数,因此赋值和读取行为都被自定义了。
六、Set和Map数据结构
1.Set
基本用法:
(1)类似于数组,但是成员的值都是唯一的,没有重复的值(可做数组去重)
let smap=new Set([1,2,3,1,2,3]);
console.log(smap); //Set(3) {1, 2, 3}
(2)Set本身是一个构造函数,用来生成 Set 数据结构
let s=new Set();
let a=[1,2,5,2,6,4,6];
a.forEach(function(v){
s.add(v);
});
console.log(s); //Set(5) {1, 2, 5, 4, 6}
说明:通过add()方法向 Set 结构加入成员,结果表明 Set 结构不会添加重复的值
(3)去数组重复成员的方法
// 去除数组的重复成员
[...new Set(array)]
(4)去字符串里的重复字符
let a=[...new Set('ababbc')].join('');
console.log(a); //abc
(5)两个对象总是不相等的
let set = new Set();
set.add({});
console.log(set.size); // 1
set.add({});
console.log(set.size); // 2
说明:由于两个空对象不相等,所以它们被视为两个值。
Set 实例的属性和方法:
(1)属性
- Set.prototype.constructor:构造函数,默认就是Set函数;
- Set.prototype.size:返回Set实例的成员总数
(2)方法
- add(value):添加某个值,返回 Set 结构本身;
- delete(value):删除某个值,返回一个布尔值,表示删除是否成功;
- has(value):返回一个布尔值,表示该值是否为Set的成员;
- clear():清除所有成员,没有返回值
例:属性和方法的实例如下
s.add(1).add(2).add(2);
// 注意2被加入了两次
s.size // 2
s.has(1) // true
s.has(2) // true
s.has(3) // false
s.delete(2);
s.has(2) // false
(3)Array.from方法可以将 Set 结构转为数组
const items = new Set([1, 2, 3, 4, 5]);
const array = Array.from(items);
(4)去数组重复成员的另一种方法
function deput(array) {
return Array.from(new Set(array));
}
deput([1, 1, 2, 3]) // [1, 2, 3]
遍历操作:
- keys():返回键名的遍历器
- values():返回键值的遍历器
- entries():返回键值对的遍历器
- forEach():使用回调函数遍历每个成员
(1)keys(),values(),entries()
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"]
说明:entries方法返回的遍历器,同时包括键名和键值,所以每次输出一个数组,它的两个成员完全相等。
可以省略values方法,直接用for…of循环遍历 Set:
let set = new Set(['red', 'green', 'blue']);
for (let x of set) {
console.log(x);
}
// red
// green
// blue
(2) forEach()
Set 结构的实例与数组一样,也拥有forEach方法,用于对每个成员执行某种操作,没有返回值。
let set = new Set([1, 4, 9]);
set.forEach((value, key) => console.log(key + ' : ' + value))
// 1 : 1
// 4 : 4
// 9 : 9
说明:该函数的参数与数组的forEach一致,依次为键值、键名、集合本身。
注:Set 结构的键名就是键值(两者是同一个值),因此第一个与第二个参数的值永远是一样的。
2.Map
基本用法:
(1)类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键
例:类似于存值
let m=new Map();
let s={name:"张三",sex:"男"};
m.set('stu',s);
console.log(m.get('stu')); //{name: "张三", sex: "男"}
console.log(m.has('stu')); //true
例:如何向 Map 添加成员
const m=new Map();
const o={p: 'Hello World'};
m.set(o, 'content');
console.log(m.get(o)); // content
m.has(o); // true
m.delete(o); // true
m.has(o); // false
说明:使用 Map 结构的set方法,将对象o当作m的一个键,然后又使用get方法读取这个键,接着使用delete方法删除了这个键。
(2)作为构造函数,Map 也可以接受一个数组作为参数
该数组的成员是一个个表示键值对的数组。
const map = new Map([
['name', '张三'],
['title', 'Author']
]);
map.size; // 2
map.has('name'); // true
map.get('name'); // "张三"
map.has('title'); // true
map.get('title'); // "Author"
说明:在新建 Map 实例时,就指定了两个键name和title。
(3)Map构造函数接受数组作为参数
例:模式匹配器存值
const items = [
['name', '张三'],
['title', 'Author']
];
const map = new Map();
items.forEach(
([key, value]) => map.set(key, value)
);
实例的属性和操作方法:
(1)size 属性
返回 Map 结构的成员总数
(2)set(key, value)
设置键名key对应的键值为value,然后返回整个 Map 结构。若key已有值,则键值会被更新,否则新生成该键。
(3)get(key)
读取key对应的键值,若找不到key,返回undefined。
(4)has(key)
返回一个布尔值,表示某个键是否在当前 Map 对象之中。
(5)delete(key)
删除某个键,返回true;若删除失败,返回false。
(6)clear()
清除所有成员,没有返回值。