美文网首页饥人谷技术博客
JS中的面向对象程序设计初识

JS中的面向对象程序设计初识

作者: EnochQin | 来源:发表于2018-08-07 21:01 被阅读6次

前言:面向对象程序设计(Object-oriented programming,简称OOP),是一种常见的编程思想。JavaScript 的核心是支持面向对象的,同时它也提供了强大灵活的 OOP 语言能力。本文从对面向对象编程的介绍开始,和您一起探索 JavaScript 的对象模型,最后描述 JavaScript 当中面向对象编程的一些概念。(本文部分文字摘自知乎MDN阮一峰JS教程


1、什么是面向对象

  • 比较正经的回答:

把一组数据结构和处理它们的方法组成对象(object),把相同行为的对象归纳为(class),通过类的封装(encapsulation)隐藏内部细节,通过继承(inheritance)实现类的特化(specialization)/泛化(generalization),通过多态(polymorphism)实现基于对象类型的动态分派(dynamic dispatch)。

  • 抖机灵的回答:


  • MDN中的介绍:

面向对象编程是用抽象方式创建基于现实世界模型的一种编程模式。它使用先前建立的范例,包括模块化,多态和封装几种技术。
面向对象编程可以看作是使用一系列对象相互协作的软件设计。 在 OOP 中,每个对象能够接收消息,处理数据和发送消息给其他对象。每个对象都可以被看作是一个拥有清晰角色或责任的独立小机器。

  • 可尝试理解MDN中介绍的面向对象的一些术语

Class 类
定义对象的特征。它是对象的属性和方法的模板定义.
Object 对象
类的一个实例。
Property 属性
对象的特征,比如颜色。
Method 方法
对象的能力,比如行走。
Constructor 构造函数
对象初始化的瞬间, 被调用的方法. 通常它的名字与包含它的类一致.
Inheritance 继承
一个类可以继承另一个类的特征。
Encapsulation 封装
一种把数据和相关的方法绑定在一起使用的方法.
Abstraction 抽象
结合复杂的继承,方法,属性的对象能够模拟现实的模型。
Polymorphism 多态
多意为‘许多’,态意为‘形态’。不同类可以定义相同的方法或属性。

其实面向对象最核心的四个概念就是:抽象、继承、封装、多态

2、JavaScript中的面向对象编程

首先我们就要来了解一下原型编程,因为JS中实际上是弱类化的编程语言,是基于原型的而不是基于类的编程。

  • 基于原型的编程不是面向对象编程中体现的风格,且行为重用(在基于类的语言中也称为继承)是通过装饰它作为原型的现有对象的过程实现的。这种模式也被称为弱类化,原型化,或基于实例的编程。
  • 原始的(也是最典型的)基于原型语言的例子是由大卫·安格尔和兰德尔·史密斯开发的。然而,弱类化的编程风格近来变得越来越流行,并已被诸如JavaScript,Cecil,NewtonScript,IO,MOO,REBOL,Kevo,Squeak(使用框架操纵Morphic组件),和其他几种编程语言采用。

完整的内容可看MDN 关于JS 面向对象的介绍。本文仅做基础的介绍。

3、命名空间

命名空间是一个容器,它允许开发人员在一个独特的,特定于应用程序的名称下捆绑所有的功能。 在JavaScript中,命名空间只是另一个包含方法,属性,对象的对象。

注意:需要认识到重要的一点是:与其他面向对象编程语言不同的是,Javascript中的普通对象和命名空间在语言层面上没有区别。比如var MySpace = {},从语言层面上,可以理解成创建了一个名为MySpace的对象,也可以理解为一个名为MySpace的命名空间,到底应该理解成哪一种主要是看你如何使用它。

创造的JavaScript命名空间背后的想法很简单:一个全局对象被创建,所有的变量,方法和功能成为该对象的属性。使用命名空间也最大程度地减少应用程序的名称冲突的可能性。

举例说明:
我们来创建一个全局变量叫做 MYAPP
var MYAPP = MYAPP || {};
在上面的代码示例中,我们首先检查MYAPP是否已经被定义(是否在同一文件中或在另一文件)。如果是的话,那么使用现有的MYAPP全局对象,否则,创建一个名为MYAPP的空对象用来封装方法,函数,变量和对象。

然后我们就可以创建子命名空间:
MYAPP.event = {};

4、类

JavaScript是一种基于原型的语言,它没类的声明语句,比如C+ +或Java中用的。相反,JavaScript可用方法作类。定义一个类跟定义一个函数一样简单。在下面的例子中,我们定义了一个新类Person。

function Person() { } 
// 或
var Person = function(){ }

对象(类的实例)
我们使用 new obj 创建对象obj 的新实例, 将结果(obj 类型)赋值给一个变量方便稍后调用。举例说明:
在下面的示例中,我们定义了一个名为Person的类,然后我们创建了两个Person的实例(person1person2).

function Person() { }
var person1 = new Person();
var person2 = new Person();

5、构造函数

面向对象编程的第一步,就是要生成对象。前面说过,对象是单个实物的抽象。通常需要一个模板,表示某一类实物的共同特征,然后对象根据这个模板生成。

典型的面向对象编程语言(比如 C++ 和 Java),都有“类”(class)这个概念。所谓“类”就是对象的模板,对象就是“类”的实例。但是,JavaScript 语言的对象体系,不是基于“类”的,而是基于构造函数(constructor)和原型链(prototype)

JavaScript 语言使用构造函数(constructor)作为对象的模板。所谓”构造函数”,就是专门用来生成实例对象的函数。它就是对象的模板,描述实例对象的基本结构。一个构造函数,可以生成多个实例对象,这些实例对象都有相同的结构。

构造函数就是一个普通的函数,但是有自己的特征和用法。如:

var Vehicle = function () {
  this.price = 1000;
};

上面代码中,Vehicle就是构造函数。为了与普通函数区别,构造函数名字的第一个字母通常大写

构造函数的特点有两个:

  • 函数体内部使用了this关键字,代表了所要生成的对象实例。
  • 生成对象的时候,必须使用new命令。

下面将详细介绍一下new命令和this关键字。

6、new 命令

写之前先放一个链接,是方大写的关于new的文章,私以为能解决很多人关于new的疑惑。

new命令的作用,就是执行构造函数,返回一个实例对象。如:

var Vehicle = function () {
  this.price = 1000;
};
var v = new Vehicle();
v.price // 1000

上面代码通过new命令,让构造函数Vehicle生成一个实例对象,保存在变量v中。这个新生成的实例对象,从构造函数Vehicle得到了price属性。new命令执行时,构造函数内部的this,就代表了新生成的实例对象,this.price表示实例对象有一个price属性,值是1000。

var v = new Vehicle();使用new,JS默默的做了哪些事情呢:

  • 创建临时对象
  • 临时对象.__proto__ = Vehicle.prototype
  • return 临时对象
  • Viehicle.prototype = { constructor: 构造函数 }
  • 将这个临时对象赋值给函数内部的this关键字。
来自方方的示意图

7、this 关键字

关于this关键字,我之前写的一篇关于函数的博客初步的介绍过,本文将再次详细的了解一下这个看起来有点坑的事物。(这里也推荐一篇方大写的介绍this的博客 ,下面的内容也借鉴了该博客所说的思路)

  • 什么是this,最本质的概念是:this就是你 call 一个函数时,传入的第一个参数。
  • 怎么判断this到底指的是什么,将函数的调用形式转换为 call 形式即可。
  • 如果是一些封装过的函数(比如onclick()、addEventListener()等),如何判断this
    1. 看源码中对应的函数是怎么被 call 的(这是最靠谱的办法)
    2. 看文档
    3. console.log(this)
    4. 不要瞎猜,你猜不到的

举例说明:

  • 首先来看如何将普通的函数调用转换为call的形式:
func(p1, p2) // 等价于
func.call(undefined, p1, p2)

obj.child.method(p1, p2) // 等价于
obj.child.method.call(obj.child, p1, p2)
  • 下面看几个例子。
    例1:
function func(){
  console.log(this)
}
func() // 转化为下面的句子
func.call(undefined) // 可以简写为 func.call()

此时this即为undefined,但注意:
如果你的call传的第一个参数是 null 或者 undefined,那么 window 对象就是默认的this(严格模式下默认为 undefined

  • 例2:
var obj = {
  foo: function(){
    console.log(this)
  }
}
obj.foo() // 转化为下面的语句
obj.foo.call(obj)

本例中的this即为obj

  • 例3:
function fn (){ console.log(this) }
var arr = [fn, fn2]
arr[0]() // 这里面的 this 又是什么呢?

我们可以把 arr[0]( ) 想象为arr.0( ),虽然后者的语法错了,但是形式与转换代码里的 obj.child.method(p1, p2)对应上了,于是就可以愉快的转换了:

arr[0]() 
假想为    arr.0()
然后转换为 arr.0.call(arr)
那么里面的 this 就是 arr 了 :)
  • 例4:(下面三个例子都是一些常见的经过封装后的函数的this,这些函数就无法转化成call的形式了,需要看文档才能知道,我帮大家看了文档,所以下面的例子就需要单独记忆啦)
button.onclick = function(){
  console.log(this)
}
// 这里的 this 指的是 触发事件的元素 即 button
  • 例5:
button.addEventListener('click',function(){
  console.log(this)
})
// 这里的 this 指的是 触发事件的元素的引用 即 button
  • 例6:(jQuery)
$('ul').on('click','li',function(){
  console.log(this)
})
// 这里的 this 指的是 正在执行事件的元素 即 li
  • 例7:下面三个例子就比较绕啦
function X() {
  return obj = {
    name:'obj',
    fn1(x) {
        x.fn2()
      },
      fn2() {
        console.log(1)
        console.log(this) // A
      }
  }
}
var options = {
  name:'options',
  fn1() {},
    fn2() {
      console.log(2)
      console.log(this) // B
    }
}
var x = X()
x.fn1(options)

问:这段的代码执行的是A还是B,打印出来的this指的是什么(不公布答案,自己思考后可在控制台验证结果)

  • 例8:
function X() {
  return obj = {
    name:'obj',
    fn1(x) {
        x.fn2.call(this)
      },
      fn2() {
        console.log(1)
        console.log(this) // A
      }
  }
}
var options = {
  name:'options',
  fn1() {},
    fn2() {
      console.log(2)
      console.log(this) // B
    }
}
var x = X()
x.fn1(options)

问题同上。

  • 例9:
function X() {
  return obj = {
    name: 'obj',
    options: null,
    fn1(x) {
      this.options = x
      this.fn2()
    },
    fn2() {
      console.log(1)
      this.options.fn2.call(this) // A
    }
  }
}
var options = {
  name: 'options',
  fn1() {},
  fn2() {
    console.log(2)
    console.log(this) // B
  }
}
var x = X()
x.fn1(options)

问题同上。

8、做几个小练习吧

  1. 补全下面的代码:
function Human(options){

} // 构造函数结束

Human.prototype.______ = ___________
Human.prototype.______ = ___________
Human.prototype.______ = ___________

var human = new Human({name:'Frank', city: 'Hangzhou'})
var human2 = new Human({name:'Jack', city: 'Hangzhou'})
  • 补全代码,使得 human 对象满足以下条件:
    • human 这个对象本身具有属性 namecity
    • human.__proto__对应的对象(也就是原型)具有物种(species)、走(walk)和使用工具(useTools)这几个属性
    • human.__proto__.constructor === Human 为 true

human2 和 human 类似。

  • 参考答案:
function Human(options){
    this.name = options.name
    this.city = options.city

} // 构造函数结束

Human.prototype.species = 'Human'
Human.prototype.walk = function(){}
Human.prototype.useTools = function(){}

var human = new Human({name:'Frank', city: 'Hangzhou'})
var human2 = new Human({name:'Jack', city: 'Hangzhou'})
  1. 填空(本题不给答案,不确定的可以直接在控制台测试):
var object = {}
object.__proto__ ===  ????填空1????  // 为 true

var fn = function(){}
fn.__proto__ === ????填空2????  // 为 true
fn.__proto__.__proto__ === ????填空3???? // 为 true

var array = []
array.__proto__ === ????填空4???? // 为 true
array.__proto__.__proto__ === ????填空5???? // 为 true

Function.__proto__ === ????填空6???? // 为 true
Array.__proto__ === ????填空7???? // 为 true
Object.__proto__ === ????填空8???? // 为 true

true.__proto__ === ????填空9???? // 为 true

Function.prototype.__proto__ === ????填空10???? // 为 true
  1. 在 ES5 中如何用函数模拟一个类?
  • 参考答案:
    ES 5 没有 class 关键字,所以只能使用函数来模拟类。代码如下:
    function Human(name){
      this.name = name
    }
    Human.prototype.run = function(){}
    
    var person = new Human('enoch')
    
    

相关文章

  • JS中的面向对象程序设计初识

    前言:面向对象程序设计(Object-oriented programming,简称OOP),是一种常见的编程思想...

  • python面向对象!

    python全栈开发,初识面向对象 面向过程 VS 面向对象 面向过程的程序设计的核心是过程(流水线式思维),过程...

  • JavaScript原型与原型链

    一、概述 在 JavaScript 中,是一种面向对象的程序设计语言,但是 JS 本身是没有 “类” 的概念,JS...

  • 编程语言基础概念1

    什么是面向对象? 面向对象:指在程序设计中采用封装、继承、多态等设计方法。 什么是面向过程? 面向过程:程序设计按...

  • 扒一扒所谓的面向对象

    面向对象是很多高级程序程序设计语言的核心。面向对象的标志就是类、对象、继承、派生等。js严格意义上来讲并不是面向对...

  • 《软件工程》笔记7

    面向对象的实现 面向对象的程序设计最好还是选用面向对象的编程语言。 良好的程序设计风格对于面向对象实现来说格外重要...

  • 第四章 对象与类

    1 面向对象程序设计概述 面向对象程序设计(简称OOP)是当今主流的程序设计范型 Java是完全面向对象的,必须熟...

  • 《Java核心技术卷 I》之 Java对象与类

    Java对象与类 面向对象程序设计 面向对象程序设计,简称OOP,Java语言就是完全面向对象的。 类 类(cla...

  • JavaScript面向对象编程指南--读书笔记(上)

    第一章:面向对象的JavaScript 面向对象的程序设计 面向对象程序设计(oop)中最常用到的概念: 对象、方...

  • Copy和MutableCopy

    一、从面向对象到Objective-C概览copy 面向对象: 在面向对象的程序设计中,对象的copy就是创建一个...

网友评论

    本文标题:JS中的面向对象程序设计初识

    本文链接:https://www.haomeiwen.com/subject/rvrmvftx.html