JS高阶 - day03

一、编程思想

1.1 面向过程编程

面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次 调用就可以了。

简单来说:面向过程,就是按照我们分析好了的步骤,按照步骤解决问题。

1.2 面向对象编程

面向对象编程(oop)是把事务分解成为一个个对象,然后由对象之间分工与合作。

简单来说:面向对象是以对象功能来划分问题,而不是步骤。

面向对象的特性: 封装性,继承性,多态性

好处:

        在面向对象程序开发思想中,每一个对象都是功能中心,具有明确分工。

        面向对象编程具有灵活、代码可复用、容易维护和开发的优点,更适合多人合作的大型软件项目。

 1.3 两者对比

面向过程编程

        优点:

                性能比面向对象高,适合跟硬件联系很紧密 的东西,例如单片机就采用的面向过程编程。

        缺点:

                没有面向对象易维护、易复用、易扩

面向对象编程

        优点:

                易维护、易复用、易扩展,由于面向对象有封装 、继承、多态性的特性,可以设计出低耦合的系统,使 系统更加灵活、更加易于维护

        缺点:

                性能比面向过程低

注:

        前端面向过程更多

二、构造函数

作用:

        构造函数体现了面向对象的封装特性。

        构造函数实例创建的对象彼此独立、互不影响

缺点:

        存在浪费内存的问题

        需要借助原型对象来使所有的对象使用同一个函数以此来节省内存

  function Star (uname,age){
    this.uname = uname
    this.age = age
    this.sing = function (){
      console.log('唱歌')
    }
  }

  const ldh = new Star('刘德华',55)
  const zxy = new Star('张学友',58)
  因为新建一个对象会在栈里面新建一个地址,通过地址指向堆里面新建的sing function,
  但是sing function,会多占用内存
  console.log(ldh.sing() === zxy.sing()) // false

三、原型

3.1 原型对象

作用:利用原型对象实现方法共享

原理:

        构造函数通过原型分配的函数是所有对象所共享的

        JavaScript 规定,每一个构造函数都有一个prototype 属性,指向另一个对象,所以我们也称为原型对象

        这个对象可以挂载函数,对象实例化不会多次创建原型上函数,节约内存

        我们可以把那些不变的方法,直接定义在prototype 对象上,这样所有对象的实例就可以共享这些方法。

        构造函数和原型对象中的this 都指向实例化的对象

  // 原型是一个对象,我们也称为 prototype为原型对象
  function Star (uname,age){
    this.uname = uname
    this.age = age
  }
  // 公共的方法写到原型对象上
  // 这样不管创建几次对象,都是指向的构造函数Star的sing方法
  Star.prototype.sing = function (){
    console.log('唱歌')
  }
  const ldh = new Star('刘德华',55)
  const zxy = new Star('张学友',58)
  console.log(ldh.sing() === zxy.sing()) // true

3.2 constructor属性

每个原型对象(prototype)里面都有个constructor属性(constructor 构造函数)

作用:

       constructor 指向该原型对象的构造函数

  function Star (uname,age){
    this.uname = uname
    this.age = age
    this.sing = function (){
      console.log('唱歌')
    }
  }  
  // 原型对象的constructor 属性 每个原型对象里面抖音,该属性指向对象的构造函数
  // 作用 指向原型对象的构造函数
  console.log(Star.prototype.constructor);// Star
  console.log(Star.prototype.constructor === Star)// true

使用场景:

        如果有多个对象的方法,我们可以给原型对象采取对象形式赋值. 但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象constructor 就不再指向当前构造函数了 此时,我们可以在修改后的原型对象中,添加一个constructor 指向原来的构造函数。

function Person(){
    this.eyes = 2
    this.head = 1
}
function Teacher(){

}
// Teacher 通过原型来继承Person
Teacher.prototype = new Person() // 不同的对象
// 因为进行了赋值覆盖 还需要指回来原来的构造函数
Teacher.prototype.constructor = Teacher

3.3 对象原型

对象都会有一个属性__proto__ 指向构造函数的prototype 原型对象。

__proto__对象原型里面也有一个constructor属性,指向创建该实例对象的构造函数

注:

        __proto__ 是JS非标准属性

        [[prototype]]和__proto__意义相同

        用来表明当前实例对象指向哪个原型对象prototype  

  // 实例对象里面有对象原型: __proto__ 指向构造函数的prototype原型对象
  // __proto__ 是对象的一个属性
  //是JS非标准属性 [[prototype]] 和__proto__意义相同
  // __proto__对象原型里面也有一个constructor属性,指向创建该实例对象的构造函数
  // 是只读属性
  function Star() {}
  const ldh = new Star()
  console.log(ldh.__proto__)
  console.log(ldh.__proto__ === Star.prototype)//true
  console.log('------------------------')
  console.log(ldh.__proto__.constructor)
  console.log(ldh.__proto__.constructor === Star)//true

3.4 原型继承

JavaScript 中大多是借助原型对象实现继承的特性。

1. 封装-抽取公共部分

2. 继承-让老师和学生都能继承人的一些属性和方法、

3.老师和学生不要使用同一个对象,但是不同对象里面包含相同的属性和方法(构造函数)

注:

        老师和学生都同时使用了同一个对象,根据引用类型的特点,他们指向同一个对象,修改一个就会都影响

总结:

        人共有的属性和方法有那些,然后做个构造函数,进行封装,一般公共属性写到构造函数内部,公共方法,挂载 到构造函数原型身上。

        老师做成构造函数继承人的属性和方法,之后创建自己独有的属性和方法

        学生同理

// 父构造函数(父类)// 公共属性和方法    子构造函数(子类)继承父类并具有独特的属性和方法
// 子类的原型 = new 父类
// 子类的原型的constructor = 子类
//(可以选择给子类的(原型对象)prototype添加独特的方法)
//......................
// 对象在new来进行实例化子类

// 封装成构造函数的形式,new出来的对象 结构一样,但是对象不一样
function Person(){
    this.eyes = 2
    this.head = 1
}
// 老师构造函数 继承 Person
function Teacher(){

}
// Teacher 通过原型来继承Person
Teacher.prototype = new Person() // 不同的对象
// 因为进行了赋值覆盖 还需要指回来原来的构造函数
Teacher.prototype.constructor = Teacher
// 给老师添加一个方法 讲课
Teacher.prototype.teaching = function (){
    console.log('老师讲课')
}
// 会出现一个问题  因为老师和学生的prototype 都是指向的Person
// 给老师的prototype添加方法就是修改Person,
// 这样会导致同样指向Person的Student的prototype也会被修改
// 解决办法,给他们的prototype 指向的对象不一样但是要指向的内容一样
//         就把之前抽取的对象,改成抽取成为构造函数就是,这样 new一个就是一个新的对象
const teacher = new Teacher()
console.log(teacher.__proto__)
// 学生构造函数 继承Person
function Student(){

}
// Student 通过原型来继承Person
Student.prototype = new Person() // 不同的对象
// 因为进行了赋值覆盖 还需要指回来原来的构造函数
Student.prototype.constructor = Student

const student = new Student()
console.log(student)

3.5 原型链

原型链:

JS高阶 - day03_第1张图片

总结:(为了一眼明目)

        只要是构造函数就有属性 原型对象(prototype)

        通过new来创建构造函数对应的实例对象

        只要是对象(实例对象,原型对象(prototype)等)就有属性对象原型(__proto__)指向原型链上一级的原型对象(prototype)

        原型对象(prototype)和对象原型(__proto__)都有属性constructor指向对应的构造函数

        万物皆对象,Object的原型对象(prototype)的对象原型(__proto__)指向null

定义:

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

原型链查找规则:

        1. 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。

        2. 如果没有就查找它的原型(也就是__proto__指向的prototype 原型对象)

        3. 如果还没有就查找原型对象的原型(Object的原型对象)

        4. 依此类推一直找到Object 为止(null)

        5. __proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线

注:

        可以使用 instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上

// 检查是否出现在其原型链上 instanceof 子 instanceof 父
function Star (uname,age){
    this.uname = uname
    this.age = age
}
const ldh = new Star('刘德华',55)
console.log(ldh instanceof Star) // true
console.log(ldh instanceof Object)// true
//console.log(Star instanceof ldh)// 报错
console.log(ldh instanceof Array)// false
console.log([1,2,3] instanceof Array)// true
// 最终的一切的一切,万物皆对象
console.log(Array instanceof Object)// true

你可能感兴趣的:(JS,javascript,开发语言,ecmascript)