JS高级--面向对象&创建对象的几种方式&构造函数&定义函数的三种方式

120 阅读6分钟

一、面向对象编程

基本概念

所谓面向对象就是指:你想做一件事情,你自己做不到,那么就去找到能够实现这个功能的对象,调用它的方法(传递参数,遵守方法的规则)

什么是对象?

Everything is object (万物皆对象) 对象到底是什么,我们可以从两次层次来理解。

(1) 对象是具体事物的抽象。

一本书、一辆汽车、一个人都可以是对象,当实物被抽象成对象,实物之间的关系就变成了对象之间的关系,从而就可以模拟现实情况,针对对象进行编程。

问: 书是对象吗

(2)对象是无序键值对的集合,其属性可以包含基本值、对象或者函数

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

什么是面向对象?

面向对象编程 —— Object Oriented Programming,简称 OOP ,是一种编程开发思想。

在面向对象程序开发思想中,每一个对象都是功能中心,具有明确分工,可以完成接受信息、处理数据、发出信息等任务。 因此,面向对象编程具有灵活、代码可复用、高度模块化等特点,容易维护和开发,比起由一系列函数或指令组成的传统的过程式编程(procedural programming),更适合多人合作的大型软件项目。

面向对象与面向过程:

  • 面向过程就是亲历亲为,事无巨细,有条不紊,面向过程是解决问题的一种思维方式,(执行者的角度)

    • 关注点在于解决问题的过程(先xx,然后xx,在xx);
  • 面向对象就是找一个对象,让她去做这件事情(指挥者的角度)

    • 关注点在找到能解决问题的对象上。
  • 面向对象不是面向过程的替代,而是面向过程的封装

  • 例如洗衣服(面向过程和面向对象的区别)

面向对象的特性:

  • 封装性

    • 将功能的具体实现,全部封装到对象的内部,外界使用对象时,只需要关注对象提供的方法如何使用,而不需要关心对象的内部具体实现,这就是封装。
  • 继承性

    • 在js中,继承的概念很简单,一个对象没有的一些属性和方法,另外一个对象有,拿过来用,就实现了继承。
    • 注意:在其他语言里面,继承是类与类之间的关系,在js中,是对象与对象之间的关系。
  • [多态性]

    • 多态是在强类型的语言中才有的。js是弱类型语言,所以JS不支持多态

4.栈和堆

(stack)中主要存放一些基本类型的变量和对象的引用其优势是存取速度比堆要快,但缺点是存在栈中的数据大小与生存期必须是确定的,缺乏灵活性,

(heap 多)用于复杂数据类型(引用类型)分配空间,例如数组对象、object对象;它是运行时动态分配内存的,因此存取速度较慢。

栈和堆的图例

image-20200713144353620.png

  • 值类型:简单类型,变量在存储的时候,存储的是值本身。如果做为参数传递,仅仅是将栈空间中存储的内容复制一份进行赋值,修改其中一个变量另外不会变化
  • 引用类型:复杂类型,变量在存储的时候,存储的是对象的地址,如果做为参数传递,是将栈空间中存储的引用地址复制一份进行赋值,造成实参和形参指向同一个空间,修改其中一个另外一个也会变化

二、创建对象的几种方式

内置构造函数创建

我们可以直接通过 new Object() 创建:

//在js中,对象有动态特性,可以随时的给一个对象增加属性或者删除属性。
var person = new Object()
person.name = 'Jack'
person.age = 18person.sayName = function () {
  console.log(this.name)
}

缺点:麻烦,每个属性都需要添加。

对象字面量创建

var person = {
  name: 'Jack',
  age: 18,
  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)

缺点:但却没有解决对象识别的问题,创建出来的对象都是Object类型的。

继续改进:构造函数

一:什么是构造函数
1.它是一个函数
2.它可以定义形参,接收实参
3.它的作用主要是进行对象的初始化:初始化就是为对象添加成员
4.在js中,定义一个构造函数,就相当于自己定义了一种类型
二:构造函数的语法
1.定义起来和普通函数差不多
2.它的首字母一般是大写
三:构造函数的调用
1.要配合new关键字调用构造函数,没有new就相当于调用普通函数
2.它会返回当前创建的对象
四:new到底做了什么事情--面试常问
1.创建一个对象
2.将对象的引用赋值给构造函数中的this
3.调用构造函数为this添加成员
4.this的引用返回
构造函数中的this指向调用这个构造函数时new所创建的对象
构造函数是一个函数,用于实例化对象,需要配合new操作符使用。
function Person (name, age) {
  this.name = name
  this.age = age
  this.sayName = function () {
    console.log(this.name)
  }
}
​
var p1 = new Person('Jack', 18)
p1.sayName() // => Jackvar p2 = new Person('Mike', 23)
p2.sayName() // => Mike

而要创建 Person 实例,则必须使用 new 操作符。 以这种方式调用构造函数会经历以下 4 个步骤:

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

构造函数需要配合new操作符使用才有意义,构造函数首字母一般为大写

构造函数的缺点

使用构造函数带来的最大的好处就是创建对象更方便了,但是其本身也存在一个浪费内存的问题:

function Person (name, age) {
  this.name = name
  this.age = age
  this.type = 'human'
  this.say = function () {
    console.log('hello ' + this.name)
  }
}
​
var person1 = new Person('lpz', 18)
var person2 = new Person('Jack', 16)
console.log(person1.say === person2.say) // => false

解决方案:

图示

image-20200713150001114.png

解决:提取同一个 say 方法

  1. 解决了浪费内存的弊端
  2. 但是造成了 污染全局变量 的问题
    // 提前将say 声明好
    function say() {
      console.log(this.name);
    }
    function createStudent(name, age) {
      this.name = name;
      this.age = age;
      this.say = say
    }
​
    const obj = new createStudent("悟能", 83);
    const obj1 = new createStudent("悟能1", 84);
​
    console.log(obj.say === obj1.say); // true 

图示

image-20200713150551251.png

定义函数的三种方式

  1. 函数声明
  2. 函数表达式
  3. 构造函数Function

函数声明

常规的方式

fn();//函数声明可以先调用,在声明
function fn(参数..){
  console.log("这是函数声明")
  return 返回值
}

函数表达式

const fn = function() {
  console.log("这是函数表达式");  
}
fn();//函数表达式必须先声明,再调用

构造函数Function

所有函数都可以通过Function构造函数来创建

函数也可以看成对象

// new Function([前面是参数列表,]最后一个参数是函数体)
var fn1 = new Function("a1", "a2", "alert(a1+a2)");
fn1(1,2);