JavaScript Class类实例讲解
亦世凡华、 人气:0Class类
ES6提供了更接近传统语言的写法,引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。基本上,ES6的class可以看作只是一个语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰,更像面向对象编程的语法。
初识class
之前ES5通过构造函数实现实例化的方法
<script> // 定义人类 function People(name,age){ this.name = name this.age = age } // 添加方法 People.prototype.say = function (){ console.log('hello world'); } // 实例化方法 let person = new People('张三',18) person.say()//hello world console.log(person);//People {name: '张三', age: 18} </script>
ES6 class方法实现
constructor()方法是类的默认方法,通过 new 命令生成对象实例时,自动调用该方法,一个类必须有constructor()方法,如果没有显示定义,一个空的constructor()方法会被默认添加。
<script> // class class People { // 构造方法 名字是固定格式不能修改 constructor(name,age){ this.name = name this.age = age } // 方法必须使用该语法,不能使用 ES5 的对象完整形式 say(){ console.log('hello world'); } } // 实例化对象 let person = new People('张三',18) person.say()//hello world console.log(person);//People {name: '张三', age: 18} </script>
class中getter和setter设置
在ES6中,类的内部可以使用 getter (取值函数) 和 setter (存值函数) 关键字,即 get 和 set ,对某个属性设置取值函数和存值函数,拦截该函数的存取行为。
<script> class People { get name(){ console.log('我是张三'); return '这是我的名字'//如果不写return,默认是undefined } set name(Name){//形参必须有 console.log('我的名字被修改了'); } } // 实例化对象 let p = new People() // 只要读取 p 的实例化属性,就会执行 get 关键字函数里面代码,而且这个函数的返回值就是属性的一个值 console.log(p.name); // 只要对 name 属性进行一个修改,如果有set关键字函数,就会执行该函数 p.name = 'f' </script>
表达式方式书写
和函数一样,类可以用表达式定义书写,需要注意的是:定义的类名只能在Class内部使用,指代当前类,在Class外部,类只能用自己定义等于类的常量。
<script> const myClass = class Me { getClass(){ return Me.name//返回类名 } } let c = new myClass() console.log(c.getClass())//Me Me.name//Me is not defined // 如果类的内部没用到的话,可以省略Me,也就是可以写成下面的形式 const MyClass = class {}; </script>
静态属性与静态方法
静态属性是指 Class 本身的属性,即 Class.propName,而不是定义在实例对象 this 上的属性。
实例对象和函数对象的属性是不相通的,实例对象的属性和构造函数的原型对象相通,实例对象只能继承构造函数原型中的属性和方法。
<script> function People(){ } // 函数属性和方法 People.name = '张三' People.say = function(){ console.log('hello world'); } // 原型对象属性和方法 People.prototype.age = 18 // 实例化对象 let p = new People() console.log(People.name,People.say()); console.log(p.age); console.log(p.name,p.say()); </script>
以class方法展示,因为ES6明确规定,Class内部只有静态方法,没有静态属性,而要想得到设置静态属性,需要在实例属性前面加上 static 关键字;静态方法也要加上 static 关键字,表示该方法不会被实例继承,而是直接通过类来调用。
<script> class People { // 静态属性 static name = '张三' static say(){ console.log('hello world'); } } let p = new People() console.log(p.name);//undefined console.log(People.name);//张三 </script>
私有属性和私有方法
常见需求:私有属性和方法,是只能在类内部访问的属性和方法,外部不能访问,有利于代码的封装。
ES6中正式为class添加了私有属性和方法,方法是在属性和方法名之前使用 # 表示,如果不带 # ,会被当作另一个属性和方法。
<script> class person { // 私有属性 #name constructor(name){ this.#name = name } // 私有方法 #sayName(){ return this.#name } result(){ console.log(this.#name); } } let p = new person() p.result = '张三' console.log(p);//person {result: '张三', #sayName: ƒ, #name: undefined} p.#name//报错 </script>
当我们想判断某个类的私有属性是否存在时,我们可以用 in 运算符进行判断。
<script> class A { #foo = 0; m() { console.log(#foo in this); // true console.log(#bar in this); // Private field '#bar' must be declared in an enclosing class(提示我们:私有字段“#bar”必须在封闭类中声明) } } let a = new A() a.m() </script>
class继承
构造函数实现继承
通过原型链进行继承,如果有不熟悉原型链的朋友,可以看一下我之前的文章:原型和原型链
<script> // 动物 function Animals(name,age){ this.name = name this.age = age } Animals.prototype.call = function(){ console.log('我是动物'); } // 狗 function Dog(name,age,color,gender){ // 改变this的指向,继承父类 Animals.call(this,name,age) this.color = color this.gender = gender } // 设置子类构造函数的原型 Dog.prototype = new Animals //此时子类的实例对象就会继承父类上面的方法 Dog.prototype.constructor = Dog // 声明子类的方法 Dog.prototype.say = function(){ console.log('汪汪汪!!!'); } // 子类对象进行实例化 const d = new Dog('小明',3,'棕色','雄') console.log(d); </script>
class类实现继承
class可以通过 extends 关键字实现继承,让子类继承父类属性和方法,可以看出 extends 的写法比上文 原型链继承 清晰方便的多。
<script> class Animals { // 构造方法 constructor(name,age){ this.name = name this.age = age } // 父类成员的属性 call(){ console.log('我是动物'); } } class Dog extends Animals { // 构造方法 constructor(name,age,color,gender){ // 调用父类方法,需要用super(),super()就是父类的constructor()方法 super(name,age) this.color = color this.gender = gender } // 子类独有的方法 say(){ console.log('汪汪汪!!!'); } // 当子类和父类重名时,优先调用的是子类的方法 call(){ console.log('我也是动物'); // 如果想调用父类方法,使用如下语句:super.父类方法() super.call() } } // 实例化子类对象 const d = new Dog('小明',3,'棕色','雄') console.log(d); d.call() d.say() </script>
super 关键字
上面代码用到 super 这个关键字,这里简单说明一下:子类继承父类的 constructor() 构造函数中必须要有 super(),代表调用父类的构造函数,没有就会报错,super虽然代表父类的构造函数,但是返回的是子类的实例,即super内部的this指的是子类的实例。作为函数时,super() 只能用在子类的构造函数中,用在其他地方就会报错。
判断继承是否存在
Object.getPrototypeOf()方法可以用来从子类上获取父类,所以可以用来判断一个类是否继承另一个类。
<script> class people {} class boy extends people {} console.log(Object.getPrototypeOf(boy) === people);//true </script>
静态属性和方法继承
父类的静态属性和方法也能被子类继续,如下:
<script> class people { // 父类静态属性 属性为数值 static age = 18 // 父类静态属性 属性为对象 static h = {height:180} // 父类静态方法 static say(){ console.log('hello world'); } } // 子类继承父类 class boy extends people { constructor(){ // 调用父类的构造函数 super() // boy类继承静态属性时,会采用浅拷贝,拷贝父类静态属性的值,因此people.age和boy.age是两个彼此独立的属性。 boy.age-- // 如果父类的静态属性的值是一个对象,那么子类的静态属性也会指向这个对象,因为浅拷贝只会拷贝对象的内存地址所以,子类修改这个对象的属性值,会影响到父类。 boy.h.height-- } } // 实例化子类 let b = new boy() boy.say() console.log(people.age); console.log(boy.age); console.log(boy.h.height); console.log(people.h.height); console.log(b); </script>
私有属性和方法继承
私有属性和方法只能定义在它本身的class里面使用,所以子类会继承父类所有的属性和方法除了私有属性和方法,那么如何让子类访问到父类中的私有属性和方法呢?如果父类定义了私有属性的读写方法,子类就可以通过这些方法,读取私有属性。
<script> class people { #name = '张三' // 定义用来读取私有属性和方法的函数 getName(){ return this.#name } } class boy extends people { constructor(){ // 调用父类的构造函数 super() console.log(this.getName());//张三 } } let b = new boy() </script>
class显示原型与隐式原型关系
每个对象都有隐式原型 __proto__ 属性,指向对应的构造函数的显示原型 prototype 属性,class作为构造函数的语法糖,同时也具有 prototype 属性和 __proto__ 属性,所以存在两条继承链。当然这里这做一个了解。
<script> class people {} class boy extends people{} // 子类的__proto__属性,表示构造函数的继承,总是指向父类。 console.log(boy.__proto__ === people); // true // 子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。 console.log(boy.prototype.__proto__ === people.prototype); // true </script>
加载全部内容