面向对象

了解构造函数原型对象的语法特征,掌握 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. 将 Docker Engine 节点从 dockershim 迁移到 cri-dockerd

    官网地址:https://kubernetes.io/zh-cn/docs/tasks/administer-cluster/migrating-from-dockershim/migrate-doc ...

  2. 安装Alertmanager,nginx配置二级路径代理访问

    安装配置 Alertmanager wget https://github.com/prometheus/alertmanager/releases/download/v0.20.0/alertman ...

  3. Elasticsearch中的一些重要概念:cluster, node, index, document, shards及replica

    首先,我们来看下一下如下的这个图: Cluster Cluster也就是集群的意思.Elasticsearch集群由一个或多个节点组成,可通过其集群名称进行标识.通常这个Cluster 的名字是可以在 ...

  4. 前端ES6 面试过关宝典

    ES6 部分 Typescript 部分 前端工程面经(节流防抖.https.前端攻击.性能优化...) https://juejin.cn/post/6844903734464495623 ES6面 ...

  5. 使用css制作轮播图

    <!--HTML代码--> <!DOCTYPE html> <html lang="en"><head> <meta char ...

  6. Oracle字段约束

    初识约束 约束是数据库用来确保数据满足业务规则的手段,对数据做的条件限制. 约束的类型 1. 主键约束(PRIMARY KEY) 2. 唯一性约束(UNIQUE) 3. 非空约束(NOT NULL) ...

  7. IDEA快速生成数据库表的实体类

    IDEA连接数据库 IDEA右边侧栏有个DataSource,可以通过这个来连接数据库,我们先成功连接数据库 点击进入后填写数据库进行连接,注意记得一定要去Test Connection 确保正常连接 ...

  8. 【算法】基础DP

    参考资料 背包九讲 一.线性DP 如果现在在状态 i 下,它上一步可能的状态是什么. 上一步不同的状态依赖于什么. 根据上面的分析,分析出状态和转移方程.注意:dp 不一定只有两维或者一维,一开始设计 ...

  9. 第二阶段:高级核心基础知识·第4章shell特性·2

    1.统计日志,日志内容 39.96.187.239 - - [11/Nov/2019:10:08:01 +0800] "GET / HTTP/1.1" 302 0 "-& ...

  10. Intel GPU Gen 9 架构

    * 参考spec:the-compute-architecture-of-intel-processor-graphics-gen9-v1d0.pdf SOC 架构 Gen9 架构是早期用在igpu ...