面向对象思想类:定义某一事物的抽象特点,包含属性和方法,举个栗子,狗这个类包含狗的一些基础特征,如毛皮颜色,吠叫等能力。 对象:类的一个实例,还是举个栗子,小明家的白色的狗和小红家红色的狗。 属性:对象的特征,比如刚提到的狗皮毛的颜色。 方法:对象的行为,比如刚才提到的狗的吠叫能力。 封装性:通过限制只有特定类的对象可以访问特定类的成员,一般包含public protected private 三种,不同语言的实现不同。 继承性:一个类会有子类,这个子类是更具体化的一个抽象,它包含父类的一些属性和方法,并且有可能有不同于父类的属性和方法。 多态性:多意为‘许多’,态意为‘形态’。不同类可以定义相同的方法或属性。 抽象性:复杂现实问题转化为类定义的途径,包括以上所有内容。
如何实现对象(类)的定义JS中并没有类(class)的概念,更多的时候我们称之为对象(function),然后把对象叫做实例(instance)。如下,Person 类型为function class Person {
// ...
sayHi() {
console.log('Hi')
}
}
typeof Person // 'function'
Person === Person.prototype.constructor // 'true'
let p1 = new Person();
p1.sayHi() // Hi上列代码表明,类的数据类型就是函数,类本身就是指向构造函数。 使用的时候,也是直接对类使用new命令,跟构造函数的用法完全一致。 类的由来在js里面,我们通常都是通过构造函数来创建对象(class),然后通过new这个关键字来实例化一个对象,如: ES5中的方法:通过构造函数实现 function Person(name,age) {
this.name = name;
this.age = age;
}
// Person.prototype.showName = function () {
// return `我的名字为:${this.name}`
// }
// Person.prototype.showAge = function () {
// return `我的年龄为:${this.age}`
// }
// 通过assgin方法可以优化代码写入
Object.assign(Person.prototype,{
showName() {
return `我的名字为:${this.name}`
},
showAge() {
return `我的年龄为:${this.age}`
}
})
let p1 = new Person('Jing',18)
console.log(p1.showName()); // 我的名字为:Jing
console.log(p1.showAge()); // 我的年龄为:18
上面这种写法跟传统的面向对象语言(比如 C++ 和 Java)差异很大,很容易让新学习这门语言的程序员感到困惑。 ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。 基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用 ES6 的class改写,就是下面这样。 构造函数的prototype属性 构造函数的prototype属性,在 ES6 的“类”上面继续存在。事实上,类的所有方法都定义在类的prototype属性上面。 例: class Person {
constructor() {
}
showName() {
// ...
}
showAge() {
// ...
}
}
// 等同于
Person.prototype = {
constructor() {},
showName() {},
showAge() {},
}在类的实例上调用方法其实就是调用原型上的方法
ES6中的变形:方法的传值class Person {
showName(name) {
return `我的名字为:${name}`
}
}
let p1 = new Person();
console.log(p1.showName('Jing'))
类的传参实例: class Person {
constructor(name,age) {
this.name = name;
this.age = age;
}
showName() {
return `我的名字为:${this.name}`
}
showAge() {
return `我的年龄为:${this.age}`
}
}
let p1 = new Person('Jing',18)
console.log(p1.showName()); // 我的名字为:Jing
console.log(p1.showAge()); // 我的年龄为:18constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。如下: class Person {
}
// 等同于
class Person {
constructor () {}
}
表达式形式 let Person = class {
constructor(name,age) {
this.name = name;
this.age = age;
}
showName() {
return `我的名字为:${this.name}`
}
showAge() {
return `我的年龄为:${this.age}`
}
}
let p1 = new Person('Jing',18)
console.log(p1.showName()); // 我的名字为:Jing
console.log(p1.showAge()); // 我的年龄为:18 let Person = new class {
constructor(name,age) {
this.name = name;
this.age = age;
}
showName() {
return `我的名字为:${this.name}`
}
}('Jing')
console.log(Person.showName()) // Jing属性表达式: 类的属性名可以采用表达式let methodName = 'showName';
class Person {
constructor (name) {
this.name = name;
}
[methodName]() {
return `我的名字为:${this.name}`
}
}
let p1 = new Person('Jing')
console.log(p1.showName()); // 我的名字为:Jing上列代码中Person类的方法名showName是从表达式得到的。 // 字符串拼接形式
let aa = 'show';
let bb = 'Name';
class Person {
constructor (name) {
this.name = name;
}
[aa+bb]() {
return `我的名字为:${this.name}`
}
}
let p1 = new Person('Jing')
console.log(p1.showName()); // 我的名字为:Jingclass取值函数,存值函数 class Person { constructor() { } get aaa() { return `aaa的属性` } set aaa(val) { console.log(`设置aaa属性,值为:${val}`); } } let p1 = new Person(); p1.aaa = '123' p1.aaa //设置aaa属性,值为:123
注意点严格模式类和模块的内部,默认就是严格模式,所以不需要使用use strict指定运行模式。只要你的代码写在类或模块之中,就只有严格模式可用。考虑到未来所有的代码,其实都是运行在模块之中,所以 ES6 实际上把整个语言升级到了严格模式。 不存在变量提升 let p1 = new Person() console.log(p1); class Person { constructor (){ this.name = 'Jing' } } // 报错显示初始化前无法访问Person(即Person类没有创建)静态方法静态方法 类身上的方法 在方法名前加上static,可以直接用Person类调用 类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。 class Person { constructor() { } showName() { return `这是showName方法` } static aaa() { return `这是静态方法` } } let p1 = new Person(); console.log(p1.showName()); //这是showName方法 console.log(Person.aaa()); // 这是静态方法实例属性新写法实例属性除了定义在constructor()方法里面的this上面,也可以定义在类的最顶层。 class IncreasingCounter { constructor() { this._count = 0; } get value() { console.log('Getting the current value!'); return this._count; } increment() { this._count++; }}上面代码中,实例属性this._count定义在constructor()方法里面。另一种写法是,这个属性也可以定义在类的最顶层,其他都不变。 class IncreasingCounter { _count = 0; get value() { console.log('Getting the current value!'); return this._count; } increment() { this._count++; }}上面代码中,实例属性_count与取值函数value()和increment()方法,处于同一个层级。这时,不需要在实例属性前面加上this。 这种新写法的好处是,所有实例对象自身的属性都定义在类的头部,看上去比较整齐,一眼就能看出这个类有哪些实例属性。 类的继承 class Person { constructor(name) { this.name = name; } showName() { return `我的名字为${this.name}` } } // 子类 class Student extends Person{ } // 调用 let stu1 = new Student('Jing') console.log(stu1.showName());super class Person { constructor(name) { this.name = name; } showName() { return `我的名字为${this.name}` } } // 子类 class Student extends Person{ constructor(name,skill) { super(name) this.skill = skill } } // 调用 let stu1 = new Student('Jing','学习') console.log(stu1.skill);
|