目录
类
类是对象的模版,定义了同一组对象共有的属性和方法
ES5中的类与继承
- 定义类
ES5其实并没有类的概念,是通过function 构造函数来模拟一个类。在构造函数中,通常会将不变的方法直接定义在prototype对象上,这样所有对象实例就可以共享这些方法,节省内存。
// 类
function People(name, age) {
this.name = name;
this.age = age;
}
People.prototype.showName = function () {
console.log("我的名字是" + this.name);
};
let p1 = new People("zzz", 18);
console.log(p1);
p1.showName();
let p2 = new People("小明", 20);
console.log(p2);
p2.showName();
- 定义静态属性
静态属性就是直接在定义在类上的属性,使用时直接通过类调用它
// 类
function People(name, age) {
this.name = name;
this.age = age;
People.count++;
}
People.count = 0;
let p1 = new People("zzz", 18);
let p2 = new People("小明", 20);
console.log(People.count) // 2
- 静态方法
静态方法跟静态属性一样,也是定义在类上,使用时直接通过类调用它。在静态方法中,this指向构造函数,所以无法通过this获取实例属性
People.getCount = function () {
console.log(this); // 指向构造函数
console.log("当前共有" + People.count + "个人");
};
People.getCount();
- 继承
①构造函数继承,可以继承构造函数里面的方法和属性,但是不继承原型链
// 父类
function Animal(name) {
this.name = name;
}
Animal.prototype.showName = function () {
console.log("名字是" + this.name);
};
// 子类
function Dog(name, color) {
Animal.call(this, name); //继承属性
this.color = color;
}
let d1 = new Dog("wangcai", "white");
console.log(d1); //Dog { name: 'wangcai', color: 'white' }
②原型链继承,只能继承原型链上的方法,无法继承构造函数里面的方法和属性
将子类的prototype对象指向父类的实例化对象,并且当前Dog原型的构造函数需要再指回到Dog上。
// 父类
function Animal(name) {
this.name = name;
}
Animal.prototype.showName = function () {
console.log("名字是" + this.name);
};
// 子类
function Dog(name, color) {
// Animal.call(this, name); //继承属性
this.color = color;
}
Dog.prototype = new Animal(); // Animail.prototye
Dog.prototype.constructor = Dog;
let d1 = new Dog("wangcai", "white");
console.log(d1); //Dog { color: 'white' }
d1.showName(); //名字是undefined
③组合式继承,就是将构造函数继承和原型链继承一起使用,既可以继承构造函数里面的方法和属性,又可以继承原型链上的方法。
// 父类
function Animal(name) {
this.name = name;
Animal.count++;
}
Animal.prototype.showName = function () {
console.log("名字是" + this.name);
};
// 子类
function Dog(name, color) {
Animal.call(this, name); //继承属性
this.color = color;
}
Dog.prototype = new Animal(); // Animail.prototye
Dog.prototype.constructor = Dog;
let d1 = new Dog("wangcai", "white");
console.log(d1); //Dog { name: 'wangcai', color: 'white' }
d1.showName(); //名字是wangcai
ES6中的类与继承
- 类的定义
在ES6中提供了一个关键字Class
可以用来声明一个类,在类中有constructor
构造方法可以初始化属性,实例方法直接写在class里
class People {
constructor(name, age) {
this.name = name;
this.age = age;
}
showName() {
console.log(this.name);
}
}
let p1 = new People("zzz", 18);
p1.showName();
- 继承
在ES6中可以使用extends
和super
关键字实现对类的继承,其中super
是用来在constructor
中继承父类的属性
class Coder extends People {
constructor(name, age, company) {
super(name, age); // 继承父类的name和age属性
this.company = company;
}
showCompany() {
console.log(this.company);
}
}
let c1 = new Coder("小明", 18, "路人甲公司");
console.log(c1);
c1.showName();
c1.showCompany();
- 访问器
在类中可以使用 get xxx
或者set xxx
创建访问器方法,用来访问/返回值或者设置某一个属性的值。
class Coder extends People {
constructor(name, age, company) {
super(name, age); // 继承父类的name和age属性
this.company = company;
}
get sex() {
return "female";
}
showCompany() {
console.log(this.company);
}
}
let c1 = new Coder("小明", 18, "路人甲公司");
console.log(c1.sex); // female
如果使用get
创建了属性,那么直接修改这个属性的值是无效的,还需要定义set
方法来修改,在使用set
时需要创建一个额外的属性来存储属性值,否则每次使用set修改属性会一直死循环set
方法。
class Coder extends People {
constructor(name, age, company) {
super(name, age); // 继承父类的name和age属性
this.company = company;
this._sex = -1;
}
get sex() {
return this._sex;
}
set sex(val) {
this._sex = val;
}
showCompany() {
console.log(this.company);
}
}
let c1 = new Coder("小明", 18, "路人甲公司");
c1.sex = "male";
console.log(c1.sex); // male
- 静态方法
在ES6中使用static
关键字定义静态方法,使用时直接通过类调用,子类可以继承父类的静态方法。
//在父类定义静态方法
class People {
constructor(name, age) {
this.name = name;
this.age = age;
}
static getCount() {
return 6;
}
showName() {
console.log(this.name);
}
}
console.log(People.getCount()); // 6
console.log(Coder.getCount()); // 6
- 静态属性
ES6明确规定,class
内部只能定义静态方法,不能定义静态属性。可以同ES5方式一样通过类名.xxx
定义静态属性,因为类本质是构造函数的语法糖
People.count = 9;
console.log(typeof People); // function
console.log(People.count);
新的原始数据类型
ES5的原始数据类型有 undefined
、null
、number
、string
、boolean
、和引用类型Object
。ES6新增了一种新的原始数据类型Symbol
。Symbol
在ES6表示独一无二
的意思
- 声明方式
①无描述值
,即使创建的2个Symbol都无描述值,它们依然不相等
let s1 = Symbol();
let s2 = Symbol();
console.log(s1); // Symbol()
console.log(s2); // Symbol()
console.log(s1 === s2); // false
//打印描述值
console.log(s1.description); // undefined
②有描述值
,把字符串作为参数传到Symbol当描述
let s1 = Symbol("s1");
let s2 = Symbol("s2");
console.log(s1); // Symbol(s1)
console.log(s2); // Symbol(s2)
console.log(s1 === s2); //false
//打印描述值
console.log(s1.description); // s1
③如果传入的参数是一个对象,Symbol会自动调佣对象的toString
方法
const obj1 = {
name: "obj1",
};
const obj2 = {
name: "obj2",
toString() {
return this.name;
},
};
let s1 = Symbol(obj1);
let s2 = Symbol(obj2);
console.log(s1); // Symbol([object Object])
console.log(s2); // Symbol(obj2)
④Symbol.for()
,通过这种方式创建的Symbol值,相当于在全局环境中定义了,每次创建时都会在全局查找是否有相同的描述值的Symbol。而Symbol()每次都会创建一个新值。
let s1 = Symbol.for("foo");
let s2 = Symbol.for("foo");
console.log(s1 === s2); // true
function bar(){
return Symbol.for('bar')
}
const x = bar()
const y = Symbol.for('bar')
console.log(x === y) // true
Symbol.keyFor
方法返回一个已经登记的Symbol类型值的key。
let s1 = Symbol("foo");
let s2 = Symbol.for("foo");
console.log(Symbol.keyFor(s1)); // undefined
console.log(Symbol.keyFor(s2)); // foo
- 应用场景
①利用Symbol来作为对象的属性名
如果 一个年级里有2个同名的同学,那么不使用Symbol的情况下无法进行区分,前一个同学的信息被后一个同学覆盖。
const grade = {
李四: { address: "zzz", tel: "111" },
李四: { address: "yyy", tel: "222" },
};
console.log(grade); // { '李四': { address: 'yyy', tel: '222' } }
利用Symbol
来作为对象的属性名
,可以进行区分,因为它形成的是独一无二
的key,无法被覆盖。
const stu1 = Symbol("李四");
const stu2 = Symbol("李四");
const grade = {
[stu1]: { address: "zzz", tel: "111" },
[stu2]: { address: "yyy", tel: "222" },
};
console.log(grade);
console.log(grade[stu1]);
console.log(grade[stu2]);
②定义某个类的私有属性或方法
定义一个登录类,通过Symbol
将密码设为类似私有属性
的属性,不允许对外访问。
// a文件
const password = Symbol('pwd');
class Login {
constructor(username, pwd) {
this.username = username;
this[password] = pwd;
}
checkPassword(pwd) {
console.log(this[password]);
return this[password] === pwd;
}
}
export default Login;
// b文件
const login = new Login("admin", "123");
console.log(login.checkPassword("123"));
login.password // 无法访问
login[password] // 无法访问
login["password"] // 无法访问
for (let key in login) {
// 只能取到普通属性
console.log(key); // username
}
for (let key of Object.keys(login)) {
// 只能取到普通属性
console.log(key); // username
}
for (let key of Object.getOwnPropertySymbols(login)) {
// 只能取到Symbol属性
console.log(key); // Symbol(pwd)
}
for (let key of Reflect.ownKeys(login)) {
// 既取到普通属性又能取到Symbol属性
console.log(key); // 输出username和Symbol(pwd)
}
- 消除魔术字符串
魔术字符串
指的是,在代码之中多次出现
、与代码形成强耦合的某一个具体的字符串或者数值。风格良好的代码,应该尽量消除魔术字符串,改由含义清晰的变量替代。
const shapeType = {
triangle: Symbol(),
circle: Symbol(),
};
function getArea(shape) {
let area = 0;
switch (shape) {
case shapeType.triangle:
area = 1;
break;
case shapeType.circle:
area = 2;
break;
}
return area;
}
console.log(getArea(shapeType.triangle)); // 1
新的数据结构Set
Set
是一组唯一值
的集合
,每个值只能在Set
中出现一次,Set
可以容纳任何数据类型的值
- 常用方法
①使用 new 关键字和 Set 构造函数可以创建一个集合:
let s = new Set([1, 2, 3, 2, "2"]);
console.log(s); // Set(4) { 1, 2, 3, '2' }
②add(): 添加元素
s.add("4").add("zzz");
③has(): 查询Set实例是否存在某元素(返回布尔值)
console.log(s.has("zzz")); // true
④delete(): 删除Set实例中某个元素(返回布尔值)
s.delete("zzz");
⑤clear(): 清空Set实例
s.clear();
console.log(s); // Set(0) {}
⑥size: 获取Set实例的元素个数
console.log(s.size)
⑦Set实例转数组
通过扩展运算符
或者Array.from
可以将Set实例转数组
let s = new Set([1, 2, 3, 4]);
console.log([...s]); // 转换为数组
console.log(Array.from(s)); // 转换为数组
- 遍历
通过遍历可知,Set
这种数据结构,它的key和value都是完全一样的值。
s.forEach((item) => console.log(item));
for (let item of s) {
console.log(item);
}
for (let item of s.keys()) {
console.log(item);
}
for (let item of s.values()) {
console.log(item);
}
for (let item of s.entries()) {
console.log(item);
}
- 应用场景
①数组去重
// 数组去重
let arr = [1, 2, 3, 4, 2, 3];
let s = new Set(arr);
console.log(s);
②合并去重
//合并去重
let arr1 = [1, 2, 3, 4];
let arr2 = [2, 3, 4, 5, 6];
let s = new Set([...arr1, ...arr2]);
console.log(s); // Set(6) { 1, 2, 3, 4, 5, 6 }
③交集
//交集
let arr1 = [1, 2, 3, 4];
let arr2 = [2, 3, 4, 5, 6];
let s1 = new Set(arr1);
let s2 = new Set(arr2);
let result = new Set(arr1.filter((item) => s2.has(item)));
console.log(result); // Set(3) { 2, 3, 4 }
④差集
// 差集
let arr1 = [1, 2, 3, 4];
let arr2 = [2, 3, 4, 5, 6];
let s1 = new Set(arr1);
let s2 = new Set(arr2);
let arr3 = new Set(arr1.filter((item) => !s2.has(item)));
let arr4 = new Set(arr2.filter((item) => !s1.has(item)));
console.log([...arr3, ...arr4]); // [ 1, 5, 6 ]
- WeakSet
WeakSet的使用其实和Set比较类似,他们的区别主要有两个:
- WeakSet的成员只能是对象,而不是能是别的类型的值
- WeakSet的对象都是弱引用,WeakSet没有
size
属性,不能遍历
const ws = new WeakSet();
const obj1 = {
name: "zzz",
};
const obj2 = {
name: "xxx",
};
ws.add(obj1);
ws.has(obj2);
//ws.delete(obj1);
WeakSet中的对象都是弱引用
:WeakSet 中对对象的引用不会被考虑进垃圾回收机制
,这些值不属于正式的引用,不会阻止垃圾回收,即只要没有其他的对象引用该对象,则该对象就会被回收,而不管它在不在 WeakSet。
因此, WeakSet 适用于临时存放一组对象,以及存放和对象绑定的信息,只要这些对象在外部消失,那么它在 WeakSet 里面的引用也会自动消失。
Map
Map是ECMAScript 6 的新增特性,是一种新的集合类型,为javascript带来了真正的键/值存储机制。
①Map 对象存有键值对,其中的键可以是任何数据类型。
②Map 对象记得键的原始插入顺序。
③Map 对象具有表示映射大小的属性。
- 常用方法
①使用 new 关键字和 Map 构造函数创建一个映射
let m1 = new Map();
//如果想在创建的同时初始化实例,可以给 Map 构造函数传入一个可迭代对象,需要包含键/值对数组。
//可迭代对象中的每个键/值对都会按照迭代顺序插入到新映射实例中
let m2 = new Map([["key1", "val1"]]);
②set():添加键/值对
let m = new Map([["key1", "val1"]]);
let obj = {
name: "zzz",
};
m.set(obj, "es");
console.log(m); // Map(2) { 'key1' => 'val1', { name: 'zzz' } => 'es' }
③get():获取 Map 对象中键的值
console.log(m.get(obj)); // es
③has():查询键存在于 Map 中,返回 布尔值。
console.log(m.has("key1")); // true
console.log(m.has("key2")); // false
④delete():删除一个键/值对
m.delete("key1");
console.log(m); // Map(1) { { name: 'zzz' } => 'es' }
⑤clear():清除这个映射实例中的所有键/值对
console.log(m); // Map(0) {}
⑥size属性:返回 Map 元素的数量。
console.log(m.size) // 0
- 遍历
let map = new Map([
["key1", "val1"],
["key2", "val2"],
["key3", "val3"],
]);
map.forEach((value, key) => console.log(value, key));
for (let [key, value] of map) {
console.log(key, value);
}
for (let key of map.keys()) {
console.log(key);
}
for (let value of map.values()) {
console.log(value);
}
for (let [key, value] of map.entries()) {
console.log(key, value);
}
- 应用场景
Map 的大多数特性都可以通过 Object 类型实现,但二者之间还是存在一些细微的差异。
使用 Map
:
①储存的键不是字符串/数字/或者 Symbol
时,选择 Map
,因为 Object
并不支持
②储存大量的数据时,选择 Map
,因为它占用的内存更小
③需要进行许多新增/删除元素的操作时,选择 Map
,因为速度更快
④需要保持插入时的顺序的话,选择 Map
,因为 Object
会改变排序
⑤需要迭代/遍历的话,选择 Map
,因为它默认是可迭代对象,迭代更为便捷
使用 Object
:
①只是简单的数据结构时,选择 Object
,因为它在数据少的时候占用内存更少,且新建时更为高效
②需要用到 JSON
进行文件传输时,选择 Object
,因为 JSON
不默认支持 Map
③需要对多个键值进行运算时,选择 Object
,因为句法更为简洁
④需要覆盖原型上的键时,选择 Object
虽然 Map
在很多情况下会比 Object
更为高效,不过 Object
永远是 JS
中最基本的引用类型,它的作用也不仅仅是为了储存键值对。
- WeakMap
WeakMap
是一种弱映射
的集合类型,它与Map最大的不同在于,WeakMap的键只能是引用数据类型。
let wm = new WeakMap();
wm.set([1], 2);
wm.set(
{
name: "zzz",
},
"es"
);
console.log(wm);
WeakMap的常见方法有四个:set()
、get()
、delete()
、has()
WeakMap没有size
属性,不仅不能使用clear()
也不能进行遍历
WeakMap的key对对象的引用是弱引用,如果没有其他引用引用这个对象,那么垃圾回收机制(GC)可以将这个对象回收。
字符串的扩展
- 字符的Unicode表示法
在ES6中可以采用\uxxxx
的形式表示一个字符,其中xxxx
表示字符的 Unicode 码点,码点范围为0000~ffff
。
例:
标签:11,ES6,console,log,Symbol,let,new,ES,name From: https://www.cnblogs.com/Small-Windmill/p/17527419.html