深入理解 JavaScript 中的静态属性、原型属性与实例属性

用 "一家三口" 的家庭关系来类比,一眼就能懂:

  1. 静态属性:家里的 "户口本"
  • 归属:属于整个家(对应构造函数 / 类),不是某个人的。
  • 用法:只有提 "我们家" 时才用(比如 "我们家户口本地址是 XX"),你不能说 "这是我的户口本"。
  1. 原型属性:家里的 "公共物品"(比如冰箱、电视)
  • 归属:放在家里客厅,全家共用(对应原型对象)。
  • 用法:爸妈、你都能用来存东西 / 看电视(所有实例共享),但你不能把冰箱搬去自己房间说成 "我的"(实例不能独占)。
  1. 实例属性:你的 "私人用品"(比如你的手机、日记本)
  • 归属:只属于你个人(对应单个实例)。
  • 用法:只有你能改手机壁纸、写日记(实例独自控制),爸妈的手机(其他实例)和你没关系。

一、静态属性(Static Properties)

概念与本质

静态属性是直接挂载在函数本身的属性,与函数的实例无关,也不会出现在原型链中。从本质上讲,JavaScript 中函数是特殊的对象,静态属性就是这个 "函数对象" 自身的属性,类似于其他语言中 "类的静态成员"。

定义方式

通过 函数名.属性名 直接定义,无需依赖实例:

ini 复制代码
// 构造函数
function Tool() {}

// 定义静态属性(包括静态方法)
Tool.version = "2.1.0"; // 静态常量:版本号
Tool.count = 0; // 静态变量:工具调用次数
Tool.format = function(str) { // 静态方法:格式化字符串
  return str.toUpperCase();
};

访问规则

  1. 只能通过函数本身访问,无法通过实例访问;
  2. 不参与继承,子类无法继承父类的静态属性(除非手动复制);
  3. 所有访问共享同一属性,修改静态属性会影响所有通过函数访问的地方。

典型应用场景及详细举例

1. 工具类方法与常量(最常见场景)

当需要一组与实例无关的工具函数时,静态属性是最佳选择。例如 JavaScript 内置的 Math 对象:

javascript 复制代码
// 内置静态属性示例
console.log(Math.PI); // 3.141592653589793(静态常量)
console.log(Math.max(1, 3, 5)); // 5(静态方法)

// 自定义日期工具类
function DateUtils() {}
// 静态常量:常用日期格式
DateUtils.FORMATS = {
  DATE: "YYYY-MM-DD",
  DATETIME: "YYYY-MM-DD HH:mm:ss"
};
// 静态方法:格式化日期
DateUtils.format = function(date, format) {
  // 实现逻辑...
  return formattedStr;
};

// 使用:无需创建实例,直接调用
console.log(DateUtils.FORMATS.DATE); // "YYYY-MM-DD"
DateUtils.format(new Date(), DateUtils.FORMATS.DATETIME);

优势:工具方法无需依赖实例状态,直接通过类名调用,避免创建无意义的实例。

2. 实例计数器与全局状态

用于统计构造函数创建的实例数量,或维护全局唯一的状态:

ini 复制代码
function User(name) {
  this.name = name;
  User.totalCount++; // 每次创建实例时自增计数器
}
// 静态属性:统计用户总数
User.totalCount = 0;
// 静态属性:记录当前在线用户(全局状态)
User.onlineUsers = [];

// 静态方法:添加在线用户
User.addOnlineUser = function(user) {
  this.onlineUsers.push(user);
};

// 使用
const u1 = new User("张三");
const u2 = new User("李四");
console.log(User.totalCount); // 2(共创建2个实例)
User.addOnlineUser(u1);
console.log(User.onlineUsers.length); // 1

优势:全局状态由构造函数统一管理,避免散落在全局变量中,便于维护。

3. 命名空间与枚举值

通过静态属性创建命名空间,或定义枚举值(固定选项集合):

javascript 复制代码
// 订单状态枚举(静态属性集合)
function Order() {}
Order.STATUS = {
  PENDING: "pending", // 待支付
  PAID: "paid",       // 已支付
  SHIPPED: "shipped", // 已发货
  DELIVERED: "delivered" // 已送达
};

// 使用:通过类名访问枚举值,避免硬编码
const order = new Order();
if (order.status === Order.STATUS.PAID) {
  console.log("订单已支付");
}

优势:枚举值集中管理,修改时只需改一处,提高代码可维护性。

二、原型属性(Prototype Properties)

概念与本质

原型属性是挂载在函数的 prototype 对象 上的属性。JavaScript 中每个函数都有 prototype 属性(原型对象),通过该函数创建的所有实例会共享这个原型对象,因此原型属性是所有实例的 "公共资源"。

定义方式

通过 函数名.prototype.属性名 定义:

javascript 复制代码
function Person(name) {
  this.name = name;
}

// 定义原型属性(包括原型方法)
Person.prototype.species = "人类"; // 原型常量:所有实例共享的物种
Person.prototype.greet = function() { // 原型方法:公共行为
  return `你好,我是${this.name}`;
};

访问规则

  1. 通过实例访问:实例会先查找自身属性,若不存在则沿原型链查找原型属性;
  2. 所有实例共享:修改原型属性会影响所有未被 "覆盖" 的实例;
  3. 可通过原型对象直接访问函数名.prototype.属性名 可直接操作原型属性。

典型应用场景及详细举例

1. 实例公共方法(节省内存的核心场景)

当多个实例需要共享同一方法时,将方法定义在原型上可避免重复创建(每个实例无需单独存储方法):

javascript 复制代码
// 错误示例:每个实例都创建独立的方法(浪费内存)
function Student(name) {
  this.name = name;
  this.study = function() { // 每个实例都会复制这个函数
    return `${this.name}在学习`;
  };
}

// 正确示例:原型方法(所有实例共享)
function Student(name) {
  this.name = name;
}
Student.prototype.study = function() { // 仅在原型上定义一次
  return `${this.name}在学习`;
};

// 使用
const s1 = new Student("小明");
const s2 = new Student("小红");
console.log(s1.study === s2.study); // true(共享同一方法)

优势:对于创建 1000 个实例的场景,原型方法仅占用 1 份内存,而实例方法会占用 1000 份内存。

2. 默认属性与基础配置

为所有实例提供默认属性值,实例可根据需要覆盖:

javascript 复制代码
function Button(text) {
  this.text = text; // 实例独有的文本
}
// 原型属性:所有按钮的默认样式
Button.prototype.style = {
  width: "100px",
  height: "40px",
  color: "black"
};
// 原型方法:渲染按钮
Button.prototype.render = function() {
  return `<button style="width:${this.style.width};height:${this.style.height}">${this.text}</button>`;
};

// 使用
const btn1 = new Button("确定");
const btn2 = new Button("取消");

//  btn1 覆盖默认样式(不影响其他实例)
btn1.style.width = "150px";

console.log(btn1.render()); // 宽度150px的按钮
console.log(btn2.render()); // 宽度100px的默认按钮

优势:默认配置集中管理,实例可灵活定制,兼顾复用与个性化。

3. 原型链继承与方法扩展

通过修改原型对象实现继承,或为内置对象扩展方法:

javascript 复制代码
// 为数组扩展原型方法(谨慎使用,避免污染内置对象)
Array.prototype.sum = function() {
  return this.reduce((total, item) => total + item, 0);
};

// 使用
const arr = [1, 2, 3];
console.log(arr.sum()); // 6(所有数组实例均可调用)

注意:扩展内置对象原型需谨慎,可能与未来的 JavaScript 标准方法冲突。

三、实例属性(Instance Properties)

概念与本质

实例属性是绑定到具体实例 的属性,通过构造函数内部的 this 关键字定义,或在实例创建后动态添加。每个实例的属性独立存储,互不干扰,是实例独有的状态或数据。

定义方式

  1. 构造函数内部通过 this.属性名 初始化:

    ini 复制代码
    function Product(name, price) {
      this.name = name; // 实例属性:名称
      this.price = price; // 实例属性:价格
    }
  2. 实例创建后动态添加:

    ini 复制代码
    const product1 = new Product("手机", 5999);
    product1.stock = 100; // 动态添加实例属性:库存

访问规则

  1. 只能通过实例访问,无法通过函数或原型对象直接访问;
  2. 实例间相互独立:修改一个实例的属性不会影响其他实例;
  3. 优先级最高:若实例属性与原型属性同名,访问时优先使用实例属性。

典型应用场景及详细举例

1. 实例独有数据(核心场景)

存储每个实例独有的信息,如用户的个人信息、商品的具体参数:

javascript

运行

ini 复制代码
function User(id, name, email) {
  this.id = id;       // 实例独有的ID
  this.name = name;   // 实例独有的姓名
  this.email = email; // 实例独有的邮箱
}

// 使用
const user1 = new User(1, "张三", "zhangsan@example.com");
const user2 = new User(2, "李四", "lisi@example.com");

console.log(user1.name); // "张三"
console.log(user2.email); // "lisi@example.com"
user1.email = "new@example.com"; // 修改user1的邮箱,不影响user2

核心价值:每个实例的个性化数据必须通过实例属性存储,确保数据隔离。

2. 实例状态管理

记录实例的动态状态(如是否激活、当前进度等):

ini 复制代码
function Task(title) {
  this.title = title;
  this.status = "todo"; // 实例状态:待办(初始值)
  this.progress = 0;    // 实例状态:进度(0-100)
}

// 实例方法:更新进度(依赖实例状态)
Task.prototype.updateProgress = function(percent) {
  this.progress = percent;
  if (percent === 100) {
    this.status = "done"; // 更新状态为"已完成"
  }
};

// 使用
const task1 = new Task("完成报告");
task1.updateProgress(50);
console.log(task1.progress); // 50(task1的进度)
console.log(task1.status); // "todo"(仍未完成)

const task2 = new Task("整理文件");
task2.updateProgress(100);
console.log(task2.status); // "done"(task2的状态独立)

优势:状态与实例绑定,多个实例的状态变化互不干扰,逻辑清晰。

3. 动态临时属性

为特定实例添加临时数据(如缓存、临时标记):

ini 复制代码
function Article(id, content) {
  this.id = id;
  this.content = content;
}

// 使用
const article = new Article(1, "这是一篇长文...");

// 动态添加临时缓存属性(仅当前实例有效)
article.tempCache = {
  summary: "文章摘要(临时计算结果)",
  keywords: ["前端", "JavaScript"]
};

// 处理完成后清除临时属性
delete article.tempCache;

优势:临时数据无需定义在构造函数中,避免污染其他实例,灵活应对临时需求。

四、三类属性的核心区别对比

属性类型 定义位置 访问方式 共享性 内存占用
静态属性 函数本身(函数名.xxx 仅函数可访问 函数级共享 全局唯一,占用一份内存
原型属性 函数的 prototype 实例访问(原型链查找) 所有实例共享 原型对象中存储,一份内存
实例属性 构造函数内 this 仅实例可访问 实例独立不共享 每个实例单独存储

简单测试掌握与否(有兴趣的可以瞅一眼)

1

以下代码执行后,控制台输出的三个结果分别是什么?

ini 复制代码
function Player(name) {
  this.name = name; // 实例属性
}

// 静态属性
Player.game = "足球";
// 原型属性
Player.prototype.position = "前锋";

const player1 = new Player("梅西");
const player2 = new Player("C罗");

player1.position = "中场";
Player.game = "篮球";

console.log(player1.name);
console.log(player2.position);
console.log(Player.game);

2

以下代码中,car1.colorCar.colorcar2.getColor() 的输出结果分别是什么?

ini 复制代码
function Car(brand) {
  this.brand = brand;
  this.color = "白色"; // 实例属性
}

// 静态属性
Car.color = "黑色";
// 原型方法
Car.prototype.getColor = function() {
  return `颜色:${this.color}`;
};

const car1 = new Car("特斯拉");
const car2 = new Car("比亚迪");

car1.color = "红色";
Car.prototype.color = "蓝色";

console.log(car1.color);
console.log(Car.color);
console.log(car2.getColor());

3

执行以下代码后,user1.levelUser.leveluser2.getLevel()User.prototype.level 分别输出什么?

ini 复制代码
function User() {
  this.level = 1; // 实例属性
}

// 静态属性
User.level = 10;
// 原型属性
User.prototype.level = 5;

// 原型方法
User.prototype.getLevel = function() {
  return this.level + User.level;
};

const user1 = new User();
const user2 = new User();

user1.level = 3;
User.prototype.level = 7;
User.level = 20;

console.log(user1.level);
console.log(User.level);
console.log(user2.getLevel());
console.log(User.prototype.level);
相关推荐
linda26188 小时前
链接形式与跳转逻辑总览
前端·javascript
怪可爱的地球人8 小时前
骨架屏
前端
用户677847150628 小时前
前端将html导出为word文件
前端
前端付豪8 小时前
如何使用 Vuex 设计你的数据流
前端·javascript·vue.js
李雨泽8 小时前
通过 Prisma 将结构推送到数据库
前端
前端小万8 小时前
使用 AI 开发一款聊天工具
前端·全栈
咖啡の猫9 小时前
Vue消息订阅与发布
前端·javascript·vue.js
下一站丶9 小时前
【JavaScript性能优化实战】
开发语言·javascript·性能优化