面向对象

了解构造函数原型对象的语法特征,掌握 JavaScript 中面向对象编程的实现方式,基于面向对象编程思想实现 DOM 操作的封装。

  • 了解面向对象编程的一般特征
  • 掌握基于构造函数原型对象的逻辑封装
  • 掌握基于原型对象实现的继承
  • 理解什么原型链及其作用
  • 能够处理程序异常提升程序执行的健壮性

一、面向对象

学习 JavaScript 中基于原型的面向对象编程序的`语法实现,理解面向对象编程的特征。

面向对象编程是一种程序设计思想,它具有 3 个显著的特征:封装、继承、多态。

1.1 封装

封装的本质是将具有关联的代码组合在一起,其优势是能够保证代码复用且易于维护,函数是最典型也是最基础的代码封装形式,面向对象思想中的封装仍以函数为基础,但提供了更高级的封装形式。

命名空间

先来回顾一下以往代码封装的形式:

<script>
// 普通对象(命名空间)形式的封装
let beats = {
name: '狼',
setName: function (name) {
this.name = this.name;
},
getName() {
console.log(this.name);
}
} beats.setName('熊');
beats.getName();
</script>

以往以普通对象(命名空间)形式封装的代码只是单纯把一系列的变量或函数组合到一起,所有的数据变量都被用来共享(使用 this 访问)。

构造函数

对比以下通过面向对象的构造函数实现的封装:

<script>
function Person() {
this.name = '佚名';
// 设置名字
this.setName = function (name) {
this.name = name;
}
// 读取名字
this.getName = () => {
console.log(this.name);
}
} // 实例对像,获得了构造函数中封装的所有逻辑
let p1 = new Person();
p1.setName('小明');
console.log(p1.--name);// 小明 // 实例对象
let p2 = new Person();
console.log(p2.name); // 佚名
</script>

构造函数相当于一个"模子",能够像字面量那样创建出对象来,所不同的是借助构造函数创建出来的实例对象之间是彼此不影响的。

总结:

  1. 构造函数体现了面向对象的封装特性
  2. 构造函数实例创建的对象彼此独立、互不影响
  3. 命名空间式的封装无法保证数据的独立性

注:可以举一些例子,如女娲造人等例子,加深对构造函数的理解。

原型对象

实际上每一个构造函数都有一个名为 prototype 的属性,译成中文是原型的意思,prototype 的是对象类据类型,称为构造函数的原型对象,每个原型对象都具有 constructor 属性代表了该原型对象对应的构造函数。

目标:能够利用原型对象实现方法的共享的

  1. JavaScript规定,每一个构造函数都有一个prototype属性,指向另一个对象,所以我们也称为原型对象
  2. 构造函数通过原型分配的函数是所有对象所共享的
  3. 这个对象可以挂载函数,对象实例化不会多次创建原型上函数,节约内存
  4. 我们向以把那些不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法。
  5. 构造函数和原型对象中的this都指向实例化的对象
<script>
function Person() { } // 每个函数都有 prototype 属性
console.log(Person.prototype);
</script>

如下图所示:

了解了 JavaScript 中构造函数与原型对象的关系后,再来看原型对象具体的作用,如下代码所示:

<script>
function Person() {
// 此处未定义任何方法
} // 为构造函数的原型对象添加方法
Person.prototype.sayHi = function () {
console.log('Hi~');
} // 实例化
let p1 = new Person();
p1.sayHi(); // 输出结果为 Hi~
</script>

其结构如图所示:

构造函数 Person 中未定义任何方法,这时实例对象调用了原型对象中的方法 sayHi,接下来改动一下代码:

<script>
function Person() {
// 此处定义同名方法 sayHi
this.sayHi = function () {
console.log('嗨!');
}
} // 为构造函数的原型对象添加方法
Person.prototype.sayHi = function () {
console.log('Hi~');
} let p1 = new Person();
p1.sayHi(); // 输出结果为 嗨!
</script>

构造函数 Person 中定义与原型对象中相同名称的方法,这时实例对象调用则是构造函中的方法 sayHi

通过以上两个简单示例不难发现 JavaScript 中对象的工作机制:当访问对象的属性或方法时,先在当前实例对象是查找,然后再去原型对象查找,并且原型对象被所有实例共享。

<script>
function Person() {
// 此处定义同名方法 sayHi
this.sayHi = function () {
console.log('嗨!' + this.name);
}
} // 为构造函数的原型对象添加方法
Person.prototype.sayHi = function () {
console.log('Hi~' + this.name);
}
// 在构造函数的原型对象上添加属性
Person.prototype.name = '小明'; let p1 = new Person();
p1.sayHi(); // 输出结果为 嗨! let p2 = new Person();
p2.sayHi();
</script>

什么是原型对象??

答:是构造函数的一个属性,它的数据类型是对象

原型对象有啥用??

答:原型对象对应的构造函数的实例方法或属性不存在时会去查找原型对象

总结:结合构造函数原型的特征,实际开发重往往会将封装的功能函数添加到原型对象中。

1.4给数组扩展方法

<script>
const arr = [1, 2, 3, 4, 56, 67]
Array.prototype.sum = function () {
return this.reduce((prev, item) => prev + item, 0)
}
console.log(arr.sum()) // 133
Array.prototype.max = function () {
return Math.max(...this) // this指向实例对象arr
} console.log(arr.max()) //67
</script>

1.5 constructor属性

function Star(){
}
const ssx = new Star()
//每个原型对象上都有一个constructor属性,指向创建出来的构造函数
Star.prototype.constructor === Star // -----------------------------------------
用法:
function Star(){
}
//向prototype 中加入两个方法
function Star() {}
//向prototype 中加入两个方法
console.log(Star.prototype)
Star.prototype = {
sing: function () {
console.log('唱歌')
},
dance: function () {
console.log('跳舞')
},
constructor: Star, //重新指向Star ,如果不指向,会没有Star中的属性
}
console.log(Star.prototype)

1.6对象原型

对象都会有一个属性__proto__
指向构造函数的prototype原型对象,之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有__proto__原型的存在。
function Star() {}
let ssx = new Star()
console.log(ssx.__proto__ === Star.prototyp) //true

注意:

  1. __proto__是J S非标准属性
  2. [[prototype]__proto__意义相同
  3. 用来表明当前实例对象指向哪个原型对象prototype
  4. __proto__对象原型里面也有一个constructor属性,指向创建该实例对象的构造函数
  5. 对象原型里面也有一个constructor 指向 构造函数 Star

1.2 继承

继承是面向对象编程的另一个特征,通过继承进一步提升代码封装的程度,JavaScript 中大多是借助原型对象实现继承的特性。

龙生龙、凤生凤、老鼠的儿子会打洞描述的正是继承的含义,分别封装中国人和日本人的行为特征来理解编程中继承的含义,代码如下:

<script>
// 封装中国人的行为特征
function Chinese() {
// 中国人的特征
this.arms = 2;
this.legs = 2;
this.eyes = 2; this.skin = 'yellow';
this.language = '中文'; // 中国人的行为
this.walk = function () {}
this.sing = function () {}
this.sleep = function () {}
} // 封装日本人的行为特征
function Japanese() {
// 日本人的特征
this.arms = 2;
this.legs = 2;
this.eyes = 2; this.skin = 'yellow';
this.language = '日文'; // 日本人的行为
this.walk = function () {}
this.sing = function () {}
this.sleep = function () {}
}
</script>

其实我们都知道无论是中国人、日本人还是其它民族,人们的大部分特征是一致的,然而体现在代码中时人的相同的行为特征被重复编写了多次,代码显得十分冗余,我们可以将重复的代码抽离出来:

原型继承

基于构造函数原型对象实现面向对象的继承特性。

<script>
//原型继承
const Person = {
eyes: 2,
head: 1,
}
//通过原型来继承Person
Woman.prototype = Person
//找回原来的构造函数
Woman.prototype.constructor = Woman
//女人
function Woman() {}
const woman = new Woman()
console.log(woman) //通过原型来继承Person
Man.prototype = Person
//找回原来的构造函数
Man.prototype.constructor = Man
//男人
function Man() {}
const man = new Man()
console.log(man)
</script>

原型继承不同的方法时出现问题

*   当为一个构造函数的原型对象上添加一个方法时,另一个对象也会改变
//问题解决
//原型继承
// const Person = {
// eyes: 2,
// head: 1,
// } //构造函数 new 出来的对象,结构一样,但是对象不一样
function Person() {
this.eyes = 2
this.head = 1
}
--------------------------------------------------------------
//通过原型来继承Person
Woman.prototype = new Person()
//找回原来的构造函数
Woman.prototype.constructor = Woman
//女人
//独有的方法:
Woman.prototype.baby = function () {
console.log('生孩子')
}
function Woman() {}
const woman = new Woman()
console.log(woman)
--------------------------------------------------------------
//通过原型来继承Person
Man.prototype = new Person()
//找回原来的构造函数
Man.prototype.constructor = Man
//男人
function Man() {}
const man = new Man()
console.log(man)

如下图所示:

上述代码中是以命名空间的形式实现的继承,事实上 JavaScript 中继承更常见的是借助构造函数来实现:

如下图所示:

原型链

基于原型对象的继承使得不同构造函数的原型对象关联在一起,并且这种关联的关系是一种链状的结构,我们将原型对象的链状结构关系称为原型链,如下图所示:

作用:用于查找成员提供机制

<script>
// Person 构造函数
function Person() {
this.arms = 2;
this.walk = function () {}
} // Person 原型对象
Person.prototype.legs = 2;
Person.prototype.eyes = 2;
Person.prototype.sing = function () {}
Person.prototype.sleep = function () {} // Chinese 构造函数
function Chinese() {
this.skin = 'yellow';
this.language = '中文';
} // Chinese 原型对象
Chinese.prototype = new Person();
Chinese.prototype.constructor = Chinese; // 实例化
let c1 = new Chinese();
console.log(c1);
</script>

在 JavaScript 对象中包括了一个非标准备的属性 __proto__ 它指向了构造函数的原型对象,通过它可以清楚的查看原型对象的链状结构。

1.25原型链-查找规则(只要是对象就是 __proto__ ,只要是原型对象就有constructor指向构造函数)

  1. 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。
  2. 如果没有就查找它的原型(也就是__proto__指向的prototype原型对象
  3. 如果还没有就查找原型对象的原型(Object的原型对象)
  4. 依此类推一直找到Object为止(null)
  5. __proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线
  6. 可以使用instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上

1.26 instanceof

object instanceof constructor
object 要检测的对象.
constructor 某个构造函数

1.3 写在最后

面向对象(OOP)是编程时的一种指导思想,需要通过不断的实践才能体会面向对象编程的优势,在 JavaScript 中面向对象编程的实现是以构造函数和原型对象为核心的,因此掌握构造函数和原型对象的语法是灵活运用面向对象的基础。

面向对象多态的特性在 JavaScript 中应用场景相对较少,本次课中暂不讲解。

二、异常处理

了解 JavaScript 中程序异常处理的方法,提升代码运行的健壮性。

2.1 throw

异常处理是指预估代码执行过程中可能发生的错误,然后最大程度的避免错误的发生导致整个程序无法继续运行。

<script>
function counter(x, y) { if(!x || !y) {
// throw '参数不能为空!';
throw new Error('参数不能为空!');
} return x + y;
} counter();
</script>

总结:

  1. throw 抛出异常信息,程序也会终止执行
  2. throw 后面跟的是错误提示信息
  3. Error 对象配合 throw 使用,能够设置更详细的错误信息
2.2 try ... catch
<script>
function foo() { try {
// 查找 DOM 节点
var p = docunent.querySelector('p'); } catch(error) {
// try 代码段中执行有错误时,会执行 catch 代码段 // 查看错误信息
console.log(error.message); // 终止代码继续执行
return;
} // 改变文本样式
p.style.color = 'red';
} foo();
</script>

总结:

  1. try...catch 用于捕获错误信息
  2. 将预估可能发生错误的代码写在 try 代码段中
  3. 如果 try 代码段中出现错误后,会执行 catch 代码段,并截获到错误信息

JavaScript笔记之面向对象的更多相关文章

  1. JavaScript高级程序设计笔记之面向对象

    说起面向对象,大部分程序员首先会想到 类 .通过类可以创建许多具有共同属性以及方法的实例或者说对象.但是JavaScript并没有类的概念,而且在JavaScript中几乎一切皆对象,问题来了,Jav ...

  2. javascript笔记—面向对象

    什么是对象: 对象是一个整体,对外提供一些操作. 什么是面向对象: 使用对象时,只关注对象提供的功能,不关注其内部细节,例如jquery 面向对象是一种通用思想,并非只有编程中能用,任何事情都可以用. ...

  3. 精通javascript笔记(智能社)——简易tab选项卡及应用面向对象方法实现

    javascript代码(常规方式/面向过程): <script type="text/javascript"> window.onload=function(){ v ...

  4. [Effective JavaScript 笔记]第3章:使用函数--个人总结

    前言 这一章把平时会用到,但不会深究的知识点,分开细化地讲解了.里面很多内容在高3等基础内容里,也有很多讲到.但由于本身书籍的篇幅较大,很容易忽视对应的小知识点.这章里的许多小提示都很有帮助,特别是在 ...

  5. javascript高级特性(面向对象)

    javascript高级特性(面向对象): * 面向对象: * 面向对象和面向过程的区别: * 面向对象:人就是对象,年龄\性别就是属性,出生\上学\结婚就是方法. * 面向过程:人出生.上学.工作. ...

  6. Lua学习笔记:面向对象

    Lua学习笔记:面向对象 https://blog.csdn.net/liutianshx2012/article/details/41921077 Lua 中只存在表(Table)这么唯一一种数据结 ...

  7. ASP.NET MVC 学习笔记-7.自定义配置信息 ASP.NET MVC 学习笔记-6.异步控制器 ASP.NET MVC 学习笔记-5.Controller与View的数据传递 ASP.NET MVC 学习笔记-4.ASP.NET MVC中Ajax的应用 ASP.NET MVC 学习笔记-3.面向对象设计原则

    ASP.NET MVC 学习笔记-7.自定义配置信息   ASP.NET程序中的web.config文件中,在appSettings这个配置节中能够保存一些配置,比如, 1 <appSettin ...

  8. 前端开发:面向对象与javascript中的面向对象实现(二)构造函数与原型

    前端开发:面向对象与javascript中的面向对象实现(二)构造函数与原型 前言(题外话): 有人说拖延症是一个绝症,哎呀治不好了.先不说这是一个每个人都多多少少会有的,也不管它究竟对生活有多么大的 ...

  9. 简单分析JavaScript中的面向对象

    初学JavaScript的时候有人会认为JavaScript不是一门面向对象的语言,因为JS是没有类的概念的,但是这并不代表JavaScript没有对象的存在,而且JavaScript也提供了其它的方 ...

  10. 前端开发:面向对象与javascript中的面向对象实现(一)

    前端开发:面向对象与javascript中的面向对象实现(一) 前言: 人生在世,这找不到对象是万万不行的.咱们生活中,找不到对象要挨骂,代码里也一样.朋友问我说:“嘿,在干嘛呢......”,我:“ ...

随机推荐

  1. 《Win10——如何进入高级启动选项》

    Win10--如何进入高级启动选项       第一种方法: 管理员命令提示符输入如下代码,自动重启并进入高级启动选项. shutdown /r /o /f /t 00     第二种方法: 1. 管 ...

  2. 百度ueditor工具栏配置大全

    toolbars: [[ 'source', // 源代码 'anchor', // 锚点 'undo', // 撤销 'redo', // 重做 'bold', // 加粗 'indent', // ...

  3. Kubernetes(k8s)通过环境变量将 Pod 信息呈现给容器

    Downward API 有两种方式可以将 Pod 和 Container 字段呈现给运行中的容器: 环境变量 卷文件 这两种呈现 Pod 和 Container 字段的方式统称为 Downward ...

  4. k8s控制器理解

    DaemonSet 一个DaemonSet对象能确保其创建的Pod在集群中的每一台(或指定)Node上都运行一个副本.如果集群中动态加入了新的Node,DaemonSet中的Pod也会被添加在新加入N ...

  5. Docker 部署 JIRA(破解版)

    一. 说明 1.1 素材 本文采用素材如下: Docker镜像 Github链接(https://github.com/cptactionhank) 破解工具 Gitee链接(https://gite ...

  6. k3s部署全过程

    # 安装k3s博客 ## 准备工作 1.准备俩台可以相互访问的服务器 2.需要先安装dockers 3.以下教程将使用VsCode+ssh插件来进行插件图 ssh连接到俩台服务器 点击打开ssh操作界 ...

  7. 用户输入学号,如果是以aabcddef开头,并且后边是4位数字,前两位大于06小于等于当前年份。判断用户输入是否合法

    package seven_topic; import java.util.*; public class p_19_1 { public static void main(String[] args ...

  8. HBase1.4.6安装搭建及shell命令使用

    HBase1.4.6安装搭建 目录 HBase1.4.6安装搭建 一.前期准备(Hadoop,zookeeper,jdk) 搭建Hbase 1.上传解压 2.配置环境变量 3.修改hbase-env. ...

  9. [Android开发学iOS系列] 快速上手UIKit

    快速上手iOS UIKit UIKit是苹果官方的framework, 其中包含了各种UI组件, window和view, 事件处理, 交互, 动画, 资源管理等基础设施支持. 按照前面的介绍, 用U ...

  10. mongodb基础整理篇————副本概念篇[外篇]

    前言 副本集整理. 开始逐步把mongodb博客补齐了. 正文 什么是副本集 副本集是一组服务器,其中一个是用于处理写入操作的主节点,还有多个用于保存主节点的数据副本的从节点. 如果主节点崩溃了,则从 ...