原型是 JavaScript 實現繼承與代碼復用的核心機制,也是面試高頻考點。本文結合個人學習經驗、核心概念解析與實戰案例,幫你徹底搞懂原型、prototype
、__proto__
及相關知識點,同時分享高效的學習方法。
一、個人學習方法:高效掌握復雜知識點
復雜概念(如原型)的學習,關鍵在“邊輸入邊記錄”和“先模仿后創新”,我的核心方法如下:
1. 邊看教程邊做“雙筆記”
- 截圖筆記:遇到關鍵代碼(如
prototype
掛載方法)、原型鏈圖示時,立刻截圖并標注核心邏輯(比如在截圖上寫“__proto__
指向構造函數的prototype
”); - 文字筆記:用自己的話總結知識點(比如“
new
關鍵字的作用,就是創建一個對象并讓它的__proto__
指向構造函數的prototype
”),避免照搬教程話術。 - 好處:復盤時既能看代碼細節,又能快速回憶自己的理解,比單純看視頻效率高 2 倍以上。
2. 利用碎片化時間,拒絕“無效等待”
很多事情(如視頻加載、代碼運行)存在空隙時間,此時可以:
- 快速默寫核心公式(如“
對象.__proto__ === 構造函數.prototype
”); - 梳理知識點邏輯(如“先區分普通對象和函數對象,再理解
prototype
的作用”); - 注意:如果事情需要全程專注(如調試復雜代碼),則不強行拆分時間,避免打斷思路。
3. 先仿造再創新:入門的“黃金法則”
學習原型這類抽象概念時,先“照抄”老師的代碼(如給 Date
原型加方法),理解每一行的作用后,再做創新修改(如給 Array
原型加“去重方法”)。
- 示例:先仿造“給
Date
加showNowTime
方法”,再創新“給Date
加showTime
方法,支持返回yyyy-MM-dd HH:mm
格式”。 - 好處:降低入門難度,避免因“想不出創新點”而放棄,同時在仿造中積累手感。
二、原型核心概念:先理清“3 個基礎問題”
在學原型前,必須先解決 3 個核心問題:區分普通對象與函數對象、理解 prototype
、理解 __proto__
。
1. 問題 1:如何區分普通對象與函數對象?
JS 中所有值要么是“普通對象”,要么是“函數對象”,兩者的核心區別是:是否有 prototype
屬性(函數對象有,普通對象沒有),常用 typeof
檢測:
類型 | 定義方式 | typeof 結果 | 是否有 prototype | 示例 |
---|---|---|---|---|
普通對象 | 對象字面量、new Object() | object | 無 | var o = {name: 'a'} |
函數對象 | 函數聲明、new Function() | function | 有 | function fn(){} , Date |
實戰檢測代碼
// 普通對象:typeof 為 object,無 prototype
var o1 = { name: '詩書畫唱' };
var o2 = new Object();
var arr = [1, 2, 3]; // 數組是特殊的普通對象
console.log(typeof o1); // object(普通對象)
console.log(typeof arr); // object(普通對象)
console.log(o1.prototype); // undefined(普通對象無 prototype)// 函數對象:typeof 為 function,有 prototype
function fn() {}
var Dog = function() {};
console.log(typeof fn); // function(函數對象)
console.log(typeof Date); // function(Date 是內置函數對象)
console.log(fn.prototype); // object(函數對象有 prototype,默認是普通對象)// 特殊例外:Function.prototype 是函數對象(唯一例外)
console.log(typeof Function.prototype); // function(其他函數對象的 prototype 都是 object)
2. 問題 2:prototype
是什么?有什么用?
prototype
是函數對象特有的屬性,本質是一個“普通對象”(除了 Function.prototype
),核心作用是實現代碼復用——給 prototype
加的屬性/方法,所有由該函數構造的對象都能訪問到。
核心特點
- 只有函數對象有
prototype
(普通對象沒有); - 函數對象的
prototype
中,默認有constructor
屬性,指向該函數本身(如fn.prototype.constructor === fn
); - 示例:給
Stu
函數的prototype
加sex
屬性,所有new Stu()
創建的對象都能訪問sex
。
實戰代碼:prototype
實現代碼復用
// 1. 定義函數對象(構造函數)
function Stu(name) {this.name = name; // 每個對象獨有的屬性(姓名不同)
}// 2. 給 prototype 加“共享屬性/方法”(所有學生都有相同的性別和“學習方法”)
Stu.prototype.sex