首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【重学前端】004-JavaScript:我们真的需要模拟类吗

【重学前端】004-JavaScript:我们真的需要模拟类吗

作者头像
訾博ZiBo
发布2025-01-06 18:53:56
发布2025-01-06 18:53:56
12200
代码可运行
举报
运行总次数:0
代码可运行

【重学前端】004-JavaScript:我们真的需要模拟类吗

一、曾经的“模拟面向对象”

1、“模拟面向对象”

思维导图
早期情况概述

早期的 JavaScript 程序员一般有过使用 JavaScript **“模拟面向对象”**的经历;

实际上,JavaScript 本身就是正统的面向对象语言

描述对象的方式 = 基于类(Java等) + 基于原型(JavaScript等);

基于类的描述方式更成功,导致人们错误地认为这样的方式才是面向对象。

公司政治原因

这模仿得不够彻底啊!如果 Java 的写法都能在这里使用,那对于 Java 程序员岂不是一件大好事! 如果 JavaScript 也是 sun 公司写的就好了,或者直接使用 Java 写前后端,岂不妙哉! 就像 TypeScript 的语法,和微软自家的 C# 就很像! 当然,JavaScript 本身也有很多实用而 Java 没有的特性!

由于公司的一些政治原因,JavaScript 在推出之时,被要求去模仿 Java ,因此有了 newthis等语言特性,使其看起来更像 Java !

2、ES6 提供了 class 关键字

ES6 提供了 class 这个关键字来定义类,尽管其仍然是基于原型运行时系统的模拟,但修正了一些常见的“坑”,统一了社区方案,这对语言的有很大好处!

二、什么是原型

0、思维导图

1、顺应人类自然思维的产物

原型是顺应人类自然思维的产物。

比如“照猫画虎”,这就是一种基于原型的思维! 再比如电影中的“外星人”,他们和人类很相似,这也是一种基于原型的思维!人们想象不出来外星人到底是什么样子,就在自身的基础上创造出“外星人”的样子!这就是基于原型的思维! 再比如电影、电视剧、小说中的故事,我们常说主人公的原型就是谁谁谁,我认为这也是基于原型的思维! 说了这么多,原型的含义也没啥复杂的了!多举例子是表达的妙诀!

2、基于“原型”描述对象

此时,我们就更倾向于描述一个东西像什么,比如老虎像大猫!

创建对象的方式:复制过来,改一改!

JavaScript 实现复制操作的两种方式:

引用方式: 新对象持有一个原对象的引用;

真复制: 直接复制原对象,新对象与原对象无任何关系!

3、基于“类”描述对象

比较基于“原型”,我们是把这一类对象抽象成一个模型,然后基于这个模型来描述具体的对象!比如学生这一类对象,我们把他们共有的状态和行为抽象成一个学生类,比如都有姓名、学号、年龄、性别等等,然后每一个具体的学生根据类这个模型创建出来,这就达到了描述对象的效果!

基于“类”比基于“原型”更抽象一点!

创建对象的方式:基于类模型,创建!

三、JavaScript 的原型

1、对原型系统的 2 条概括

抛开模拟 Java 类的复杂语法设施,原型系统非常简单!

  • 如果所有对象都有私有字段[[prototype]],就是对象的原型;
  • 读一个属性,如果对象本身没有,则会继续访问对象的原型,直到原型为空或者找不到为止

2、访问和操作原型

这个原型在 ES6 以来提供了一系列内置函数,以便更为直观地访问和操作原型

3、三个方法

  • Object.create:根据指定的原型创建新对象,原型可以是 null ;
  • Object.getPrototypeOf:获得一个对象的原型;
  • Object.setPrototyoeOf:设置一个对象的原型。
创建新对象
代码演示
代码语言:javascript
代码运行次数:0
运行
复制
// 创建一个对象作为原型
var 父亲 = {
  名字: "刘备",
  年龄: 63,
  民族: "汉族",
  说: function () {
    console.log("我是刘备");
  },
};
console.log(父亲);
父亲.说();

// 创建一个新对象
var 儿子 = Object.create(父亲);
console.log(儿子);
儿子.名字 = "刘禅";
儿子.年龄 = 16;
儿子.说 = function () {
  console.log("我是刘禅");
};
console.log(儿子);
儿子.说();
console.log(儿子.民族); // 注意:汉族
运行结果

如果对象本身没有,则会继续访问对象的原型,直到原型为空或者找不到为止!

代码语言:javascript
代码运行次数:0
运行
复制
PS D:\MyFile\VSCodeProjects\study-js> node .\zibo.js
{ '名字': '刘备', '年龄': 63, '民族': '汉族', '说': [Function: 说] }
我是刘备
{}
{ '名字': '刘禅', '年龄': 16, '说': [Function (anonymous)] }
我是刘禅
汉族
获得一个对象的原型
方法说明
代码语言:javascript
代码运行次数:0
运行
复制
Object.getPrototypeOf(obj) :返回指定对象的原型(内部[[Prototype]]属性的值)。
obj:要返回其原型的对象。
返回值:给定对象的原型。如果没有继承属性,则返回 null 
代码演示
代码语言:javascript
代码运行次数:0
运行
复制
// 创建一个对象作为原型
var 父亲 = {
  名字: "刘备",
  年龄: 63,
  民族: "汉族",
  说: function () {
    console.log("我是刘备");
  },
};
console.log(父亲);
父亲.说();

// 获得对象的原型
console.log(Object.getPrototypeOf(父亲));

// 创建一个新对象
var 儿子 = Object.create(父亲);
console.log(儿子);
儿子.名字 = "刘禅";
儿子.年龄 = 16;
儿子.说 = function () {
  console.log("我是刘禅");
};
console.log(儿子);
儿子.说();
console.log(儿子.民族); // 注意:汉族

// 获得对象的原型
console.log(Object.getPrototypeOf(儿子));
运行结果
代码语言:javascript
代码运行次数:0
运行
复制
PS D:\MyFile\VSCodeProjects\study-js> node .\zibo.js
[Object: null prototype] {}
{ '名字': '刘备', '年龄': 63, '民族': '汉族', '说': [Function: 说] }
设置一个对象的原型
方法说明
代码语言:javascript
代码运行次数:0
运行
复制
Object.setPrototypeOf(obj1, obj2):将 obj1 的原型设置为 obj2
代码演示
代码语言:javascript
代码运行次数:0
运行
复制
// 创建一个对象作为原型
var 父亲 = {
  名字: "刘备",
  年龄: 63,
  民族: "汉族",
  说: function () {
    console.log("我是刘备");
  },
};

// 获得对象的原型
console.log(Object.getPrototypeOf(父亲));

// 创建一个新对象
var 儿子 = {};
// 设置对象的原型
Object.setPrototypeOf(儿子, 父亲);

// 获得对象的原型
console.log(Object.getPrototypeOf(儿子));
运行结果
代码语言:javascript
代码运行次数:0
运行
复制
PS D:\MyFile\VSCodeProjects\study-js> node .\zibo.js
[Object: null prototype] {}
{ '名字': '刘备', '年龄': 63, '民族': '汉族', '说': [Function: 说] }

四、ES6 中的类

1、class 关键字

ES6 加入了新特征 class ,在任何场景下都推荐使用 ES6 语法来定义类,令 function 回归原本的函数语义。

ES6 中引入了 class 关键字,并且在标准中删除了所有 [[class]] 相关的私有属性描述,类的概念正式从属性升级成语言的基础设施,从此,基于类的编程方式成为了 JavaScript 的官方编程范式

2、基本写法

代码示例

在现有的类语法中,getter/setter 和 method 是兼容性最好的。

代码语言:javascript
代码运行次数:0
运行
复制
class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
  // Getter
  get area() {
    return this.calcArea();
  }
  // Method
  calcArea() {
    return this.height * this.width;
  }
}

console.log(new Rectangle(3, 4).area); // 12
说明

类的写法实际上也是由原型运行时来承载的,逻辑上 JavaScript 认为每个类是有共同原型的一组对象,类中定义的属性和方法被写在原型对象上

3、类的继承

代码示例
代码语言:javascript
代码运行次数:0
运行
复制
class Animal { 
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    console.log(this.name + ' makes a noise.');
  }
}
 
class Dog extends Animal {
  constructor(name) {
    super(name); // 调用父类构造函数并传入 name 参数
  }
 
  speak() {
    console.log(this.name + ' barks.');
  }
}
 
let d = new Dog('Mitzie');
d.speak(); // Mitzie barks.
说明

使用 extends 关键字自动设置了 constructor,并且会自动调用父类的构造函数,这是一种更少坑的设计。

一些激进的观点认为,class 关键字和箭头运算符可以完全替代旧的 function 关键字,它更明确地区分了定义函数和定义类两种意图,这是有一定道理的。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-07-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 【重学前端】004-JavaScript:我们真的需要模拟类吗
  • 一、曾经的“模拟面向对象”
    • 1、“模拟面向对象”
      • 思维导图
      • 早期情况概述
      • 公司政治原因
    • 2、ES6 提供了 class 关键字
  • 二、什么是原型
    • 0、思维导图
    • 1、顺应人类自然思维的产物
    • 2、基于“原型”描述对象
    • 3、基于“类”描述对象
  • 三、JavaScript 的原型
    • 1、对原型系统的 2 条概括
    • 2、访问和操作原型
    • 3、三个方法
      • 创建新对象
      • 获得一个对象的原型
      • 设置一个对象的原型
  • 四、ES6 中的类
    • 1、class 关键字
    • 2、基本写法
      • 代码示例
      • 说明
    • 3、类的继承
      • 代码示例
      • 说明
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档