10.JavaScript 中的类和对象以及继承

可枚举属性的补充

js 复制代码
var obj = {
    name: 'why',
    age: 18
}

Object.defineProperty(obj, 'address', {
    value: '北京',
})

// address 是不可枚举的,但是属性在浏览器里面会展示成灰色的,这个是为了方便我们调试
console.log(obj); 

JavaScript 中的类和对象

js 复制代码
function Person(name, age) {
    
}

var p1 = new Person('why', 18)
  • 在 JavaScript 中 Person 应该称之为一个构造函数
  • 从很多面向对象语言过来的开发者,也习惯称之为类

面向对象的特性-继承

面向对象的三大特性:封装、继承、多态

  • 封装:将属性和方法封装到一个类中,可以称之为封装的过程
  • 继承:子类继承父类,可以复用父类的功能,并且对父类进行扩展
  • 多态:不同的对象在执行时表现出不同的形态

继承可以帮助我们将重复的代码和逻辑抽取到父类中,子类只需要继承父类即可,而不需要重复编写相同的代码

JavaScript 原型链

从一个对象上获取属性,如果在当前对象中没有获取到就会去它的原型上面获取

js 复制代码
var obj = {
    name: '张三',
    age: 18
}

// [[Get]]操作
// 1. 先从obj对象中查找属性
// 2. 如果obj对象中没有,则从原型(__proto__)中查找
console.log(obj.address);


obj.__proto__ = {
    
}

obj.__proto__.__proto__ = {

}

obj.__proto__.__proto__.__proto__ = {
    address: '北京'
}

console.log(obj.address);

Object 的原型

顶层原型是什么?

js 复制代码
var obj = {
    name: "why"
}

console.log(obj.address);

// 到底是找到哪一层对象之后停止继续查找了呢?
console.log(obj.__proto__);
// 字面量对象 obj 的原型是 [Object: null prototype] {}
// [Object: null prototype] {} 就是顶层的原型

console.log(obj.__proto__.__proto__);

顶层原型来自哪里?

js 复制代码
var obj1 = {} // 创建一个对象
var obj2 = new Object() // 创建一个对象

// Object.prototype // 顶层原型
console.log(obj2.__proto__ === Object.prototype);

function Object() {

}

// Object.prototype // 顶层原型 Object 的原型对象
// Object.prototype.constructor

console.log(Object.getOwnPropertyDescriptors(Object.prototype));

[Object: null prototype] {} 原型有什么特殊?

  • 该对象有原型属性,但是它的原型属性已经指向的是 null,也就是已经是顶层原型了
  • 该对象上有很多默认的属性和方法

原型链关系的内存图

Object 是所有类的原型

原型链最顶层的原型对象就是 Object 的原型对象

js 复制代码
function Person(){
    
}

console.log(Person.prototype.__proto__);
console.log(Person.prototype.__proto__.__proto__);

实现继承

为什么需要有继承

代码方法重复

js 复制代码
function Student(name, age) {
    this.name = name;
    this.age = age;
}

Student.prototype.running = function () {
    console.log(this.name + " is running");
}

Student.prototype.eating = function () {
    console.log(this.name + " is eating");
}

Student.prototype.studying = function () {
    console.log(this.name + " is studying");
}

function Teacher(name, age, title) {
    this.name = name;
    this.age = age;
    this.title = title;
}

Teacher.prototype.running = function () {
    console.log(this.name + " is running");
}

Teacher.prototype.eating = function () {
    console.log(this.name + " is eating");
}

Teacher.prototype.teaching = function () {
    console.log(this.name + " is teaching");
}

原型链的继承方案

js 复制代码
// 父类:公共属性和方法
function Person() {
    this.name = 'why';
}

Person.prototype.eating = function () {
    console.log(this.name + " eating");
}

// 子类:特有属性和方法
function Student() {
    this.sno = 111;
}

// 原型链继承
var p = new Person();
Student.prototype = p;

Student.prototype.studing = function () {
    console.log(this.name + " studying");
}

var stu = new Student();
console.log(stu.name);
stu.eating()

原型链实现继承的弊端:

  • 1、打印 stu 对象,继承的属性是看不到的
  • 2、创建出来两个 stu 的对象
js 复制代码
var stu1 = new Student();
var stu2 = new Student();
// 获取引用,修改引用中的值,会相互影响
stu1.friends.push('kobe');
console.log(stu2.friends);
// 直接修改对象上的属性,是给本对象添加了一个新属性
stu1.name = 'kobe';
console.log(stu2.name);
  • 3、在前面的实现类的过程中都没有传递参数
js 复制代码
var stu1 = new Student('lilei', 112);

借用构造函数继承

为了解决原型链继承中存在的问题,开发人员提供了一种新的技术:constructor stealing (借用构造函数、经典继承、伪造对象)

借用继承的做法非常简单,在子类型构造函数的内部调用父类型构造函数

  • 因为函数可以在任意时刻被调用
  • 因此通过 apply()call() 方法也可以在新创建的对象上执行构造函数
js 复制代码
// 父类:公共属性和方法
function Person(name, age, friends) {
    // this = stu
    this.name = name;
    this.age = age;
    this.friends = friends;
}

Person.prototype.eating = function () {
    console.log(this.name + " eating");
}

// 子类:特有属性和方法
function Student(name, age, friends) {
    this.sno = 111;
    // 借用上面Person构造函数的执行过程
    Person.call(this, name, age, friends)
}
// 原型链继承
var p = new Person();
Student.prototype = p;

Student.prototype.studing = function () {
    console.log(this.name + " studying");
}

var stu1 = new Student('why', 18, ['lilei']);
var stu2 = new Student('kobe', 30, ['james']);
console.log(stu1);
console.log(stu2);

弊端:

  • 1.Person函数至少被调用了两次
  • 2.stu原型对象上会多出来一些属性,这些属性是没有必要的
相关推荐
To_OC12 分钟前
我被 useState 坑了两次之后,终于把它的脾气摸透了
前端·javascript·react.js
kyriewen5 小时前
我review了一份Vibe Coding写的前端代码——能跑,但5个地方迟早要命
前端·javascript·ai编程
Hilaku7 小时前
为什么在 2026 年,MPA(多页面应用)正在悄悄复辟?
前端·javascript·程序员
悟空瞎说7 小时前
iOS 高效绘图
javascript
触底反弹8 小时前
🔥 从 MySQL 到 Milvus:用 AI 日记本项目搞懂向量数据库和 RAG
javascript·人工智能·面试
妙码生花8 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(三十九):前端上传组件实现
javascript·vue.js·ai编程
dota李天王8 小时前
简单的 socket 实现
前端·javascript·websocket
寒水馨10 小时前
Linux下载、安装 Bun v1.3.14(附安装包bun-linux-x64.zip)
linux·javascript·typescript·node.js·bun·运行时·包管理器
windliang10 小时前
Claude Code 源码分析(一):从 claude 命令到 Agent 主循环
javascript·面试·ai编程
申君健248641827720210 小时前
WebGPU 内部运行原理:Format、Texture 与 GPU 侧到底在干什么
javascript