JavaScript 面向对象编程

  • JavaScript 执行过程
  • JavaScript 面向对象编程
    • 面向对象介绍
      • 什么是对象
      • 什么是面向对象
      • 程序中面向对象的基本体现
    • 创建对象
      • 简单方式
      • 简单方式的改进:工厂函数
    • 构造函数
      • 更优雅的工厂函数:构造函数
      • 解析构造函数代码的执行
      • 构造函数和实例对象的关系
      • 构造函数的问题

JavaScript 执行过程

JavaScript 运行分为两个阶段:

  • 预解析

    • 全局预解析(所有变量和函数声明都会提前;同名的函数和变量函数的优先级高)
    • 函数内部预解析(所有的变量、函数和形参都会参与预解析)
      • 函数
      • 形参
      • 普通变量
  • 执行

先预解析全局作用域,然后执行全局作用域中的代码,
在执行全局代码的过程中遇到函数调用就会先进行函数预解析,然后再执行函数内代码。


JavaScript 面向对象编程

面向对象介绍

什么是对象

Everything is object (万物皆对象)

对象到底是什么,我们可以从两次层次来理解。

(1) 对象是单个事物的抽象。

一本书、一辆汽车、一个人都可以是对象,一个数据库、一张网页、一个与远程服务器的连接也可以是对象。当实物被抽象成对象,实物之间的关系就变成了对象之间的关系,从而就可以模拟现实情况,针对对象进行编程。

(2) 对象是一个容器,封装了属性(property)和方法(method)。

属性是对象的状态,方法是对象的行为(完成某种任务)。比如,我们可以把动物抽象为animal对象,使用“属性”记录具体是那一种动物,使用“方法”表示动物的某种行为(奔跑、捕猎、休息等等)。

在实际开发中,对象是一个抽象的概念,可以将其简单理解为:数据集或功能集

ECMAScript-262 把对象定义为:无序属性的集合,其属性可以包含基本值、对象或者函数
严格来讲,这就相当于说对象是一组没有特定顺序的值。对象的每个属性或方法都有一个名字,而每个名字都
映射到一个值。

提示:每个对象都是基于一个引用类型创建的,这些类型可以是系统内置的原生类型,也可以是开发人员自定义的类型。

什么是面向对象

面向对象不是新的东西,它只是过程式代码的一种高度封装,目的在于提高代码的开发效率和可维护性。

面向对象编程 —— 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 这种数据类型应该被视为一个对象,这个对象拥有 namescore 这两个属性(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 个步骤:

  1. 创建一个新对象
  2. 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象)
  3. 执行构造函数中的代码
  4. 返回新对象

下面是具体的伪代码:

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)

在该示例中,从表面上好像没什么问题,但是实际上这样做,有一个很大的弊端。
那就是对于每一个实例对象,typesayHello 都是一模一样的内容,
每一次生成一个实例,都必须为重复的内容,多占用一些内存,如果实例对象很多,会造成极大的内存浪费。

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

至此,我们利用自己的方式基本上解决了构造函数的内存浪费问题。
但是代码看起来还是那么的格格不入,那有没有更好的方式呢?

那就要通过原型对象的方法来建立,下面这篇博文进行介绍

JavaScript 面向对象编程思想简介相关推荐

  1. JavaScript 面向对象编程思想

    JavaScript 面向对象编程思想 什么是面向对象 面向对象不是新的东西,它只是过程式代码的一种高度封装,目的在于提高代码的开发效率和可维护性. 面向对象编程 -- Object Oriented ...

  2. JavaScript面向对象编程-第三版不完全系统解读

    JavaScript面向对象编程-第三版不完全系统解读 作者:老九-技术大黍 产品:查看原文 社交:知乎 公众号:老九学堂(新手有福利) 特别声明:原创不易,未经授权不得转载或抄袭,如需转载可联系笔者 ...

  3. 面向对象编程思想概览(一)类和对象

    一.简介 生活中有许多技巧值得我们在程序设计中模仿和借鉴.采用类的思想可以方便地把具有相同属性和方法归为一类,从而简化编程.本讲利用生活的常见示例,浅显通俗的语言,生动地介绍了面向对象编程中类与对象的 ...

  4. JavaScript 面向对象编程(三) —— 函数进阶 / 严格模式 / 高阶函数 / 闭包 / 浅拷贝和深拷贝

    本篇为 JavaScript 进阶 ES6 系列笔记第三篇,将陆续更新后续内容.参考:JavaScript 进阶面向对象 ES6 :ECMAScript 6 入门 系列笔记: JavaScript 面 ...

  5. JavaScript 面向对象编程(二) —— 构造函数 / 原型 / 继承 / ES5 新增方法

    本篇为 JavaScript 进阶 ES6 系列笔记第二篇,将陆续更新后续内容.参考:JavaScript 进阶面向对象 ES6 :ECMAScript 6 入门 : Javascript 继承机制的 ...

  6. 面向对象编程思想概览(三)继承

    一.简介 本讲以大家耳熟能详的<西游记>中的唐僧师徒四人为例,介绍了类的继承的概念和实现方法,总结了继承的特性和优点,帮助同学们理解面向对象编程中继承的用法,进而掌握面向对象程序设计的基本 ...

  7. JS(四)--JavaScript 面向对象编程

    JavaScript 面向对象编程 一.JavaScript 面向对象编程 1.1..什么是面向对象编程 1.2.面向过程和面向对象的对比 1.3.JavaScript 中的对象? 1.3.1.属性和 ...

  8. JavaScript面向对象编程浅析

    JavaScript面向对象编程及设计模式 JavaScript面向对象编程及设计模式 一.面向对象编程 1.简述 2.面向对象编程特点 3.封装 4.this 5.call和apply 6.new ...

  9. 《JavaScript函数式编程思想》

    自序 伴随着Web技术的普及,JavaScript已成为应用最广泛的编程语言之一.由于其在Web前端编程中的统治地位.语言本身的表现力.灵活性.开源的本质和ECMAScript标准近年来的快速发展,J ...

最新文章

  1. arcgis python脚本实现从界面选择输入输出_arcgis python脚本实现从界面选择输入输出_ArcGIS Python编程案例(2)-使用ArcPy编写脚本......
  2. 【Window / 浏览器】 常用 快捷键 整理
  3. 好书推荐——CSDN《新程序员》
  4. 新版vue-cli模板下本地开发环境使用node服务器跨域
  5. bzoj1038500AC!
  6. CVPR2021 双图层实例分割,大幅提升遮挡处理性能
  7. 好用的跨平台开源截图工具推荐--flameshot
  8. ledshowtw2013(LED图文编辑软件)v13.10.8.1
  9. Pr剪辑上分攻略 -- 动漫电影混剪
  10. 智能制造与大数据平台
  11. Dialog去掉默认白色背景
  12. 与Windows更新的抗争-取消Windows系统自动更新
  13. 腾讯云轻量应用服务器搭建网站
  14. 【《大象》第一部分】初识UML和建模基础
  15. 单片机原理及应用 实验六 双机通信与PCB设计
  16. PS进阶篇——如何PS软件钢笔工具抠图(三)
  17. MySQL 查询排名
  18. 常用的Linux命令(面试/工作必备)
  19. Qt入门(九)——“头脑风暴”(在线答题_*重点项目*)
  20. 按系列罗列Linux的发行版,并描述不同发行版之间的联系与区别。

热门文章

  1. 3D模型代下【淘宝店3D模型代下渠道】解密
  2. 二进制的前导的零 — C语言【十进制转二进制分大于0,小于0和等于0】
  3. 关于win10系统(客户机)无法添加网络共享打印机的解决办法(提示信息:操作失败,错误为0X0000011B)
  4. Java Map按value值的大小进行排序
  5. SpringBoot使用Redisson实现分布式锁秒杀
  6. 【由浅入深学Python】Python 保留字
  7. java高内聚低耦合什么意思_高内聚低耦合的理解
  8. 旧美(旧走遍美国)云集
  9. Python串口编程(转载)
  10. 三周速通AWS Certified Solutions Architect - Associate(SAA-C03)经验分享