JS面向对象实现

博客内容仅是个人实践理解,经验不足,如有错误,敬请指正!

面向对象的抽象表示

面向对象的抽象是指以代码形式抽象表示现实世界中任意对象的方方面面。

一:ES5面向对象基本语法

function Animal(name) {
    
    
	// 实例属性
    this.name = name+'_animal_tag'
}
// 静态(类)属性/方法:Animal函数对象(类)可以访问定义在该对象上的属性,Animal实例无法访问
Animal.num = 42
Animal.getNum = function () {
    
    
    return Animal.num
}
// 实例方法:Animal实例可以访问Animal原型上的方法(属性搜索遵从原型链机制)
Animal.prototype.getName = function () {
    
    
    return this.name
}
Animal.prototype.setName = function (name) {
    
    
    this.name = name
}
类实例

在这里插入图片描述

二:ES6形式面向对象基本语法

class AnimalES6{
    
    
    constructor(name) {
    
    
    	// 实例属性
        this.name = name+'_animal_tag'
    }
    // 静态方法(类方法)
    static getNum(){
    
    
        return AnimalES6.num
    }
    // 实例方法
    getName(){
    
    
        return this.name
    }
    setName(name){
    
    
        this.name = name
    }
}
// 静态属性(类属性)
AnimalES6.num = 42
类实例

在这里插入图片描述

  • 对比ES5类实例结构,可见二者组成以及功能上都基本一致。
  • 显然ES6的class语法是个语法糖

面向对象三大特性之封装

JavaScript的类和实例的封装从本质上看都是纸糊的(js函数对象/普通对象都不是封闭的,所有属性和方法均是public访问)。

一:ES5封装

1._约定
// 私有方法
Animal.prototype._bar = function () {
    
    
	// 私有属性
    return this._foo
}
2.借用模块化的封装性
Animal.prototype.fn= function (foo) {
    
    
    bar.call(this,foo)
}
// 私有方法
function bar(foo){
    
    
// 私有属性
	var foo = foo
}

二:ES6封装

1.#开头的属性名和方法名视为私有属性【提案,当前不可用】
class IncreasingCounter {
    
    
  // 私有属性
  #count = 0;
  get value() {
    
    
    console.log('Getting the current value!');
    return this.#count;
  }
  increment() {
    
    
    this.#count++;
  }
}
const counter = new IncreasingCounter();
counter.#count // 报错
counter.#count = 42 // 报错
2._约定(凡是以下划线开头的属性和方法就认为是私有的,外部尽量不要访问)
class Widget {
    
    

  // 公有方法
  foo (baz) {
    
    
    this._bar(baz);
  }

  // 私有方法,外部可访问
  _bar(baz) {
    
    
    return this.snaf = baz;
  }

  // ...
}
3.定义在class外部 + 模块化的封装性
class Widget {
    
    
  foo (baz) {
    
    
    bar.call(this, baz);
  }

  // ...
}
// 只在当前模块下可见,其它模块无法访问
function bar(baz) {
    
    
  return this.snaf = baz;
}
4. symbol + 模块化的封装性(使用当前模块下可见的symbol值作为私有属性名/方法名,外部无法获取到私有属性/方法名进行访问)
const bar = Symbol('bar');
const snaf = Symbol('snaf');
// 只暴露myClass,不暴露bar和snaf,外部无法调用[bar]方法,但内部方法foo可调用
export default class myClass{
    
    

  // 公有方法
  foo(baz) {
    
    
    this[bar](baz);
  }

  // 私有方法
  [bar](baz) {
    
    
  	// this[snaf]私有属性,模块内可访问
    return this[snaf] = baz;
  }

  // ...
};

面向对象三大特性之继承

一:继承的是什么?

1.继承父类的静态属性、静态方法
2.继承父类的实例属性、实例方法
3.继承父类的构造方法

二:ES6继承

1.父类
class AnimalES6{
    
    
    constructor(name) {
    
    
        this.name = name+'_animal_tag'
    }
    static getNum(){
    
    
        return AnimalES6.num
    }
    getName(){
    
    
        return this.name
    }
    setName(name){
    
    
        this.name = name
    }
}
AnimalES6.num = 42
2.子类
class CatES6 extends AnimalES6{
    
    
    constructor(name,cry) {
    
    
        super(name)
        this.cry = cry
    }
    getName(){
    
    
        return `[Cat] ${
      
      this.name}`
    }
}
3.构造、查看子类实例
console.log('-----------ES6继承------------------')
let catES6 = new CatES6('cat','喵喵')
console.log(catES6)

在这里插入图片描述

4.分析子类实例
  • 没有继承父类静态属性:num
  • 继承了父类实例属性:name
  • 没有继承但调用了父类构造方法:子类属性上有name属性,子类原型(父类)上没有name属性。
  • 没有继承父类静态方法:getNum
  • 继承了父类实例方法:setName,getName

三:ES5组合继承

1.父类
function Animal(name) {
    
    
    this.name = name+'_animal_tag'
}
Animal.num = 42
Animal.getNum = function () {
    
    
    return Animal.num
}
Animal.prototype.getName = function () {
    
    
    return this.name
}
Animal.prototype.setName = function (name) {
    
    
    this.name = name
}
2.子类:原型链继承
function Cat1(name,cry) {
    
    
    this.name = name
    this.cry = cry
}
Cat1.prototype = new Animal()// Animal构造函数参数无意义
Cat1.prototype.constructor = Cat1 // 将Cat原型上的构造函数从Animal修正回Cat

// 自有方法(自有方法在原型链中优先级更高,可实现重写的目的)
Cat1.prototype.getName = function () {
    
    
    return `[Cat] ${
      
      this.name}`
}
3.子类:构造继承
function Cat2(name,cry) {
    
    
    Animal.call(this,name)// 构造继承(子类构造函数调用父类构造函数)
    this.cry = cry
}
Cat2.prototype.getName = function () {
    
    
    return `[Cat] ${
      
      this.name}`
}
4. 子类:组合继承
function Cat3(name,cry) {
    
    
    Animal.call(this,name)// 构造继承(子类构造函数调用父类构造函数)
    this.cry = cry
}
Cat3.prototype = new Animal()// Animal构造函数参数无意义
Cat3.prototype.constructor = Cat3 // 将Cat原型上的构造函数从Animal修正回Cat

Cat3.prototype.getName = function () {
    
    
    return `[Cat] ${
      
      this.name}`
}
5.构造查看各继承方式子类实例
console.log('-----------ES5原型链继承--------------')
let cat1 = new Cat1('cat1','喵喵')
console.log(cat1)
console.log('-----------ES5构造继承--------------')
let cat2 = new Cat2('cat2','喵喵')
console.log(cat2)
console.log('-----------ES5组合继承--------------')
let cat3 = new Cat3('cat3','喵喵')
console.log(cat3)

在这里插入图片描述

6.分析各继承方式子类继承实例
  • 通过原型链继承可以实现继承实例属性(如上子类、父类均有name属性,但父类上的那个没有意义)和方法。
  • 通过构造继承可以实现没有继承但调用了父类构造函数
  • 组合继承效果与如下ES6继承基本一致,同样无法继承静态属性和方法,另外缺点是多了一次父类构造函数的无效调用

面向对象三大特性之多态

多态就是同一种行为有多种表现

一:ES6多态

class AnimalES6{
    
    
    constructor() {
    
    }
    cry(){
    
    
        return 'cry'
    }
}
class CatES6 extends AnimalES6{
    
    
    constructor() {
    
    }
    cry(){
    
    
        return 'miao miao'
    }
}
class DogES6 extends AnimalES6{
    
    
    constructor() {
    
    }
    cry(){
    
    
        return 'wang wang'
    }
}
let ani = new AnimalES6()
let cat = new CatES6()
let dog= new DogES6()
ani.cry()
cat.cry()
dog.cry()

二:ES5多态

思想一致,ES6 class语法表现力更佳,此处不再赘述

参考

阮一峰 ES6 class

猜你喜欢

转载自blog.csdn.net/jw2268136570/article/details/105581650