A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 小江哥 黑马粉丝团   /  2019-6-17 18:33  /  765 人查看  /  0 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

面向对象思想
什么是面向对象编程(Object Oriented Programming| OOP)?
  • 类:定义某一事物的抽象特点,包含属性和方法,举个栗子,狗这个类包含狗的一些基础特征,如毛皮颜色,吠叫等能力。
  • 对象:类的一个实例,还是举个栗子,小明家的白色的狗和小红家红色的狗。
  • 属性:对象的特征,比如刚提到的狗皮毛的颜色。
  • 方法:对象的行为,比如刚才提到的狗的吠叫能力。
  • 封装性:通过限制只有特定类的对象可以访问特定类的成员,一般包含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()); // 我的年龄为:18
constructor方法是类的默认方法,通过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
  • 以上两种形式结果相同。
  • 使用Class表达式,可以写出立即执行的Class

  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);

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马