javascript面向对象编程(一):封装

javascript是一种解释型语言,不需要像C、C++那样先编译后执行,同时也是一种基于对象的脚本语言,可以创建对象,也可以使用现有的对象。

在ES5中,类的封装与继承是通过构造函数来实现的。ES6中可以使用Class关键字来声明一个类,只不过ES6的新特性还没有完全被浏览器所支持。好在我们可以使用es6相关的插件来转换成es5的语法。

一、为什么要封装?

大家在日常开发过程中,具有相同处理逻辑的代码通常会进行函数的封装来减少代码的冗余,使代码看起来更优雅美观,那么

当多个实体对象他们拥有相同的一些属性行为(方法)时,自然也要把这些相同的属性和行为抽象出来,减少不必要的代码。

我们举个栗子:

电脑的属性有哪些?,大家脑袋里面肯定飞快的能想到:屏幕尺寸,机身颜色,CPU等等,这些都是电脑的共有属性,那么它的功能有哪些?播放音视频、写文稿等等,这些事电脑共有的行为功能。

就以电脑为例,我们把它这些属性和行为抽象出来,代码如下

function Computer(screenSize,color,cpu){
    this.screenSize = screenSize;
    this.color = color;
    this.cpu = cpu
}
Computer.prototype.playVideo = function(){
    console.log('我具有播放音视频的功能')
}
Computer.prototype.writeWord = function(){
    console.log('我具有编辑文稿的功能')
}

二、为什么属性定义在定义在构造函数本身,而方法定义在prototype原型上?

先说方法,prototype原型链的查找规则是自下而上的,会根据继承关系层层向上查找,直到找到Object的原型(顶级),如果中途找到该方法,就会停止查找,再把该方法反馈给调用该方法的实体对象,然后执行。如果找不到,就报该方法未定义。

所以,定义在prototype上的每一个方法,是只占据一个堆内存地址,子类继承或实体对象本身是不会再创建一个堆内存的,他们都指向同一个方法。我们看一下下面的代码:

定义在原型上:

function Computer(screenSize,color,cpu){
    this.screenSize = screenSize;
    this.color = color;
    this.cpu = cpu;
}
Computer.prototype.playVideo = function(){
    console.log('我具有播放音视频的功能')
}
Computer.prototype.writeWord = function(){
    console.log('我具有编辑文稿的功能')
}
const windowComputer = new Computer('1320*768','black','i5');//生成的window电脑实例
console.log(windowComputer.playVideo === new Computer().playVideo ) //打印结果:true
console.log(windowComputer)//看下面截图,实例本身不包含playVideo 和 writeWord 方法

 

 定义在构造函数本身:

function Computer(screenSize,color,cpu){
    this.screenSize = screenSize;
    this.color = color;
    this.cpu = cpu;
    this.playVideo = function(){
        console.log('我具有播放音视频的功能')
    }
    this.writeWord = function(){
        console.log('我具有编辑文稿的功能')
    }
}
const windowComputer = new Computer('1320*768','black','i5');//生成的window电脑实例
console.log(windowComputer.playVideo === new Computer().playVideo )//打印结果:false
console.log(windowComputer)//看下面截图,实例本身也被创建了playVideo 和 writeWord 方法

 

**注意:向上查找过程中,是优先查找父构造函数(父类)本身定义的方法,找到便不再继续查找,如果找不到,再去该父构造函数(父类)的原型prototype原型上去查找。定义在构造函数本身的方法如果和定义在原型上的方法相同,构造函数上的方法会覆盖掉原型上的方法。

再来说一下属性为什么定义在构造函数(类)本身:

不同的电脑,拥有相同的属性名称,但属性值却不尽相同,所以生成的每个电脑实体一定要有自己单独的属性,是一定不能指向同一个内存地址的。

三、用ES6中的class类来实现

以上是ES5中使用构造函数来,下面以ES6的Class关键字来抽象出一个电脑的类

class Computer {
  constructor(screenSize, color, cpu){
    this.screenSize = screenSize
    this.color = color
    this.cpu = cpu
    // this.playVideo = ()=>{
    //   console.log('我具有播放音视频的功能,我是定义在类本身')
    // }
    // this.writeWord = ()=>{
    //   console.log('我具有编辑文稿的功能,我是定义在类本身')
    // }
  }
  playVideo(){
    console.log('我具有播放音视频的功能')
  }
  writeWord(){
    console.log('我具有编辑文稿的功能')
  }
}
const windowComputer = new Computer('1320*768', 'black', 'i5')
windowComputer.playVideo()
windowComputer.writeWord()

在类里面定义方法,是直接定义在原型上的。constructor方法相当于es5中构造函数本身,类被实例化之后(使用new 运算符之后)作用域和this指向将赋给新的实例对象。

最终经过插件转换之后,其实还是ES5中构造函数的写法,不过ES6迟早是要被各大浏览器支持的,这也是大势所趋。

我将我所理解的尽可能通俗易懂的解释出来,欢迎大家在下面进行积极发言讨论。

点此进入下一章节:javascript面向对象编程(二):封装

展开阅读全文

没有更多推荐了,返回首页