面向对象

了解构造函数原型对象的语法特征,掌握 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. 如何通过Java应用程序创建Word表格

    表格,又称为表,既是一种可视化交流模式,又是一种组织整理数据的手段.人们在通讯交流.科学研究以及数据分析活动当中广泛采用着形形色色的表格.那么如何通过Java应用程序创建Word表格呢?别担心,本文将 ...

  2. 使用KVM安装windows10系统出现内存直接占满的情况解决

    情况说明: 在使用kvm安装windows10系统的时候,采用的win10系统不是原版系统,而是经过进一步封装的系统,使用大白菜PE先格式化磁盘,然后再安装的系统,在系统安装好重启的时候,卡在安装界面 ...

  3. nginx+gunicorn部署Django项目

    实际采用的nginx.conf文件内容: server { charset utf-8; listen 80; server_name ip; access_log /webapps/project/ ...

  4. MySQL数据库-数据表(上)

    数据表的基本操作. MySQL 数据库支持多种数据类型,大致可以分为 3 类:数值类型.日期和时间类型.字符串(字符)类型. (1)数值类型 数值类型用于存储数字型数据,这些类型包括整数类型(TINY ...

  5. 死锁与Lock锁

    死锁1.死锁的理解:不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁 2.说明: 1)出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞 ...

  6. 【算法】基础DP

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

  7. 一天五道Java面试题----第十天(简述Redis事务实现--------->负载均衡算法、类型)

    这里是参考B站上的大佬做的面试题笔记.大家也可以去看视频讲解!!! 文章目录 1.简述Redis事务实现 2.redis集群方案 3.redis主从复制的核心原理 4.CAP理论,BASE理论 5.负 ...

  8. 齐博x1频道的二次开发入门讲解

    要进行频道的二次开发,首先我们要先了解一下频道的目录结构,如下图最基本的几个目录admin 后台文件存放目录index 前台文件存放目录member 会员中心存放目录model 数据表模型目录trai ...

  9. 39.BasicAuthentication认证

    BasicAuthentication认证介绍 BasicAuthentication使用HTTP基本的认证机制 通过用户名/密码的方式验证,通常用于测试工作,尽量不要线上使用 用户名和密码必须在HT ...

  10. Eureka Server 实现在线扩容

    Eureka Server 实现在线扩容 作者:Grey 原文地址: 博客园:Eureka Server 实现在线扩容 CSDN:Eureka Server 实现在线扩容 需求 Eureka 是 Sp ...