面向对象

86 阅读6分钟

1.什么是对象

js中一切皆对象

对象是对单个事物的抽象

对象=属性+方法

是一个容器,封装了属性和方法

面向对象就是高度实物抽象化、面向过程就是自顶向下的编程

面向过程

      var std1 = {
    name: 'Bob',
    score: 90
}
var std2 = {
    name: 'Mike',
    score: 78
}

function printScore(student) {
    console.log('姓名' + student.name + '成绩' + student.score)
}
printScore(std1);
printScore(std2);

面向对象

构造函数

定义一个类的方式

在[JavaScript]中,用new关键字来调用的函数,称为构造函数,构造函数首字母一般大写

构造函数是根据具体食物的抽象的模板

实例对象是根据构造函数得到的具体的实例对象

(面试题)new在调用构造函数的时候发生了什么?

  • 创建一个新的对象

  • 将函数内部的this指向新对象

  • 执行构造函数内部的代码

  • 讲新的对象返回

构造函数中存在的问题

在构造函数中存在的一个不足:在构造函数中存在的方法他会在每次创建实例对象中的时候,复制一份这样会浪费内存name关键字,最好不用他去定义变量

    解决的方法:
    1.公共的方法提取到构造函数之外
    2.把多个公共函数封装到一个对象中
    

解决方案1:把公共的方法提取到构造函数之外

     function sayName() {
    console.log(this.myName)
}

function sayAge() {
    console.log(this.age)
}

function Person(name, age) {
    this.name = name
    this.age = age
    this.sayName = sayName
    this.sayAge = sayAge
}

解决方案2:把一个公共函数封装到一个对象中

var fns = {
    sayName: function() {
        console.log(this.myName)
    },
    sayAge: function() {
        console.log(this.sayAge)
    },

}

function Person(name, age) {
    this.name = name
    this.age = age
    this.sayName = fns.sayName
    this.sayAge = fns.sayAge
}

instanceof 判断一个对象的具体类型

  var arr = new Array()
console.log(arr)
console.log(arr instanceof Array)//true
console.log(person1 instanceof Array)//false

基本数据类型:typeof

 // 分析出来学生的类
// 定义一个学生类
// 构造函数:第一个字母是大写
function Student(name, score) {
    this.name = name
    this.score = score
    this.printScore = function() {
        console.log('姓名' + this.name + '成绩' + this.score)
    }
}

// 实例化一个学生的实体
var std1 = new Student('Bob', 90);
  // 实例化:类创建对象的过程
var std2 = new Student('Mike', 78);
// 调用对象中的方法
std1.printScore();
std2.printScore();

创建对象的方式

1.简单的方式 new Object()

  new person = new Object()

2.通过对象字面量的方式{}

var person1 = {
        myname: 'Bob',
        age: 20,
        sayName: function() {
            console.log(this.name)
        }
    }

3.工厂函数

function creatPerson(name, age) {
    var person = new Object()
    person.myname = name
    person.age = age
    person.sayName = function() {
        console.log(this.name)
    }
    return person
}

4.构造函数

function Student(name, score) {
    this.name = name
    this.score = score
    this.printScore = function() {
        console.log('姓名' + this.name + '成绩' + this.score)
    }
}



静态成员和实例成员

静态成员:属于构造函数自身的成员,只能使用构造函数调用

实例成员:属于对象的属性也就是在构造函数内部添加给this的属性,在创建实例对象后有对象调用

function Person(name, age) {
    // 实例成员:属于对象的属性也就是在构造函数内部添加给this的属性,在创建实例对象后有对象调用
    this.name = name
    this.age = age
    this.sayName = function() {
        console.log(this.name)
    }
}

1.实例成员就是构造函数内部通过this添加的成员 实例成员只能通过实例化的对象进行访问

2.静态成员是在 构造函数本身上添加的成员 静态成员只能通过构造函数访问 不能通过对象访问

 <script>
    function Star(uname, sex) {
        this.uname = uname;
        this.sex = sex;
        this.sing = function() {
            console.log("我会唱歌");
        }
    }
    var ldh = new Star("刘德华", "男");
    //1.实例成员就是构造函数内部通过this添加的成员 uname sex sing 就是实例成员
    //实例成员只能通过实例化对象来访问
    console.log(ldh.uname);//刘德华
    console.log(Star.uname); //不可以通过构造函数来访问实例成员 undefined
    //2.静态成员 在构造函数本身上添加的成员
    Star.age = '18'; //age 就是静态成员
    //静态成员只能通过构造函数来访问
    console.log(Star.age); //18
    console.log(ldh.age); //不能通过对象访问 undefined
</script>

构造函数的原型

1.原型(对象):prototype对象 所有的对象都有一个__proto__属性,指向创建改对象的原型对象

实例对象通过p.__proto__可以调用原型对象中的方法

实例对象跳过p.__proto__直接调用原型对象中的方法在实际开发中用这个

function Person(name,age){
  this.myName = name
  this.age = age
}

// console.dir(Person)
// console.log(Person.prototype)

Person.prototype.sayName = function (){
  console.log(this)
  console.log("sayName")
}

Person.prototype.sayHi = function (){
  console.log('sayHi')
}

// console.dir(Person)

// 验证方法是同一个
// var p1 = new Person('Bob',18)
// var p2 = new Person('Mike',20)
// console.log(p1.sayName === p2.sayName)

// 探究原型对象
var p = new Person('Bob',19)
// console.log(p)
// 所有的对象都有一个__proto__属性,它也指向创建该对象的构造函数的原型
// console.log(p.__proto__)

// 验证所有的对象都有一个__proto__属性,它也指向创建该对象的构造函数的原型和构造函数的原型对象是同一个
// console.log(p.__proto__ === Person.prototype) // true


// 实例对象通过__proto__可以调用原型对象中的方法和属性
console.log(p.__proto__)
p.__proto__.sayName();

// 实例对象跳过__proto__,直接调用原型对象中的方法,在实际开发应用中用这个
// p.sayName()

// 通过p实例对象调用constructor可以得到创建该对象的构造函数
// console.log(p.constructor);

原型链

原型对象向上溯源得到的一系列原型对象构成的链条 js对象访问属性或者方法的方式:沿着原型链的结点寻找,直到找到null为止。如果在某一个结点找到了,那么就输出,并且停止往后寻找;如果找到null还没有找到相应的属性或者方法,那么就是undefined

function Person(name,age) {
    this.myName = name
    this.age = age
}

Person.prototype.type = 'human'
Person.prototype.sayName = function (){
    console.log(this.myName)
}

var person1 = new Person("Bob",18)

// console.log(person1)
var personPrototype = person1.__proto__
var prototypeofPersonPrototype = personPrototype.__proto__

// console.log(prototypeofPersonPrototype.constructor)

// 输出prototypeofPersonPrototype的原型对象
// console.log(prototypeofPersonPrototype.__proto__)

// person1.sayName()

// valueOf()也可以用来获取该对象自身
console.log(person1.valueOf());
    



image.png

实例对象读取原型对象上的属性和方法

function Person(name,age){
  this.myName = name
  this.age = age
}

Person.prototype.type = 'human'
Person.prototype.sayName = function (){
  console.log(this.name)
}
Person.prototype.address = {
  city:'福州'
}



var person1 = new Person("Bob",19)
var person2 = new Person("Mike",20)

// 读取属性和方法
console.log(person1.myName)
console.log(person1.type)
console.log(person1.address)
person1.sayName()

// 1、通过实例对象添加新成员,只会直接添加给自己
person1.sex = 'male'
person1.sayAge = function (){
  console.log(this.age)
}

console.dir(person1)

// 2、如果通过实例对象想修改原型对象上的属性或者方法,那么还是会把属性或者方法添加到实例对象自身,
// 这时如果通过实例对象再去访问属性和方法,那么实际访问的就是实例对象自己添加的属性和方法
person1.type = "person"

console.dir(person1)
console.log(person1.type)

// 3、通过实例对象修改原型对象上的复杂类型(对象)的数据中的属性的值,
// 那么还是会沿着原型链寻找,而不会给实例对象自身添加属性,并且修改相应的属性的值
person1.address.city = '上海'
console.dir(person1)

简化版的原型语法

//原来的
function Person(name,age){
    this.myName = name
    this.age = age
}

// Person.prototype.type = 'human'
// Person.prototype.sayName = function (){
//     console.log(this.myName);// webstorm  this.myName.log  + tab快速输出console.log()  。vscode下怎么快速输出?
// }

// 简化版
// 注意:{} 是新的对象
Person.prototype = {
    constructor: Person,// 在这里要加上constructor属性,并且把它指向实例对象真正的构造
    type:'human',
    sayName:function (){
        console.log(this.myName);
    }
}

var person1 = new Person("Bob",18)
// person1.sayName()
// console.dir(person1)
// console.log(person1.__proto__)
console.log(Person.prototype.constructor)

javascript常见内置构造函数

Object()

Function()

Array() 实例化数组对象的

String() 实例化字符串对象

Number() 实例化数字对象

  // 1、内置构造函数的原型对象
      console.dir(Object.prototype)
      console.dir(Function.prototype)
      console.log(Array.prototype); // 重要!!!特别是原型对象中的方法
      console.log(String.prototype); // 重要!!!特别是原型对象中的方法
      console.log(Number.prototype); // 5.789387020.toFixed(2) 保留两位小数

需求:获取一个数组中偶数项的和

 var arr = new Array(2, 4, 5, 7, 9)

      // 实现方式一:for循环
      var sum = 0
      for (var i = 0; i < arr.length; i++) {
          if (i % 2 === 0) {
              var item = arr[i]
              sum = sum + item
          }
      }

      console.log(sum)

      //实现方式二:扩展Array原型对象上的方法
      // 因为javasript代码是保护起来的。内置的构造函数是不允许修改的,无法修改Array构造函数的原型对象的指向的
      //错误-------
      Array.prototype = {
          constructor: Array,
          getEvenSum: function() {
              console.log(this)
              var sum = 0
              for (var i = 0; i < this.length; i++) {
                  if (i % 2 === 0) {
                      var item = this[i]
                      sum = sum + item
                  }
              }

              return sum
          }
      }
-------end
      Array.prototype.getEvenSum = function() {
          console.log(this)
          var sum = 0
          for (var i = 0; i < this.length; i++) {
              if (i % 2 === 0) {
                  var item = this[i]
                  sum = sum + item
              }
          }

          return sum
      }
      
      var sum = arr.getEvenSum()
      console.log(sum)