前端学习9:JavaScript--对象与原型

前言:适合有基础的同学入门尝试 / 复习回忆。


对象基础:

1.创建用户对象
const user = {// 属性(键值对)name: "小岛",age: 20,isAdmin: false,
}
2.方法(函数属性)
sayHello() {console.log(`你好,我是${this.name}`);
}
3.访问属性
console.log(user.name); // "小岛"
user.sayHello();        // 调用方法

属性和方法操作:

1.添加新属性
user.email = "Island@example.com";
2.删除属性 
delete user.isAdmin;
※3.遍历属性(重要!)
for (let key in user) {console.log(`${key}: ${user[key]}`);
}
※4. 方法中的this(重点理解!)
const car = {brand: "Toyota",start() {console.log(`${this.brand}启动了!`);}
};
car.start(); // this指向car对象

原型和继承

1. 原型链:

每个JS对象都有隐藏的[[Prototype]] (原型)属性,形成链条

同时, prototype 属性也是一个对象(称为原型对象),JavaScript 会自动为这个原型对象添加 constructor 属性,默认指向该函数本身

const animal = {eats: true
};const rabbit = {jumps: true,__proto__: animal // 设置原型(实际开发用Object.create,下面案例会有,先略过)
};console.log(rabbit.eats); // true(来自原型)
console.log(rabbit.jumps); // true(自身属性)

2. 构造函数(传统实现方式)

// 1. 创建构造函数(首字母大写)
function Person(name, age) {this.name = name;this.age = age;
}// 2. 在原型上添加方法
Person.prototype.introduce = function() {console.log(`我是${this.name},今年${this.age}岁`);
};// 3. 创建实例
const p1 = new Person("李四", 30);
p1.introduce(); // 调用原型方法// 原型关系验证
console.log(p1.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
老式方式的继承:

了解即可~这种方式是 ES6 类语法出现之前实现继承的标准方式,理解它有助于深入掌握 JavaScript 面向对象的本质 —— 基于原型的继承系统。 

// 父类构造函数 - 用于初始化实例属性
function Animal(name) {this.name = name;   // 每个动物都有"name"属性
}// 重点:在原型上定义方法 - 所有实例共享
Animal.prototype.eat = function() {console.log(`${this.name}在吃东西`);
};// 子类构造函数
function Bird(name) {Animal.call(this, name); // 关键:调用父类构造函数,继承属性
}// 继承方法
Bird.prototype = Object.create(Animal.prototype);
Bird.prototype.constructor = Bird;Bird.prototype.fly = function() {console.log(`${this.name}在飞翔`);  // 子类特有方法,不会影响父类 Animal
};// 创建Bird实例
const bird = new Bird("小鸟");// 调用继承自Animal的属性和方法
console.log(bird.name); // 输出:"小鸟"
bird.eat(); // 输出:"小鸟在吃东西"// 调用Bird自身的方法
bird.fly(); // 输出:"小鸟在飞翔"
 代码解析:
  • Animal.call(this, name) 这行代码非常重要                                                      
    • 它调用了父类 Animal 的构造函数
    • 通过 call(this) 确保父类构造函数中的 this 指向当前 Bird 实例
    • 这样 Bird 实例就能继承 Animal 的 name 属性
  • Object.create(Animal.prototype) 创建一个新对象,其原型指向 Animal.prototype
  • 把这个新对象赋值给 Bird.prototype,使得 Bird 实例能访问 Animal 原型上的方法(如 eat
  • 为什么要修复 constructor
    • 因为上一步操作后,Bird.prototype.constructor 会指向 Animal
    • 手动设置为 Bird 才符合逻辑(构造函数的原型的 constructor 应该指向自身)
原型链结构解析

bird实例 →Bird.prototype → Animal.prototype → Object.prototype → null
↑                        ↑                                ↑
fly:  function        eat: function          toString等通用方法
↑                       
constructor: Bird         

关键知识点总结(重点)
  1. 属性继承:通过 父类.call(this, 参数) 在子类构造函数中实现
  2. 方法继承:通过 子类.prototype = Object.create(父类.prototype) 实现
  3. 构造函数修复:必须手动设置 子类.prototype.constructor = 子类
  4. 原型链:实例通过 __proto__ 指向原型对象,形成链式查找结构

※3. class语法(ES6现代写法)

class Animal {// 构造函数:初始化动物名称constructor(name) {this.name = name;}// 原型方法speak() {console.log(`${this.name} 发出声音`);}
}// extends继承
class Dog extends Animal {constructor(name, breed) {super(name); // 调用父类构造函数,初始化名称this.breed = breed;   // 新增 breed 属性存储狗的品种}// 方法重写speak() {console.log(`${this.name} 汪汪叫!`);}// 新增方法run() {console.log(`${this.name} 在奔跑`);}
}// 使用
const myDog = new Dog("旺财", "金毛");
myDog.speak(); // "旺财 汪汪叫!"
myDog.run();   // "旺财 在奔跑"
代码解析
  1. Animal 基类

    • 定义了一个动物类 Animal,包含:
      • 构造函数 constructor(name):初始化动物名称
      • 原型方法 speak():输出动物发出声音的通用描述
  2. Dog 子类(继承自 Animal)

    • 使用 extends 关键字实现继承
    • 构造函数 constructor(name, breed)
      • 通过 super(name) 调用父类构造函数,初始化名称
      • 新增 breed 属性存储狗的品种
    • 方法重写:重写了父类的 speak() 方法,改为具体的 "汪汪叫"
    • 新增方法:添加了 run() 方法,是 Dog 类特有的行为
  3. 使用类创建实例

    • const myDog = new Dog("旺财", "金毛") 创建了 Dog 类的实例
    • 调用方法:myDog.speak() 和 myDog.run() 分别调用了重写的方法和新增方法 
原型链深度图解 :

原型链:当访问一个对象的属性或方法时,JavaScript 会沿着原型链向上查找,直到找到为止

myDog实例 → Dog.prototype → Animal.prototype → Object.prototype → null
↑                           ↑                        ↑                                ↑
breed                   run()              speak()基础版          toString等通用方法
↑                           ↑                        ↑
name             constructor           constructor 

代码解析: 
  • 第一层:myDog 实例

    • 包含实例自身的属性:name(来自父类)和 breed(自身特有)
  • 第二层:Dog.prototype

    • 包含 Dog 类定义的方法:run() 和重写的 speak()
    • 包含 Dog 类的构造函数
  • 第三层:Animal.prototype

    • 包含 Animal 类定义的方法:原始的 speak() 方法
    • 包含 Animal 类的构造函数
  • 第四层:Object.prototype

    • 包含 JavaScript 所有对象都具有的通用方法,如 toString()hasOwnProperty() 等
  • 终点:null

    • 原型链的末端,没有任何属性和方法
 几个小疑问:

一、为什么 Dog 子类必须用 super(name) 而不能直接 this.name = name

原因一:原型链初始化顺序
子类实例的创建过程是:先初始化父类的属性和方法,再添加子类自己的特性。
当你在子类构造函数中使用 this 关键字时,JavaScript 要求必须先通过 super() 完成父类的初始化,否则会报错。
比如下面的代码会直接报错:

class Dog extends Animal {constructor(name, breed) {this.name = name; // 错误!必须先调用 super()this.breed = breed;}
}

原因二:代码复用
父类 Animal 的构造函数已经实现了 this.name = name 的逻辑,子类通过 super(name) 调用父类构造函数,就不需要重复写 this.name = name 了,这正是继承的意义 —— 复用父类代码。

二、如果创建 Animal 类的实例,调用 speak() 会输出什么?

会输出父类定义的 “发出声音”,而不是 “汪汪叫”。
原因是:方法调用遵循 “就近原则”—— 优先使用当前类或实例自身的方法,找不到再沿原型链向上找

举例说明:

// 创建 Animal 实例
const animal = new Animal("小动物");
animal.speak(); // 输出:"小动物 发出声音"
  • animal 是 Animal 的实例,它的原型链是:animal → Animal.prototype → Object.prototype → null
  • 调用 speak() 时,JavaScript 在 Animal.prototype 上找到了 speak() 方法(父类定义的版本),所以直接执行该方法,输出 “发出声音”。

 而 Dog 实例 myDog 调用 speak() 时:

  • 它的原型链是:myDog → Dog.prototype → Animal.prototype → ...
  • JavaScript 先在 Dog.prototype 上找到了重写后的 speak() 方法(“汪汪叫”),就不会再去找父类的版本了。(子类重写会覆盖父类方法)

案例实操(在控制台尝试)

(1)原型链追溯

class A {}
class B extends A {}
const b = new B();console.log(b instanceof B);  // true
console.log(b instanceof A);  // true
console.log(b instanceof Object); // true// 手动检查原型链
console.log(b.__proto__ === B.prototype,B.prototype.__proto__ === A.prototype,A.prototype.__proto__ === Object.prototype
);

(2)方法覆盖实验

class Parent {msg = "父类属性";show() {console.log(this.msg);}
}class Child extends Parent {msg = "子类属性";
}const obj = new Child();
obj.show(); // 输出什么?为什么?

(3)静态方法

class User {static staticMethod() {console.log("我是静态方法,通过类名调用");}
}User.staticMethod(); // 正确
const u = new User();
u.staticMethod();    // 报错

(4)思考题目

1.老式传统方式:以下代码输出什么?为什么?(可以回顾上面的传统方式)

function Foo() {}
const f1 = new Foo();console.log(f1.constructor === Foo);
console.log(Foo.prototype.constructor === Foo);
  1. 首先理解几个关键概念:

    • Foo 是一个构造函数
    • f1 是通过 new Foo() 创建的实例对象
    • 每个函数都有一个 prototype(原型)属性
    • 每个对象都有一个 constructor(构造函数)属性,指向创建它的构造函数
  2. 第一行输出 f1.constructor === Foo → true

    • 当通过 new Foo() 创建 f1 时,f1 本身并没有 constructor 属性
    • JavaScript 会沿着原型链查找,在 Foo.prototype 上找到 constructor 属性
    • 这个属性指向 Foo 构造函数,因此 f1.constructor 最终指向 Foo
  3. 第二行输出 Foo.prototype.constructor === Foo → true

    • 函数的 prototype 属性是一个对象(称为原型对象)
    • JavaScript 会自动为这个原型对象添加 constructor 属性,默认指向该函数本身
    • 因此 Foo.prototype.constructor 直接指向 Foo 构造函数

补充延伸知识:

  • 原型对象的 constructor 属性是区分对象类型的重要标识
  • 如果手动修改了原型对象(如实现继承时),需要手动修复 constructor 指向,否则会出现不符合预期的结果(如前面继承示例中修复 Bird.prototype.constructor 的操作)

2.如何实现一个myInstanceof函数,判断对象是否是某个类的实例?

3.class语法中,super关键字有几种用法?分别是什么?

  • 作为函数super(...) 用于子类构造函数中,调用父类构造函数,初始化继承的属性。
  • 作为对象super.xxx 用于子类方法中,访问父类的原型方法(普通方法中)或静态方法(静态方法中)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/90769.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/90769.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络:应用层

网络:应用层 我们要知道,所有的问题解决都是在应用层。:happy: 协议是一种约定,也就是双方约定好的结构化的数据。但是在读写数据时我们都是按字符串的方式来发送接受的,那么我们应该如和传输结构化的数据呢?应用层协…

rust-包和箱子

📦 图解 Rust 代码组织层级 #mermaid-svg-fBDy1PDZZ6bi000z {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-fBDy1PDZZ6bi000z .error-icon{fill:#552222;}#mermaid-svg-fBDy1PDZZ6bi000z .error-text{fi…

C++算法竞赛篇(五)循环嵌套题型讲解

C算法竞赛篇(五)循环嵌套题型讲解前言C循环嵌套题型讲解第一题 包含数字的9第二题 求出 e 的值第三题 斐波那契数列第四题 第 n 小的质数第五题 水仙花数前言 前面的题型里我们认识了C里面的三大循环本篇博客我们开始讲解C循环嵌套题型 我的个人主页&am…

Gradio全解8——ChatInterfaceChatbot:聊天界面类与聊天机器人(3)——ChatInterface的多模态功能与附加输入输出

Gradio全解8——ChatInterface&Chatbot:聊天界面类与聊天机器人(3)——ChatInterface的多模态功能与附加输入输出8.3 ChatInterface的多模态功能与附加输入输出8.3.1 多模态功能1. 设置multimodal和fn参数2. 传入MultimodalTextbox组件及…

php算法-- 关联数组使用,优化sip账号去重

文章目录1 变量定义2. 核心特性code1 变量定义 类型:嵌套的关联数组(Nested Associative Array)外层结构:[中继ID > 账号列表]键 (Key):中继ID(字符串或整型)值 (Value):索引数组…

LLM 多语言数据集

多语言数据感觉主要还是fineweb和fineweb2, 其他数据都是主要针对特定语种比较多 101 Billion Arabic Words Dataset ClusterlabAi/101_billion_arabic_words_dataset 数据主要从e Common Crawl WET 中提取,并采用了创新的技术来进行去重和筛选,主要解决…

【HarmonyOS Next之旅】DevEco Studio使用指南(三十六) -> 配置构建(三)

目录 1 -> 定制HAR多目标构建产物 1.1 -> 定义产物的deviceType 1.2 -> 定义C工程依赖的.so文件 1.3 -> 定义产物的资源 2 -> 配置APP多目标构建产物 2.1 -> 定义产物的APP包名和供应商名称 2.2 -> 定义product的bundleName 2.3 -> 定义produc…

数据赋能(340)——技术平台——共享平台

概述重要性如下:提高数据利用效率:数据共享平台能够将分散在各部门的数据进行集中管理,促进数据流通和共享,避免数据孤岛现象,从而提高数据利用效率。促进决策科学化:通过共享平台,各部门可以获…

开闭原则在C++中的实现

开闭原则(Open/Closed Principle,简称 OCP)是面向对象设计中的一个重要原则,属于“SOLID”原则之一。它的核心思想是:“软件实体(如类、模块、函数等)应该对扩展开放,对修改关闭。”…

C语言:*p++与p++有何区别

1. 指针基础练习&#xff1a;演示p、p和(*p)的区别核心目的&#xff1a;区分指针自增与指针指向值自增的不同逻辑&#xff0c;理解运算符优先级对指针操作的影响。#include <stdio.h>void arr1() {int arr[] {11,13,15,17,19};int *p arr;printf("结果1&#xff1…

【设计】设计一个web版的数据库管理平台后端(之二)

在之前&#xff0c;我写过一篇【设计】设计一个web版的数据库管理平台后端精要 的文章&#xff0c;文章讲了一个web版数据库管理平台的实现思路及主要代码。 最近&#xff0c;我看了下Mybatis的源码&#xff0c;觉得Mybatis的分层架构挺好&#xff0c;所以想到了完善下web版数据…

Visual tudio 各版本下 C++ 开发的核心区别与实践指南

C语言的发展经历了数十年的演进&#xff0c;从 C98 到现代的 C20/23&#xff0c;语言本身发生了巨大的变革。与此同时&#xff0c;Visual Studio 作为主流的 C 开发环境之一&#xff0c;其编译器对各个 C 标准的支持程度也随版本不断演进&#xff0c;直接影响着开发者的编程方式…

怎样让阿里云服务器(centos)有界面

要让阿里云服务器 CentOS 有图形界面&#xff0c;可以按照以下步骤进行操作&#xff1a;登录服务器&#xff1a;使用 SSH 客户端工具&#xff0c;通过 IP 地址和账号登录到阿里云服务器。更新系统软件源&#xff1a;输入命令sudo yum update&#xff0c;更新系统软件源&#xf…

Qt 异步编程模式与应用

在现代软件开发中&#xff0c;异步编程已成为提升应用性能和响应性的关键技术。Qt 作为一个强大的跨平台框架&#xff0c;提供了多种异步编程模式&#xff0c;包括信号槽机制、事件循环、线程池、异步 I/O 等。本文将深入探讨 Qt 异步编程的各种模式及其应用场景&#xff0c;帮…

面试150 数字范围按位与

思路 只要 left < right&#xff0c;说明两者在某些低位上存在不同&#xff0c;为了找到它们的公共前缀&#xff08;高位相同部分&#xff09;&#xff0c;不断将 left 和 right 同时右移&#xff08;即除以2&#xff09;&#xff0c;直到它们相等&#xff0c;记录右移的次数…

数据库HB OB mysql ck startrocks, ES存储特点,以及应用场景

这些数据库和存储引擎主要有:HB(HBase)、OB(OceanBase)、MySQL、ClickHouse(CK)、StarRocks、Elasticsearch(ES),下面分别介绍它们的存储特点以及典型应用场景。 1. HBase (HB) 存储特点 分布式、面向列的NoSQL数据库 采用HDFS存储,数据以表、row key、列族、时间戳…

Java技术栈/面试题合集(17)-Git篇

场景 Java入门、进阶、强化、扩展、知识体系完善等知识点学习、性能优化、源码分析专栏分享: Java入门、进阶、强化、扩展、知识体系完善等知识点学习、性能优化、源码分析专栏分享_java高级进阶-CSDN博客 通过对面试题进行系统的复习可以对Java体系的知识点进行查漏补缺。…

破局与重构:King’s LIMS 引领电子行业实验室智能化转型

在全球化高新技术竞争白热化背景下&#xff0c;电子行业正经历从规模导向扩张向质量效益跃升的战略转型。终端用户对产品性能的极致化追求、行业质量合规标准的持续迭代升级&#xff0c;以及检测数据的指数级增长&#xff0c;共同形成"需求牵引供给、供给创造需求"的…

暑期算法训练.9

目录 43 .力扣75 颜色分类 43.1 题目解析&#xff1a; 43.2 算法思路&#xff1a; 43.3 代码演示&#xff1a; 43.4 总结反思&#xff1a; 44. 力扣 912 排序数组 44.1 题目解析&#xff1a; 44.2 算法思路&#xff1a; 44.3 代码演示&#xff1a; ​编辑 44.4 总结反…

2.安装CUDA详细步骤(含安装截图)

2.安装CUDA 第一步&#xff1a;安装anaconda 注意&#xff1a;安装CUDA之前需要安装好anaconda&#xff0c;详见安装anaconda详细步骤&#xff08;含安装截图&#xff09; 文章目录2.安装CUDA2.0 CUDA是什么&#xff0c;为什么要安装它&#xff1f;2.1 验证计算机是否安装CUDA2…