高级程序设计学习(二):面向对象编程

102 阅读2分钟

理解对象

ECMA-262 将对象定义为一组属性的无序集合

属性的类型

  1. 数据属性
  • [[Configurable]] 表示能否通过 delete 删除属性能否修改属性的特性
  • [[Enumerable]] 目标属性是否可被枚举(遍历)
  • [[Writable]] 表示能否修改属性的值,即值是可写的还是只读
  • [[Value]] 包含这个属性的数据值。读取属性时从这个位置读取;写入属性时,把新值保存在这个位置
  1. 访问器属性
  • [[Configurable]] 表示能否通过 delete 删除属性能否修改属性的特性
  • [[Enumerable]] 目标属性是否可被枚举(遍历)
  • [[Get]] 在读取属性时调用的函数。默认值是 undefined。
  • [[Set]] 在写入属性时调用的函数。默认值是 undefined。

创建对象

工厂模式 (没有解决对象标识问题)

function createPerson(name, age, job) {
  let o = new Object()
  o.name = name
  o.age = age
  o.job = job
  o.sayName = function () {
    console.log(this.name)
  }
}

let c1 = createPerson("a", 22, "a")
let c2 = createPerson("b", 28, "b")

构造函数模式(所有定义的方法都会实例化一遍)

function Person(name, age, job) {
  this.name = name
  this.age = age
  this.job = job
  this.sayName = function () {
    console.log(this.name)
  }
}

let p1 = new Person("a", 22, "a")
let p2 = new Person("b", 28, "b")

原型模式(属性和方法所有实例共享)

let Per = function () {}
Per.prototype.name = "a"
Per.prototype.age = 22
Per.prototype.job = "a"
Per.prototype.sayName = function () {
  console.log(this.name)
}

let per1 = new Per
let per2 = new Per

per1 与 per2 的 proto 属性指向原型 Per

对象迭代

  • Object.values() 返回对象值的数组

  • Object.entries() 返回键/值对的数组

  • Object.keys() 返回一个数组,包括对象自身的(不含继承的)

  • Object.getOwnPropertyNames() 包含对象自身的所有属性(不含Symbol属性,但是包括不可枚举属性)

  • for in 循环遍历对象自身的和继承的可枚举属性(不含Symbol属性)

继承

首先定义一个父类:

function Animal(name) {
  this.name = name
}

Animal.prototype.eat = function (food) {
  console.log(this.name + "正在吃" + food)
}

Animal.prototype.makeSound = function () {}

原型链

将父类的示例作为子类的原型实现继承

function Duck() {}
Duck.prototype = new Animal("鸭子")
Duck.prototype.makeSound = function () {
  console.log("嘎嘎嘎")
}
let duck = new Duck()
duck.eat("小米")
duck.makeSound()

盗用构造函数

使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类

function Dog() {
  Animal.call(this)
  this.name = "狗"
  this.makeSound = function () {
    console.log("汪汪汪")
  }
}
let dog = new Dog()
dog.makeSound()

注意:**这里 dog 无法拥有 eat 方法 **

组合继承

通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用

function Chicken() {
  Animal.call(this)
  this.name = "鸡"
}

Chicken.prototype = new Animal()
Chicken.prototype.makeSound = function () {
  console.log("咯咯咯")
}

let chicken = new Chicken()
chicken.eat("小米")
chicken.makeSound()

寄生组合继承

通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点

function Cow() {
  Animal.call(this)
  this.name = "牛"
}
(function () {
  let Super = function () {}
  Super.prototype = Animal.prototype
  Cow.prototype = new Super()
  Cow.prototype.constructor = Animal
})()
Cow.prototype.makeSound = function () {
  console.log("哞哞哞")
}

let cow = new Cow()
cow.eat("草")
cow.makeSound()