一篇文章看懂继承与原型链

[JavaScript 对象入门]

https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Objects

目录

[JavaScript 对象入门]

面向对象(object-oriented, OO)

面向对象编程 - OOP

对象基础:

对象属性方法的调用

[继承与原型链]

原型

设置原型

原型链与继承

创建的对象实例是如何与它的构造函数中的 `prototype` 关联的?

最后:继承的方法非常多变灵活,以下是部分实例


 

面向对象(object-oriented, OO)

面向对象编程 - OOP

对象基础:

首先我们需要先明白什么是对象,对象是一个包含相关数据方法的集合(通常由一些变量和函数组成,我们称之为对象里面的属性和方法)

/**
*1. 直接量
*/
const obj = {
  name: '张三',
  age: 18,
  address: {},
  study() {},
}

//当一个对象初始化创建后,还可以继续向该对象中添加保存新的属性特征,如:
stu.clazz = '一班'
stu.eat = function() {}


/**
*2. 利用封装自定义工厂函数的方式创建对象
*/

let ID = 1
function createStudent(name, age, address) {
  const stu = {
    id: ID++,
    name, 
    age,
    address,
    study() {
      console.log(this.name + '开始学习')
    }
  }
  return stu
}

/**
*3. 利用构造函数,创建对象
*/

//构造函数函数名的命名规范: 帕斯卡命名规范(即每个单词的首字母都大写,其它字母小写)
let ID = 1
function Student(name, age, address) {
  this.id = ID++
  this.name = name
  this.age = age
  this.address = address
  this.study = function() {
    console.log(this.name + '开始学习')
  }
}

const stu = new Student('张三', 18, '四川成都')

/**
*4. `Object.create()` 静态方法以一个现有对象作为原型,创建一个新对象。
*/

const obj = Object.create(proto)

/**
*5. Class语法糖
*   这是 ES6 中新增的创建对象的方式,其只是一个语法糖,本质上还是原型链体系内容
*/

class 类名 {
  // 构造函数
  constructor() {}

  // 成员方法
  methodName() {}
}

对象属性方法的调用

1. 使用了点表示法(dot notation)来访问对象的属性和方法,如: `obj.name`

  2. 另外一种访问对象属性的方式是使用括号表示法(bracket notation),如: `obj['name']`,这也叫通过索引访问对象属性。使用方括号索引的方式访问对象属性比打点调用更灵活,因为索引是可以通过变量或运算符进行运算最终得到

[继承与原型链]

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain

原型

原型,能够实现对象中的属性复用,是JS中主要实现继承的方式,使用函数中的一个特殊属性 `prototype` 表示

设置原型

/**
* 1. Object.create() 方法创建一个新的对象,并允许你指定一个将被用作新对象原型的对象。
*这里我们创建了一个 personPrototype 对象,它有一个 greet() 方法。然后我们使用 Object.create() 来*创建一个以 personPrototype 为原型的新对象。现在我们可以在新对象上调用 greet(),而原型提供了它的
*实现。
*/

const personPrototype = {
  greet() {
    console.log("hello!");
  },
};

const carl = Object.create(personPrototype);
carl.greet(); // hello!

/**
* 1. 使用构造函数被
*在 JavaScript 中,所有的函数都有一个名为 prototype 的属性。当你调用一个函数作为构造函数时,这
*个属性被设置为新构造对象的原型(按照惯例,在名为 __proto__ 的属性中)。因此,如果我们设置一个构*造函数的 prototype,我们可以确保所有用该构造函数创建的对象都被赋予该原型:
*/
const personPrototype = {
  greet() {
    console.log(`你好,我的名字是 ${this.name}!`);
  },
};

function Person(name) {
  this.name = name;
}

Object.assign(Person.prototype, personPrototype);
// 使用 Object.assign 将 personPrototype 中定义的方法绑定到 Person 函数的 prototype 属性上。
// 或Person.prototype.greet = personPrototype.greet;
//在这段代码之后,使用 Person() 创建的对象将获得 Person.prototype 作为其原型,其中自动包含 greet 方法。
const reuben = new Person("Reuben");
reuben.greet(); // 你好,我的名字是 Reuben!

原型链与继承

创建的对象实例是如何与它的构造函数中的 `prototype` 关联的?

`prototype` 是构造函数中的一个显式的属性,它是一个普通对象,通常我们将这个属性称为`原型对象`。使用 `new` 调用构造函数创建出来一个对象实例,该对象实例中有一个隐式属性 `__proto__`,通常我们将该隐式属性称为 `原型属性`。对象实例中的 `__proto__` 指向的就是其构造函数的 `prototype` 属性。

> - 每个对象都有 `__proto__` 的隐式属性

> - 每个函数都有 `prototype` 的显式属性

> - 对象实例中的 `__proto__` 指向的就是其构造函数的 `prototype` 属性。

原型链: 由对象的 `__proto__` 及其构造函数的 `prototype` 所串联的链式结构就是原型链。 `Object.prototype.__proto__` 固定设计为 null,也就意味着到此原型链结束。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
    function Student(name) {
      this.name = name
    }
    // 共享共用 study 方法
    // 将对象需要共享的属性保存在构造函数的 prototype 属性中
    Student.prototype.study = function() {
      console.log(this.name + '开始学习')
    }

    const stu = new Student('小明')

    console.log(stu.__proto__)
    console.log(stu.__proto__ === Student.prototype) // true
    console.log(Student.prototype.__proto__)
    // 默认情况下,自定义创建的函数中,prototype 对象的 __proto__
    // 指向的是 Object.prototype
    console.log(Student.prototype.__proto__ === Object.prototype) // true

    // 在 JS 中,固定设计 Object.prototype.__proto__ 为 null
    // null 是没有原型的
    console.log(Object.prototype.__proto__) // null

    console.log('姓名:', stu.name)
    stu.study()
    console.log(stu.toString())
    console.log('年龄:', stu.age) // 原型链中查找不到,返回 undefined
    // stu.eat() // Uncaught TypeError: stu.eat is not a function
  </script>
</body>
</html>

对象属性查找(继承):当试图访问一个对象的属性时:如果在对象本身中找不到该属性,就会在原型中搜索该属性。如果仍然找不到该属性,那么就搜索原型的原型,以此类推,直到找到该属性,或者到达链的末端,在这种情况下,返回 undefined

简单地说:每个对象(object)都有一个私有属性指向另一个名为原型(prototype)的对象。原型对象也有一个自己的原型,层层向上直到一个对象的原型为 null。根据定义,null 没有原型,并作为这个原型链(prototype chain)中的最后一个环节。

注意: 如果需要操作原型链,不要操作隐式属性 `__proto__`,通过操作 `prototype` 实现功能。

最后:继承的方法非常多变灵活,以下是部分实例

//1. 构造函数继承

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

// 子
function Teacher(name, age, course) {
  Person.call(this, name, age)
  this.course = course
}

//2. 原型链继承

function Person() {}
function Teacher() {}

Teacher.prototype = new Person()

//3. 组合继承

// 父
function Person(name, age) {
  this.name = name
  this.age = age
}
Person.prototype.eat = function(){}

// 子
function Teacher(name, age, course) {
  Person.call(this, name, age)
  this.course = course
}

// Teacher.prototype = new Person()
Teacher.prototype = Object.create(Person.prototype)

//4. class 继承

class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }
  eat() {
    console.log('父类中吃饭')
  }
  sleep() {
    console.log('父类中睡觉')
  }
}

class Teacher extends Person {
  constructor(name, age, course) {
    // 调用父类构造函数
    super(name, age)
    this.course = course
  }
  // 私有的成员方法
  teach() {
    console.log('授课...')
  }
  // 重写继承到的sleep() 方法,
  // 如果重写该方法时,仍然要调用到父类中的 sleep() 方法。
  // 可以使用 super.sleep() 调用到该方法
  sleep() {
    console.log('自我催眠了...')
    super.sleep()
  }
}

  • 37
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值