JavaScript 面向对象编程思想
JavaScript 面向对象编程思想
什么是面向对象
面向对象不是新的东西,它只是过程式代码的一种高度封装,目的在于提高代码的开发效率和可维护性。
面向对象编程 —— Object Oriented Programming,简称 OOP ,是一种编程开发思想。
它将真实世界各种复杂的关系,抽象为一个个对象,然后由对象之间的分工与合作,完成对真实世界的模拟。
在面向对象程序开发思想中,每一个对象都是功能中心,具有明确分工,可以完成接受信息、处理数据、发出信息等任务。
因此,面向对象编程具有灵活、代码可复用、高度模块化等特点,容易维护和开发,比起由一系列函数或指令组成的传统的过程式编程(procedural programming),更适合多人合作的大型软件项目。
面向对象与面向过程
- 面向过程就是亲力亲为,事无巨细,面面俱到,步步紧跟,有条不紊
- 面向对象就是找一个对象,指挥得结果
- 面向对象将执行者转变成指挥者
- 面向对象不是面向过程的替代,而是面向过程的封装
面向对象的特性:
- 封装性
将对象运行所需的资源封装在程序对象中,基本上是方法和数据。对象是“公布其接口”。其他附加到这些接口上的对象不需要关心对象实现的方法即可使用这个对象。这个概念就是“不要告诉我你是怎么做的,只要做就可以了。"对象可以看作是一个自我包含的原子。对象接口包括了公共的方法和初始化数据。
- 继承性
说到继承并不太陌生,继承可以使得子类具有父类的各种的公有属性和公有方法。而不需要再次编写相同的代码。在令子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得与父类别不同的功能。子类继承父类后,子类具有父类属性和方法,然而也同样具备自己所独有的属性和方法,也就是说,子类的功能要比父类多或相同,不会比父类少。
- 多态性
按字面的意思就是“多种状态”,允许将子类类型的指针赋值给父类类型的指针。
说白了多态就是相同的事物,一个接口,多种实现,同时在最初的程序设定时,有可能会根据程序需求的不同,而不确定哪个函数实现,通过多态不需要修改源代码,就可以实现一个接口多种解决方案。
程序中面向对象的基本体现
在 JavaScript 中,所有数据类型都可以视为对象,当然也可以自定义对象。
自定义的对象数据类型就是面向对象中的类( Class )的概念。
我们以一个例子来说明面向过程和面向对象在程序流程上的不同之处。
假设我们要处理学生的成绩表,为了表示一个学生的成绩,面向过程的程序可以用一个对象表示:
var std1 = { name: 'Michael', score: 98 }
var std2 = { name: 'Bob', score: 81 }
而处理学生成绩可以通过函数实现,比如打印学生的成绩:
function printScore (student) {console.log('姓名:' + student.name + ' ' + '成绩:' + student.score)
}
如果采用面向对象的程序设计思想,我们首选思考的不是程序的执行流程,
而是 Student 这种数据类型应该被视为一个对象,这个对象拥有 name 和 score 这两个属性(Property)。
如果要打印一个学生的成绩,首先必须创建出这个学生对应的对象,然后,给对象发一个 printScore 消息,让对象自己把自己的数据打印出来。
抽象数据行为模板(Class):
function Student (name, score) {this.name = namethis.score = score
}Student.prototype.printScore = function () {console.log('姓名:' + this.name + ' ' + '成绩:' + this.score)
}
根据模板创建具体实例对象(Instance):
var std1 = new Student('Michael', 98)
var std2 = new Student('Bob', 81)
实例对象具有自己的具体行为(给对象发消息):
std1.printScore() // => 姓名:Michael 成绩:98
std2.printScore() // => 姓名:Bob 成绩 81
面向对象的设计思想是从自然界中来的,因为在自然界中,类(Class)和实例(Instance)的概念是很自然的。
Class 是一种抽象概念,比如我们定义的 Class——Student ,是指学生这个概念,
而实例(Instance)则是一个个具体的 Student ,比如, Michael 和 Bob 是两个具体的 Student 。
所以,面向对象的设计思想是:
- 抽象出 Class
- 根据 Class 创建 Instance
- 指挥 Instance 得结果
面向对象的抽象程度又比函数要高,因为一个 Class 既包含数据,又包含操作数据的方法。
创建对象
简单方式
我们可以直接通过 new Object() 创建:
var person = new Object()
person.name = 'Jack'
person.age = 18person.sayName = function () {console.log(this.name)
}
每次创建通过 new Object() 比较麻烦,所以可以通过它的简写形式对象字面量来创建:
var person = {name: 'Jack',age: 18,sayName: function () {console.log(this.name)}
}
对于上面的写法固然没有问题,但是假如我们要生成两个 person 实例对象呢?
var person1 = {name: 'Jack',age: 18,sayName: function () {console.log(this.name)}
}var person2 = {name: 'Mike',age: 16,sayName: function () {console.log(this.name)}
}
通过上面的代码我们不难看出,这样写的代码太过冗余,重复性太高。
简单方式的改进:工厂函数
我们可以写一个函数,解决代码重复问题:
function createPerson (name, age) {return {name: name,age: age,sayName: function () {console.log(this.name)}}
}
然后生成实例对象:
var p1 = createPerson('Jack', 18)
var p2 = createPerson('Mike', 18)
这样封装确实爽多了,通过工厂模式我们解决了创建多个相似对象代码冗余的问题,
但却没有解决对象识别的问题(即怎样知道一个对象的类型)。
构造函数
更优雅的工厂函数:构造函数
一种更优雅的工厂函数就是下面这样,构造函数:
function Person (name, age) {this.name = namethis.age = agethis.sayName = function () {console.log(this.name)}
}var p1 = new Person('Jack', 18)
p1.sayName() // => Jackvar p2 = new Person('Mike', 23)
p2.sayName() // => Mike
解析构造函数代码的执行
在上面的示例中,Person() 函数取代了 createPerson() 函数,但是实现效果是一样的。
这是为什么呢?
我们注意到,Person() 中的代码与 createPerson() 有以下几点不同之处:
没有显示的创建对象
直接将属性和方法赋给了 this 对象
没有 return 语句
函数名使用的是大写的 Person
而要创建 Person 实例,则必须使用 new 操作符。
以这种方式调用构造函数会经历以下 4 个步骤:创建一个新对象
将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象)
执行构造函数中的代码
返回新对象
下面是具体的伪代码:
function Person (name, age) {// 当使用 new 操作符调用 Person() 的时候,实际上这里会先创建一个对象// var instance = {}// 然后让内部的 this 指向 instance 对象// this = instance// 接下来所有针对 this 的操作实际上操作的就是 instancethis.name = namethis.age = agethis.sayName = function () {console.log(this.name)}// 在函数的结尾处会将 this 返回,也就是 instance// return this
}
构造函数和实例对象的关系
使用构造函数的好处不仅仅在于代码的简洁性,更重要的是我们可以识别对象的具体类型了。
在每一个实例对象中的__proto__中同时有一个 constructor 属性,该属性指向创建该实例的构造函数:
console.log(p1.constructor === Person) // => true
console.log(p2.constructor === Person) // => true
console.log(p1.constructor === p2.constructor) // => true
对象的 constructor 属性最初是用来标识对象类型的,
但是,如果要检测对象的类型,还是使用 instanceof 操作符更可靠一些:
console.log(p1 instanceof Person) // => true
console.log(p2 instanceof Person) // => true
总结:
- 构造函数是根据具体的事物抽象出来的抽象模板
- 实例对象是根据抽象的构造函数模板得到的具体实例对象
- 每一个实例对象都具有一个 constructor 属性,指向创建该实例的构造函数
注意: constructor 是实例的属性的说法不严谨,具体后面的原型会讲到
可以通过实例的 constructor 属性判断实例和构造函数之间的关系
注意:这种方式不严谨,推荐使用 instanceof 操作符,后面学原型会解释为什么
构造函数的问题
使用构造函数带来的最大的好处就是创建对象更方便了,但是其本身也存在一个浪费内存的问题:
function Person (name, age) {this.name = namethis.age = agethis.type = 'human'this.sayHello = function () {console.log('hello ' + this.name)}
}var p1 = new Person('lpz', 18)
var p2 = new Person('Jack', 16)
在该示例中,从表面上好像没什么问题,但是实际上这样做,有一个很大的弊端。
那就是对于每一个实例对象,type 和 sayHello 都是一模一样的内容,
每一次生成一个实例,都必须为重复的内容,多占用一些内存,如果实例对象很多,会造成极大的内存浪费。
console.log(p1.sayHello === p2.sayHello) // => false
对于这种问题我们可以把需要共享的函数定义到构造函数外部:
function sayHello = function () {console.log('hello ' + this.name)
}function Person (name, age) {this.name = namethis.age = agethis.type = 'human'this.sayHello = sayHello
}var p1 = new Person('lpz', 18)
var p2 = new Person('Jack', 16)console.log(p1.sayHello === p2.sayHello) // => true
这样确实可以了,但是如果有多个需要共享的函数的话就会造成全局命名空间冲突的问题。
你肯定想到了可以把多个函数放到一个对象中用来避免全局命名空间冲突的问题:
var fns = {sayHello: function () {console.log('hello ' + this.name)},sayAge: function () {console.log(this.age)}
}function Person (name, age) {this.name = namethis.age = agethis.type = 'human'this.sayHello = fns.sayHellothis.sayAge = fns.sayAge
}var p1 = new Person('lpz', 18)
var p2 = new Person('Jack', 16)console.log(p1.sayHello === p2.sayHello) // => true
console.log(p1.sayAge === p2.sayAge) // => true
至此,我们利用自己的方式基本上解决了构造函数的内存浪费问题。
原文地址
https://blog.csdn.net/weixin_45525272/article/details/108843527
JavaScript 面向对象编程思想相关推荐
- JavaScript 面向对象编程思想简介
JavaScript 面向对象编程 JavaScript 执行过程 JavaScript 面向对象编程 面向对象介绍 什么是对象 什么是面向对象 程序中面向对象的基本体现 创建对象 简单方式 简单方式 ...
- JavaScript 面向对象编程(三) —— 函数进阶 / 严格模式 / 高阶函数 / 闭包 / 浅拷贝和深拷贝
本篇为 JavaScript 进阶 ES6 系列笔记第三篇,将陆续更新后续内容.参考:JavaScript 进阶面向对象 ES6 :ECMAScript 6 入门 系列笔记: JavaScript 面 ...
- JavaScript 面向对象编程(二) —— 构造函数 / 原型 / 继承 / ES5 新增方法
本篇为 JavaScript 进阶 ES6 系列笔记第二篇,将陆续更新后续内容.参考:JavaScript 进阶面向对象 ES6 :ECMAScript 6 入门 : Javascript 继承机制的 ...
- JS(四)--JavaScript 面向对象编程
JavaScript 面向对象编程 一.JavaScript 面向对象编程 1.1..什么是面向对象编程 1.2.面向过程和面向对象的对比 1.3.JavaScript 中的对象? 1.3.1.属性和 ...
- JavaScript面向对象编程-第三版不完全系统解读
JavaScript面向对象编程-第三版不完全系统解读 作者:老九-技术大黍 产品:查看原文 社交:知乎 公众号:老九学堂(新手有福利) 特别声明:原创不易,未经授权不得转载或抄袭,如需转载可联系笔者 ...
- JavaScript面向对象编程浅析
JavaScript面向对象编程及设计模式 JavaScript面向对象编程及设计模式 一.面向对象编程 1.简述 2.面向对象编程特点 3.封装 4.this 5.call和apply 6.new ...
- 《JavaScript函数式编程思想》
自序 伴随着Web技术的普及,JavaScript已成为应用最广泛的编程语言之一.由于其在Web前端编程中的统治地位.语言本身的表现力.灵活性.开源的本质和ECMAScript标准近年来的快速发展,J ...
- Javascript面向对象编程思考与总结
Javascript面向对象编程 什么是对象?万物皆对象 -对象:特指的某个事物,具有属性和方法(一组无序的属性的集合) 特征:-----> 属性 行为:----->方法 对象是单个事物的 ...
- 《JavaScript面向对象编程指南》——第1章 引言1.1 回顾历史
本节书摘来自异步社区<JavaScript面向对象编程指南>一书中的第1章,第1.1节,作者: [加]Stoyan Stefanov 译者: 凌杰 更多章节内容可以访问云栖社区" ...
最新文章
- 日本上市公司提供BTC, BCH, ETH担保的贷款
- Linux基本命令之ls
- js如何打印object对象
- apt包管理 Android,apt软件包管理学习(示例代码)
- JAVA复习5(集合——LinkedList)
- (1)dotnet开源电商系统-brnshopbrnMall 和老外开发的nopCommerce(dotnet两套电商来PK--第一篇)...
- 动手做个DialoGPT:生成式多轮对话模型
- 各种文件系统支持的最大硬盘容量
- 计算机研究与发展 杂志,《计算机研究与发展》杂志投稿的具体要求详解
- Java for LintCode 验证二叉查找树
- Calendar(显示日期)
- 动态规划精卫填海之路
- Vue实现CNode
- ASP编程实现各家快递公司订单状态查询
- 求下载:CLodop_Setup_for_Win32NT.exe 免费地址谢谢
- 职称论文发表时怎么选择期刊
- oracle12c用plsql连不上,PLSQL连接oracle12c
- python控制相机,在OpenCV / Python中设置相机参数
- 怎么办?不到 20 人的 IT 公司我该去吗?
- 《软件方法》第8章 分析 之 分析类图(2)
热门文章
- java对账_java后台实现支付宝对账功能的示例代码
- Java学习之mark标记
- 情绪之潮:逆境中的治愈与成长
- 原来这就是笔杆子公文写作材料经典提纲汇编
- 17.4.17 漫画与人脸识别(二) WebCaricature: a benchmark for caricature face recognition 小感
- idea提示Your idea evaluation has expired. Your session will be limited to 30 minutes
- UG\NX二次开发 获取装配根节点的两种方式
- AI时代,三步走成为超级个体!
- 大型网站技术架构的前十个脚印
- 初识qml——PathView xyz轴 路径属性 旋转效果 鼠标键盘控制