Class类
在ES6中,class (类)作为对象的模板被引入,可以通过 class 关键字定义类。
class 的本质是 function。
它可以看作一个语法糖,让对象原型的写法更加清晰、更像面向对象编程的语法。
// 构造函数
function Person(job,name,age,sex){
this.job = job;
this.name = name;
this.age = age;
this.sex = sex;
}
//增加实例方法
Person.prototype.print = function(){
console.log('工作:',this.job)
console.log('姓名:',this.name)
console.log('年龄:',this.age)
console.log('性别:',this.sex)
}
const a = new Person("程序猿","zhansan",30,"nan");
a.print()
//以前的构造函数的问题
// 1.属性和原型方法分开了,代码可读性差
// 2.原型上的属性也是可以枚举的
for(const prop in a){
console.log(prop) //job name age sex print
}
//3.构造函数也是可以当成普通函数使用的
// ES6中
// 从逻辑上说更合理,成为一个整体
// console.log(Person) //临时性死区
class Person{
constructor(job,name,age,sex){
this.job = job;
this.name = name;
this.age = age;
this.sex = sex;
}
print(){
console.log('工作:',this.job)
console.log('姓名:',this.name)
console.log('年龄:',this.age)
console.log('性别:',this.sex)
}
}
const a = new Person("程序猿","zhansan",30,"nan");
console.log(a)
a.print()
// 1.类的声明不会被提升,和let const 一样,有临时性死区
// 2.类的所有代码全都是在严格模式中执行
// 3.类的所有方法都是不可枚举的
for(const prop in a){
console.log(prop) //job name age sex
}
// 4.类的所有方法都无法当成构造函数直接使用
// const b = new a.print()
// 5.类的构造器必须使用new 来调用
const b = Person("程序猿","zhansan",30,"nan"); //报错
1.可以写成计算属性名
const printName = "print";
const test = "job1"
class Person{
constructor(job,name,age,sex){
this[test] = job;
this.name = name;
this.age = age;
this.sex = sex;
}
[printName](){
console.log('工作:',this[test])
console.log('姓名:',this.name)
console.log('年龄:',this.age)
console.log('性别:',this.sex)
}
}
const a = new Person("程序猿","zhansan",30,"nan");
console.log(a)
a[printName]()
2.可以使用getter 和 setter
getter取值的时候必须走进来 get
setter 设置值得之后必须走进来 set
getter 不可单独出现
getter 与 setter 必须同级出现
get set 控制的属性不在原型上
ES5的写法
Object.defineProperty(this,"age", {
set(age){
},
get(){
}
})
ES6的写法
const printName = "print";
const test = "job1"
class Person{
constructor(job,name,age,sex){
this[test] = job;
this.name = name;
this.age = age;
this.sex = sex;
}
get age(){ //取值的时候走 没有参数
return this._age + "岁";
}
set age(age){ //创建的时候进来,必须接受一个参数
if(typeof age !== "number"){
throw new TypeError("age must a number")
}
if(age < 0){
age = 0
}else if(age > 200){
age = 200
}
this._age = age;
}
[printName](){
console.log('工作:',this[test])
console.log('姓名:',this.name)
console.log('年龄:',this.age)
console.log('性别:',this.sex)
}
}
const a = new Person("程序猿","zhansan",-10,"nan");
console.log(a)
a[printName]()
3.静态成员 static
class Qi{
constructor(name,width,height){
this.name = name;
// this.width = width;
// this.height = height;
}
static width = 50; //添加静态成员
static height = 50;
static method = function(){}
}
const ma = new Qi("马",50,50);
const pao = new Qi("炮",50,50);
console.log(ma.width) //undefined
console.log(pao) //Qi {name: "炮"}
console.log(Qi.width) //50
4.字段初始器(es7)
class Test{
a = 1; //初始化的工作,如果没有使用static,就是实例成员
b = 2;
static c = 3
// constructor(){
// this.a = 1;
// this.b = 2
// }
print = () => { //不会存放在原型上面,会占用一定的存储空间
console.log(this.a)
}
}
const t = new Test()
const g = new Test()
console.log(t) //Test {a: 1, b: 2, print: ƒ}
console.log(t.a,t.b,t.c,Test.c) //1 2 undefined 3
// 1.使用static添加字段初始器,添加的是静态成员
// 2.没有使用,则位于对象上
// 3.箭头函数在字段初始器位置上,指向当前对象
t.print() //1
console.log(t.print == g.print) //false
5.类表达式
const A = class{ //匿名类,类表达式,类在js中本身就是表达式
a = 1;
b = 2;
}
const a = new A();
console.log(a) // A {a: 1, b: 2}
6.类的继承
extends 继承,用于类中的定义
class Child extends Father { ... }
super
子类 constructor 方法中必须有 super ,且必须出现在 this 之前。
1.直接当成函数调用,表示父类的构造
class Animal{
constructor(type,name,age,sex){
this.type = type;
this.name = name;
this.age = age;
this.sex = sex;
}
print(){
console.log(`种类 : ${this.type}`)
console.log(`名字 : ${this.name}`)
console.log(`年龄 : ${this.age}`)
console.log(`性别 : ${this.sex}`)
}
}
class Dog extends Animal{
// 如果定义了constructor 并表示这个是子类,则必须在constructor的第一行手动调用父类的构造函数
// constructor(name,age,sex){
// super ("犬类",name,age,sex)
// }
// 如果说子类不写constructor,则会有默认的构造器,自动去调用父类的构造器
}
const d = new Dog("犬类","旺财",5,"公");
console.log(d);
d.print();
2.super如果说当成对象使用,则表示父类的原型
class Dog extends Animal{
constructor(name,age,sex){
super("犬类",name,age,sex);
this.loves = "吃骨头"
}
print(){ //同名的方法会进行覆盖
// console.log("wang")
//2.super如果说当成对象使用,则表示父类的原型
super.print();
console.log(`爱好 : ${this.loves}`)
}
}
const d = new Dog("旺财",5,"公");
console.log(d);
d.print();
Symbol
ES6 引入了一种新的原始数据类型 Symbol ,表示独一无二的值,最大的用法是用来定义对象的唯一属性名。
ES6 数据类型除了 Number 、 String 、 Boolean 、 Objec t、 null 和 undefined ,还新增了 Symbol 。
创建
const syb1 = Symbol(); //创建了一个符号
const syb2 = Symbol("asdfsdf");
// 就是一个新增数据类型
console.log(syb1,syb2) //Symbol() Symbol(asdfsdf)
console.log(typeof syb1) //symbol
特点
1.没有字面量的写法
2.新的数据类型,typeof返回的是symbol
3.每次去调用Symbol函数得到的符号永远不会相等,不管符号描述是否相同
const syb1 = Symbol("abc");
const syb2 = Symbol("abc");
console.log(syb1,syb2) //Symbol(abc) Symbol(abc)
console.log(syb1 === syb2) //false
4.符号可以作为对象的属性名使用,这种属性名叫符号属性
const syb1 = Symbol("abc");
const obj = {
a : 1,
b : 2,
[syb1] : 3 //符号属性
}
console.log(obj)
5.可以通过设计,让外面无法访问到
const Hero = (() =>{
const getRandom = Symbol() //计算属性名
return class{
constructor(attack,hp,defence){
this.attack = attack;
this.hp = hp;
this.defence;
}
gongji(){
const dmg = this.attack * this.getRandom(0.7,1.2)
}
[getRandom](min,max){
return Math.random() * (max - min) + min
}
}
})()
const h = new Hero(3,100,3)
console.log(h)
6.符号属性不能被枚举
const syb1 = Symbol("abc");
const obj = {
a : 1,
b : 2,
[syb1] : 3 //符号属性
}
console.log(obj) //["a", "b"]
for(const prop in obj){
console.log(prop) //["a", "b"]
}
console.log(Object.keys(obj)) //es5获取属性名的方法也不行
console.log(Object.getOwnPropertyNames(obj)) //ES6方法也不能获取属性名
// 针对符号属性
// getOwnPropertySymbols(obj)
// console.log(Object.getOwnPropertySymbols(obj)) [Symbol(abc)]
const sybs = Object.getOwnPropertySymbols(obj)[0];
console.log(sybs == syb1) //true
7.符号类型无法被隐式转换,数学运算,字符串拼接都是不行的
可以进行内部的显式转换 console.log的输出
const syb = Symbol();
console.log(syb + 10)
Symbol.for()
共享符号
Symbol.for(“符号描述”) 如果符号描述相等,则可以得到同一个符号
const syb1 = Symbol.for("abc");
const syb2 = Symbol.for("abc");
console.log(syb1,syb2) //Symbol(abc) Symbol(abc)
console.log(syb1 === syb2) //true
const obj = {
a : 1,
b : 2,
[Symbol.for("c")] : 3
}
console.log(obj) //{a: 1, b: 2, Symbol(c): 3}
console.log(obj[Symbol.for("c")]) //3
特殊含义的共享符号 通过Symbol的配置得到的
JavaScript 松散,写法不严谨
必须去解决这些严谨性的问题
配置底层的实现原理
1.Symbol.hasInstance
用于判断某对象是否为某构造器的实例。 因此你可以用它自定义instanceof操作符在某个类上的行为。
function A(){}
// const obj = new A();
// console.log(obj instanceof A) // true
// console.log(A[Symbol.hasInstance](obj)); // true 不能这种方式配置
Object.defineProperty(A,Symbol.hasInstance,{
value : function(obj){
console.log(obj);
return false;
}
})
const obj = new A();
console.log(obj instanceof A) // false 可以去改变instanceof 的值
2.Symbol.isConcatSpreadable
用于配置某对象作为Array.prototype.concat()方法的参数时是否展开其数组元素。
会对数组的方法产生影响
对于数组对象,默认情况下,用于concat时,会按数组元素展开然后进行连接(数组元素作为新数组的元素)重置Symbol.isConcatSpreadable可改变默认行为。
对于类似数组的对象,用于concat时,该对象整体作为新数组的元素,重置Symbol.isConcatSpreadable可改变默认行为。
// 设置Symbol.isConcatSpreadable为false
const arr1 = [3];
const arr2 = [4,5,6,7]
arr2[Symbol.isConcatSpreadable] = false;
const resul = arr1.concat(99,arr2) //对两个数组拆分链接成新的数组
console.log(resul) // [3, 99, Array(4)]
// 默认情况下
const arr = [1];
const obj = {
0 : 3,
1 : 5,
length : 2,
[Symbol.isConcatSpreadable] : true
}
const result = arr.concat(99,obj)
console.log(result) //[1, 99, 3, 5]
3.Symbol.toPrimitive
作为对象的函数值属性存在的,当一个对象转换为对应的原始值时,会调用此函数。
const obj = {
a : 1,
b : 2
}
obj[Symbol.toPrimitive] = function(){
return 123
}
console.log(obj + 123) //246
4.Symbol.toStringTag
该Symbol.toStringTag公知的符号是在创建对象的默认字符串描述中使用的字符串值属性。它由该Object.prototype.toString()方法在内部访问。 可以影响Object.prototype.toString的返回值
class Person{
[Symbol.toStringTag] = "Person"
}
const p = new Person()
console.log(p)
const arr = [12,1,2]
console.log(Object.prototype.toString.call(p)) //[object Person]