前端學習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
英文地址,請注明出處:http://en.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…