JavaScript 对象、面向对象、类(笔记)

1. 什么是对象

  • 万物皆是对象,对象有属性和方法,拿人来举例:人的姓名、年龄、性别等这些都是属性,属性可以通过点属性的形式去获取,人可以吃东西、走路、唱歌等这些都是方法,方法可以通过点方法括号的形式去调用
  • 举例来说
    • 操作浏览器要使用window对象
    • 操作网页要使用document对象
    • 操作控制台要使用console对象
  • JS 中,对象是一组无序的相关属性和方法的集合
    • 对象是由属性和方法组成的
    • 属性:事物的特征,在对象中用属性来表示(常用名词)
    • 方法:事物的行为,在对象中用方法来表示(常用动词)

2. 创建对象的三种方式

  • 字面量创建

    var obj = {}
    
  • 内置构造函数创建

    // 一般情况new关键字不能省略
    var obj = new Object()
    
    // 1.如果括号中传入的是null或undefined,将会创建并返回一个空对象
    // 2.如果传进去的是一个基本类型的值,则会对这个基本数据类型进行包装
    // 3.如果是引用数据类型,仍然会返回这个类型的值
    
  • 工厂函数

    // 第一种
    // 采用内置构造函数封装
    function getPreson(name, age) {
      var temp = new Object()
      temp.name = name
      temp.age = age
      return temp
    }
    getPreson("小红", 18)
    getPreson("小白", 19)
    
    // 第二种
    // 自定义构造函数
    function Person(name, age) {
      this.name = name
      this.age = age
    }
    var person1 = new Person("小红", 18)
    var person2 = new Person("小白", 19)
    

3. this

window上的属性和方法默认不用window点去寻找,可以直接使用

  • 全局作用域中 this 默认指向 window
  • 在函数中的谁调用该函数,this 就指向谁(谁点他就指向谁)
  • 在执行上下文中,this 指向当前上下文
  • 定时器和 Function构造处来的函数中的 this 默认指向 window

4. 对象的增删改查

// !!访问对象中没有的属性会得到undefined
var obj = {}

// 增
obj.name = "小红"
obj.age = 18

// 删,删除成功后会返回布尔值(即使没有删除到也会返回true)
delete obj.name

// 改
obj.age = 20

// 查,也可以查询原型上的属性
// 注意:如果对象上没有该属性,会返回undefined,并不会报错
obj.age

// 其他查询方式,都是返回布尔值
// 1.in:查询自己或原型上是否有该属性
"age" in obj
// 2.hasOwnProperty():查询自己是否有该属性
obj.hasOwnProperty("age")
// 3.propertyIsEnumerable():查询自己是否有该属性,并且要可枚举的属性
obj.propertyIsEnumerable("age")

5. new做了什么事情

  1. 在内存中创建了一个新的空对象
  2. 让这个空对象的__proto__指向构造函数的原型对象
  3. 让构造函数的 this 指向这个新的对象
  4. 执行构造函数里面的代码,给这个新对象添加属性和方法
  5. 如果构造函数中没有return,那么就返回这个新对象,如果有return,并且返回的为引用数据类型,那么就返回引用数据类型

6. 枚举对象

// 键值对:键(key)、值(value)

// 数组关联法:obj["key"] = value 
// 数组也可以for……in遍历
// for……in是遍历的键或索引(返回的字符串)
for (let i in obj) { // 也会遍历继承的属性
  // 遍历后的键值
  // i:获取对象obj的键
  // obj[i]:获取对象obj的值
}
// 只枚举自身可枚举的属性,返回对应的key或value存放数组中
Object.keys(obj)
Object.values(obj)

7. 对象常用API

静态方法作用返回值
1. create(obj)使返回值的值,继承括号中的值要被继承的对象
2. assign(A,B…)将一个或多个可枚举属性分配给A,如果B和A有相同属性,A的会被覆盖。A会改变,B和B之后的不会A对象
3. defineProperty(obj, prop, descriptor)给一个对象添加属性和约束obj:对象,prop:属性,descriptor:约束原对象
4. entries(obj)把自身可枚举的key和value存放到数组中数组
5. freeze(obj)冻结一个对象,使该对象不能增、删、改,只能查询该对象
6. keys(obj)把自身可枚举的key存放到数组中数组
7. values(obj)把自身可枚举的value存放到数组中数组

8. 面向对象的三大特征

  • 封装:对相同对象的属性和方法进行封装,成为一个类
  • 继承:子类可以继承父类的属性和方法
  • 多态:相当于子类对父类方法的重写,可以使用父类的,也可以自己重写

9. 原型与继承

9.1 为什么要有原型

因为如果把方法全部写在构造函数中,那么每次 new 的时候都会新创建一个方法,特别的浪费空间,可以通过原型把方法添加到原型上,这样每次 new 的时候这些实例都会继承原型上的方法,就可以公用一个方法了

9.2 原型对象之间的关系
function Person(name, age) {
  this.name = name
  this.age = age
  this.say = function () {
    console.log("你好!")
  }
}

let p1 = new Person("小红", 18)

// 实例也叫实例对象
// 实例通过隐式原型查找原型对象
p1.__proto__ => 原型对象

// 构造函数通过显式原型查找原型对象
Person.prototype => 原型对象

// 实例通过构造器指向构造函数也就是Person
p1.constructor => 构造函数
// 这一点要注意构造器是原型上的属性,实例可以使用原型上面的属性
// 所以说:
Person.prototype.constructor => 构造函数
// 或者
p1.__proto__.constructor => 构造函数
9.3 继承
9.3.1 基本概念
概念其他语言JavaScript
类 class模板构造函数,类名就是函数名
子类 subclass派生的模板原型设置为指定对象的构造函数
实例 instance某个类的对象构造函数new的对象
实例成员(方法,属性)对象的方法和属性对象的方法和属性
静态成员(方法,属性)类上的方法和属性函数上的方法和属性
9.3.2 属性访问原则
  1. 对象在调用方法或访问属性的时候,首先在当前对象中查询,如果有该成员直接使用并停止查找
  2. 如果没有该成员,就会在其原型对象中查找,如果有该成员则使用
  3. 如果还没有就会一直向上层查找,最后会查到Object.prototype上,如果没有就返回undefined
9.3.3 实现继承的几种方法
  1. 原型式继承

    // 1.继承原型对象的实例
    // 这种继承直接在原型上添加一个对象,但是会改变构造函数
    // 需要在对象中把构造器,指向需要new的构造函数
    function Person() { }
    
    Person.prototype = {
      constructor: Person,
      name: "小红",
      age: 18
    }
    
    let p1 = new Person()
    console.log(p1); // 名为Person的实例
    console.log(p1.constructor); // Person构造函数
    
    //2.继承实例
    function Person(name, age) {
      this.name = name
      this.age = age
    }
    
    function Student(gender) {
      this.gender = gender
    }
    
    // 这里Student的constructor也是Person
    Student.prototype = new Person("小红", 18)
    
    let stud = new Student("男")
    console.log(stud);
    
  2. 混入式继承

    let obj1 = {
      name: "小红",
      age: 18
    }
    
    let obj2 = {
      gender: "male"
    }
    
    // 直接通过实例合并
    function __mixin__(obj1, obj2) {
      for (let i in obj2) {
        obj1[i] = obj2[i]
      }
      return obj1
    }
    
    let obj3 = __mixin__(obj1, obj2)
    console.log(obj3);
    
  3. 混合式继承

    // 这种继承和混入一样,不算真正意义上的继承,只是合并实例
    // 混合 = 混入 + 原型
    function Person(name, age) {
      this.name = name
      this.age = age
    }
    
    function Student(gender) {
      this.gender = gender
    }
    
    // 在Student原型上添加一个混入的方法
    // 直接通过实例调用即可
    Student.prototype.__mixin__ = function (obj) {
      for (let i in obj) {
        this[i] = obj[i]
      }
      return this
    }
    
    let p1 = new Person("小红", 18)
    let stud = new Student("男")
    
    console.log(stud.__mixin__(p1));
    
  4. Object.create():这种也是原型式继承

    let person = {
      name: "Xiaong",
      age: 18
    }
    
    // 使用自带的方法实现
    // p1.__proto__ === person
    // person.__proto__ === Object.prototype
    let p1 = Object.create(person)
    console.log(p1);
    
    // 简单实现
    function Create(obj) {
      let o = {}
      o.__proto__ = obj
      return o
    }
    
    let p2 = Create(person)
    console.log(p2);
    
  5. 构造函数继承

    // 这种方式只是可以用父构造函数的属性和方法,并没有继承父类的原型
    // 父构造函数
    function Parent(name) {
      this.name = name
    }
    
    Parent.prototype.run = function () {
      return "我是父类方法"
    }
    
    // 子构造函数
    function Child(name, age) {
      Parent.call(this, name)
      this.age = age
    }
    
    Child.prototype.play = function () {
      return "我是子类方法"
    }
    
    let parent = new Parent("我是爹")
    console.log(parent); // Parent {name: '我是爹'}
    console.log(parent.run()); // 我是父类方法
    
    let child = new Child("我是儿", 18)
    console.log(child); // Child {name: '我是儿', age: 18}
    console.log(child.play()) // 我是子类方法
    console.log(child.run()); // child.run is not a function
    
  6. 寄生组合式继承

    // 构造函数继承的升级版,可以有效的继承父类的方法
    // 使子类的原型,继承父类的原型
    function clone(Parent, Child) {
      Child.prototype = Object.create(Parent.prototype)
      // 还要把构造器指向自己
      Child.prototype.constructor = Child
    }
    
    // 父构造函数
    function Parent(name) {
      this.name = name
    }
    
    Parent.prototype.run = function () {
      return "我是父类方法"
    }
    
    // 子构造函数
    function Child(name, age) {
      Parent.call(this, name)
      this.age = age
    }
    
    // 一定要先继承父类原型,在向自己的原型上添加方法,不然会被覆盖
    clone(Parent, Child)
    
    Child.prototype.play = function () {
      return "我是子类方法"
    }
    
    let parent = new Parent("我是爹")
    console.log(parent); // Parent {name: '我是爹'}
    console.log(parent.run()); // 我是父类方法
    
    let child = new Child("我是儿", 18)
    console.log(child); // Child {name: '我是儿', age: 18}
    console.log(child.play()) // 我是子类方法
    console.log(child.run()); // 我是父类方法
    
  7. extends

9.4 原型链

因为原型对象也是一个对象,只要是对象就会存在自己的原型对象,所以原型对象也有自己的原型对象,通过这样一级一级查找出的原型,就叫做对象的原型链

完成原型链如下图:
在这里插入图片描述

这张图看起来很复杂,但是只需要记住几句话就会好理解一些,

  1. Function 内置构造函数是所有函数的爹,包括构造函数和其他内置构造函数
  2. 除了 Function 实例化之后为函数,其他的实例都是对象
  3. Object 的原型对象是所有实例对象的爹
  4. Object 原型对象的隐式原型指向 null,代表它就是最顶层
  5. 实例的隐式原型 __proto__ 和 构造函数的显示原型 prototype,都指向构造函数的原型对象
  6. 所有对象的 __proto__ 和所有函数的 prototype 都为对象,所有函数的 __proto__ 都为函数
  7. 上面所有函数中不包括 Function,它的 __proto__ 和 prototype 都为函数,并且相等

10. class

10.1 class基本语法
// constructor必不可少,类名也不能与其他变量名、函数名、类名重复
class 类名 {
  // 构造函数
  constructor(){
    
  }
  // 原型方法
  sayHi(){
      
  }
}
10.2 class与构造函数区别
// class是ES6新增的,为构造函数的语法糖
// 构造函数
function Person(name, age) {
  this.name = name
  this.age = age
}
Person.prototype.sayHi = function () {
  console.log("说");
}

// class
class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }
  // 相当于直接把方法添加到原型上
  sayHi() {
    console.log("说");
  }
}
10.3 静态成员,私有成员
// 静态成员,static
// 静态成员是类(Person)的成员
class Person {
  static str = "我是静态属性"
  constructor(name, age) {
    this.name = name
    this.age = age
  }
  sayHi() {
    console.log("我是实例方法");
  }
  static hi() {
    console.log("我是静态方法");
  }
}

// 私有成员,#
class Person {
  #str = "我是私有属性"
  constructor(name, age) {
    this.name = name
    this.age = age
  }
  #sayHi() {
    console.log("我是私有方法");
  }
}
10.4 访问器,修改器
// set、get,私有属性,可以通过这种方式添加约束
// set必须要有参数,get必须要有返回值
// 比如银行存钱取钱只能为一百的倍数
class Person {
  #num = 100
  constructor() {

  }
  set num(value) {
    if (value % 100 === 0) {
      console.log("修改成功");
      this.#num = value
    } else {
      console.log("很抱歉,金额必须为100的倍数");
    }
  }
  get num() {
    return `您的余额为: ${this.#num}`
  }
}
let p1 = new Person()
console.log(p1.num); // 您的余额为: 100
console.log(p1.num = 150); // 很抱歉,金额必须为100的倍数
console.log(p1.num = 200); // 修改成功
console.log(p1.num); // 您的余额为: 200
10.5 extends,super
class // 父类:动物类
class Animal {
  constructor(name, age) {
    this.name = name
    this.age = age
  }
  call() {
    console.log(`${this.name}`)
  }
}

let animal = new Animal("动物", "未知")
console.log(animal); // Animal {name: '动物', age: '未知'}
animal.call() // 动物叫


// 子类:猫类,使用extends继承,super调用父类构造器
// super中也可以不写参数,相当于调用父类构造函数不传参,属性会为undefined
class Cat extends Animal {
  constructor(name, age, weight) {
    super(name, age)
    this.weight = weight
  }
  leap() {
    console.log("小猫会跳");
  }
}

let cat = new Cat("小猫", 6, 2n)
console.log(cat); // Cat {name: '小猫', age: 6, weight: 2n}
cat.call() // 小猫叫
cat.leap() // 小猫会跳


// 子类:狗类,使用extends继承,不写constructor和super
// 但是默认会使用super调用父类的构造器
// 这种写法属性是固定的,不能自己添加想要的属性,不太好
class Dog extends Animal {
  run() {
    console.log("小狗会跑")
  }
}

let dog = new Dog("小狗", 5)
console.log(dog); // Dog {name: '小狗', age: 5}
dog.call() // 小狗叫
dog.run() // 小狗会跑
10.6 特殊用法
class Demo {
  constructor(){
	this.arr = []	
  }
  list = []
  add() { }
}

let demo = new Demo()
// 此时,arr 和 list 都在 demo 的属性上,add 在 demo 原型上
// 相当于如果不是 constructor 里的函数会添加到原型上,而属性会添加到当前属性上
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值