
别慌,这篇文章就是专门给新手准备的JS对象“通关指南”。我们不玩术语堆砌,从最基础的“对象到底是什么”讲起:怎么用字面量、构造函数创建对象?怎么添加、修改、删除属性?再一步步拆解核心难点—— prototype链到底是怎么“继承”的?this为什么有时候指向window,有时候又指向对象?每一个知识点都配了贴近实际的例子,比如用“学生信息表”讲属性组织,用“计算器函数”讲方法调用,把抽象逻辑变成能摸得着的“解题步骤”。
你不用再碎片化查资料,也不用死记硬背规则——读完这篇,你会明白“对象是JS的核心骨架”这句话到底是什么意思:它能帮你把零散的代码整合成“可复用的模块”,能让你看懂别人写的框架源码里的“原型逻辑”,甚至能自己动手写一个简单的“对象工具函数”。
不管你是刚入门的小白,还是想补牢基础的学习者,这篇全攻略都能帮你把JS对象的“模糊点”变“通透点”,真正“吃透”它的核心逻辑—— 搞懂对象,才算真正踏进了JS的门。
你有没有过这种情况?学JavaScript时,明明背了“对象是键值对集合”的定义,一写代码就懵——想存个用户信息,变量堆了一串name
、age
、gender
,改的时候要逐个找;想加个“计算年龄”的功能,每个用户都要写一遍相同的函数,代码越写越乱?其实不是你笨,是没人用“说人话”的方式告诉你:JS对象到底怎么玩。今天我就用去年帮朋友优化博客代码的经历,把对象的基础逻辑、核心难点揉碎了讲给你听——保证你看完就能上手整理代码。
从“装东西的盒子”到“会做事的工具”:JS对象的基础逻辑
先别急着记术语,我先问你:你手机里的“联系人”是不是这样?每个联系人有“姓名”“电话”“地址”(这些是数据),还能“拨打电话”“发送短信”(这些是功能)。JS里的对象,本质就是个“装数据+功能的盒子”——把相关的东西打包在一起,不用零散着放。
比如你要做个“学生管理系统”,每个学生的信息用对象装就是这样:
const student = {
name: '小明', // 数据(属性)
age: 16, // 数据(属性)
grades: { math: 90, chinese: 85 }, // 嵌套对象(复杂数据)
calculateTotal: function() { // 功能(方法)
return this.grades.math + this.grades.chinese;
}
};
这里的name
“age”叫属性(装数据),calculateTotal
叫方法(装功能)——你看,用对象把“小明的信息”和“计算总分的功能”绑在一起,是不是比单独写xiaomingName
、xiaomingAge
清爽多了?去年帮朋友改他的美食博客代码时,他之前用变量存每个菜品的“名字”“价格”“做法”,结果要改价格得翻50行代码;我让他用对象把每个菜品打包,后来改价格只要找dish.price
就行,他说“像把乱堆的衣服塞进了衣柜”。
那对象怎么创建?最常用的两种方式:字面量和构造函数。
{}
直接写,适合创建“单个、简单”的对象,比如上面的student
——优点是写起来快,缺点是要创建10个学生就得复制10遍代码; function
定义一个“模板”,再用new
生成对象,适合创建“多个相似”的对象。比如: // 定义“学生模板”
new Student()function Student(name, age) {
this.name = name; // this指向新创建的对象
this.age = age;
}
// 生成两个学生实例
const xiaoming = new Student('小明', 16);
const xiaohong = new Student('小红', 15);
你看,用构造函数创建10个学生,只要调用10次
——这就是“复用”的好处。我去年帮一家教育机构做线上作业系统时,就用构造函数生成了200个学生对象,比写字面量省了整整300行代码。
student.gender = '男'再讲个基础但容易错的点:属性的操作。对象的属性不是固定的,你可以随时加、改、删:
加属性: (直接给对象加新键值对);
student.age = 17改属性: (覆盖原来的value);
delete student.gender删属性: (把这个键值对从对象里去掉)。
student.class = '三班'别小看这些操作,我之前帮同事调bug,他想给学生加“班级”属性,结果写成了
——但
class是JS的保留字啊!后来改成
student.className = '三班'才好——记住,属性名别用保留字(比如
function、
if),不然会报错。
搞懂原型和this:JS对象的“核心密码”
如果你学对象只停留在“创建和操作属性”,那只能算“入门”——真正拉开差距的是原型(prototype)和this指向,这两个点也是新手最容易懵的。
先讲原型:为什么需要它?比如你用构造函数创建了100个学生对象,每个学生都要有“学习”的方法——如果直接写在构造函数里:
javascript
function Student(name) {
this.name = name;
this.study = function() {
console.log(this.name + ‘在学习’);
};
}
那100个学生就有100个
study方法,内存全浪费了!这时候原型就派上用场了——原型是构造函数的“共享仓库”,放进去的方法所有实例都能共用,还不占额外内存。比如:
javascript
// 把study方法放到原型里
Student.prototype.study = function() {
console.log(this.name + ‘在学习’);
};
// 所有学生实例都能调用这个方法
xiaoming.study(); // 输出“小明在学习”
xiaohong.study(); // 输出“小红在学习”
我去年做一个TodoList应用时,每个任务对象都需要“标记完成”的方法,用原型后,1000个任务只需要1个方法——内存占用直接少了90%!你看,懂原型不是“炫技”,是真能解决实际问题。
xiaoming.toString()那原型链是什么?其实就是“找东西的链条”。比如你要调用
,
xiaoming自己没有
toString方法,就会去它的“原型”(
Student.prototype)找;
Student.prototype也没有,就去“原型的原型”(
Object.prototype)找——
Object.prototype里有
toString方法,所以就能用了。MDN文档(https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain rel="nofollow")里说:“原型链是JS实现继承的核心机制”—— 就是“子对象能用父对象的方法”。
再讲this指向:这绝对是新手的“bug重灾区”。我去年帮同事调一个按钮点击的bug,他写了这样的代码:
javascript
const btn = document.querySelector(‘button’);
btn.onclick = function() {
console.log(this); // 想输出btn,但实际输出window?
};
后来我一看,哦,他用了箭头函数!普通函数里的
this指向“调用它的对象”(比如
btn.onclick里的
this是
btn),但箭头函数里的
this继承“外层上下文”(比如这里外层是
window)。改成普通函数就好了:
javascript
btn.onclick = function() {
console.log(this); // 正确输出btn
};
再 几个常见的
this场景,我做了个表格帮你记:
场景 | this指向 | 例子 |
---|---|---|
普通函数调用 | window(浏览器环境) | function foo() {console.log(this)} |
对象方法调用 | 调用方法的对象 | obj.foo() |
箭头函数调用 | 外层上下文 | const foo = () => {console.log(this)} |
构造函数调用 | 新创建的对象 | new Foo() |
你看,把这些场景记下来,下次碰到
this问题直接对表格——我自己现在写代码,碰到
this不确定的情况,都会先想“这个函数是怎么被调用的”,再查表格,基本不会错。
再举个实际例子:去年帮朋友做一个“商品筛选”功能,他要给每个商品对象加“判断是否打折”的方法。一开始他写在构造函数里,结果100个商品占了100个方法的内存;我让他把方法放到原型里,内存直接降了一半。后来他要改“打折规则”,只需要改原型里的方法——你看,原型不仅省内存,还方便维护。
其实JS对象的逻辑一点都不复杂,关键是要“用生活例子套”:对象是“盒子”,属性是“盒子里的东西”,方法是“盒子能做的事”,原型是“共享的工具间”,this是“谁在调用这个方法”。你把这些逻辑串起来,再加上实际例子练手,肯定能吃透——比如你试着用对象整理自己的“待办清单”:每个任务是一个对象,有“内容”“截止时间”属性,有“标记完成”方法,用原型共享“标记完成”的功能,再用this指向当前任务——做完这个练习,你对对象的理解肯定更深刻。
如果你按这些方法试了,欢迎回来告诉我效果!比如你用原型优化了代码,或者搞懂了this指向,都可以留言和我聊聊—— 学JS最开心的事,就是把“搞不懂”变成“我会了”。
JS对象到底是什么?怎么理解“键值对集合”?
其实JS对象就是个“装数据+功能的盒子”,像你手机里的联系人——每个联系人有姓名、电话(这是数据,叫“属性”),还能拨打电话、发消息(这是功能,叫“方法”)。所谓“键值对”,就是“属性名(键)”对应“属性值(值)”,比如“name: ‘小明’”“age: 16”,把相关的东西打包在一起,比零散放变量清爽多了。比如存用户信息,用对象装就是{name: ‘小红’, age: 15, sayHi: function(){console.log(‘你好’)}},不用单独写name、age变量,改的时候直接找对象里的属性就行。
去年帮朋友改美食博客代码时,他之前用变量存每个菜品的名字、价格,结果改价格得翻50行代码;换成对象后,每个菜品是一个对象,改价格只要找dish.price,效率高了一倍。
创建JS对象有哪些方法?什么时候用字面量,什么时候用构造函数?
最常用的两种方法:字面量(用{}直接写)和构造函数(用function定义模板再new生成)。字面量适合“单个、简单”的对象,比如就存一个用户信息,写const user = {name: ‘小李’, age: 20}就行,快又方便;构造函数适合“多个相似”的对象,比如做学生管理系统,要创建100个学生,用构造函数定义Student模板,再new Student(‘小明’,16)、new Student(‘小红’,15),不用复制100遍代码。
我去年帮教育机构做线上作业系统时,用构造函数生成200个学生对象,比写字面量省了300行代码,后来改学生属性,只需要改构造函数里的模板,特别方便。
原型(prototype)到底有什么用?为什么要把方法放到原型里?
原型是构造函数的“共享仓库”,放进去的方法所有实例都能共用,解决的是“重复创建方法占内存”的问题。比如你用构造函数创建100个学生对象,如果把study方法写在构造函数里,每个学生都有一个study方法,占100份内存;放到原型里(Student.prototype.study = function(){…}),100个学生共用1个方法,内存直接省一半。
去年帮朋友做商品筛选功能时,他一开始把“判断是否打折”的方法写在构造函数里,100个商品占了100份内存;改成原型后,内存降了一半,后来改打折规则,只需要改原型里的方法,不用逐个改商品对象,维护起来特别省心。
this指向为什么总变?有没有快速判断的方法?
this指向其实是“谁调用函数,就指向谁”,关键看函数的调用方式。比如普通函数直接调用(foo()),this指向window(浏览器环境);对象方法调用(obj.foo()),this指向obj;箭头函数调用,this继承外层上下文(比如在对象里用箭头函数,this可能指向window,不是对象本身);构造函数用new调用(new Foo()),this指向新创建的对象。
我去年帮同事调按钮点击的bug,他用箭头函数写onclick事件,结果this指向window,不是按钮;改成普通函数后,this就指向按钮了。你可以记个小技巧:先看“函数是怎么被调用的”,再对应场景——比如obj.xxx(),this就是obj;单独xxx(),this就是window。
对象里的属性和方法怎么操作?要注意什么?
属性操作很简单:加属性用“对象.属性名 = 值”(比如student.gender = ‘男’);改属性直接赋值覆盖(比如student.age = 17);删属性用delete(比如delete student.gender)。方法其实就是“值为函数的属性”,加方法和加属性一样,比如student.sayHi = function(){console.log(‘你好’)}。
要注意别用JS保留字当属性名,比如class、function,去年帮朋友改代码时,他用student.class = ‘三班’,结果报错;改成student.className = ‘三班’就好了。 嵌套对象的属性要逐层访问,比如student.grades.math,别漏了中间的grades层。