视频加载失败

JavaScript面向对象之类和实例

2175 字
11 分钟
JavaScript面向对象之类和实例

本文学习JavaScript的构造函数、类和实例、了解原型链和继承

构造函数#

什么是构造函数

  • 用new调用一个函数,这个函数就被称为“构造函数”,任何函数都可以是构造函数,只需要用new调用它
  • 顾名思义,构造函数用来“构造新对象”,它内部的语句将 为新对象添加若干属性和方法,完成对象的初始化
  • 构造函数必须用new关键字调用,否则不能正常工作,正因 如此,开发者约定构造函数命名时首字母要大写

构造函数名称首字母约定大写

function People(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
var xiaoming = new People('小明', 12, '男');
var xiaohong = new People('小红', 10, '女');
var xiaogang = new People('小刚', 13, '男');

一定要记住:一个函数是不是构造函数,要看它是否用new 调用,而至于名称首字母大写,完全是开发者的习惯约定

如果不用new调用构造函数

function People(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
People('小明', 12, '男');
People('小红', 10, '女');
People('小刚', 13, '男');

这个时候会变成一个全局变量name,这个name的值为小刚

构造函数中的this不是函数本身

构造函数的this指向的是空对象,具体参考函数四步走

尝试为对象添加方法

function People(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
this.sayHello = function () {
console.log('我是' + this.name + ',我' + this.age + '岁了');
};
}
var xiaoming = new People('小明', 12, '男');
var xiaohong = new People('小红', 10, '女');
var xiaogang = new People('小刚', 13, '男');
xiaoming.sayHello(); //我是小明,我12岁了 规则1 this指向这个对象xiaoming
xiaohong.sayHello(); //我是小红,我10岁了
xiaogang.sayHello(); //我是小刚,我13岁了

这种方式并不推荐:

  • 每次 new People(),都会重新创建一个 sayHello 函数并挂到实例上,它们是不同的函数对象
  • 浪费内存,建了 3 个对象,就有 3 份完全一样的函数
  • 无法共享方法,方法本质应该是“行为”,不是“每个对象独有数据”。现在写法是:每个对象都“自带一份行为副本”

更推荐的方式:放到 prototype

function People(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
People.prototype.sayHello = function () {
console.log('我是' + this.name + ',我' + this.age + '岁了');
};

类和实例#

类好比是“蓝图”

  • 如同“蓝图”一样,类只描述对象会拥有哪些属性和方法, 但是并不具体指明属性的值

构造函数和“类”

  • 可以把构造函数看作是类,把new出来看作是实例
  • Java、C++等是“面向对象”(object-oriented)语言
  • JavaScript是“基于对象”(object-based)语言
  • JavaScript中的构造函数可以类比于OOP语言中的“类”, 写法的确类似,但和真正OO语言还是有本质不同,在后续 课程还将看见JS和其他OO语言完全不同的、特有的原型特性

关于更多class。再后续ES6会有更多讲解。

prototype和原型链查找#

什么是prototype

  • 任何函数都有prototype属性,prototype是英语“原型” 的意思

  • prototype属性值是个对象,它默认拥有constructor属性 指回函数

    函数 -》 函数.prototype
    函数.prototype -> 函数
    <body>
    <script>
    function sum(a, b) {
    return a + b;
    }
    console.log(sum.prototype);
    console.log(typeof sum.prototype);
    console.log(sum.prototype.constructor === sum);
    </script>
    </body>
  • 普通函数来说的prototype属性没有任何用处,而构造函数 的prototype属性非常有用

  • 构造函数的 prototype 对象,会成为“由它 new 出来的实例的原型对象”

  • 实例没有 prototype 属性!!实例有的是 *proto*

  • 构造函数的 prototype 是“未来实例共享方法的公共原型对象”

    ┌─────────────────────┐
    │ Function 构造器 │
    │ (Person函数本身) │
    └─────────┬───────────┘
    │ prototype
    ↓
    ┌────────────────────────────────┐
    │ Person.prototype │ ←── 公共方法区
    │ sayHello() 等共享方法 │
    └─────────┬──────────────────────┘
    │ __proto__
    ↓
    ┌────────────────────────────────┐
    │ Object.prototype │ ←── JS顶层原型
    │ toString() / valueOf() 等 │
    └─────────┬──────────────────────┘
    │ __proto__
    ↓
    null(原型链终点)
    ────────────────────────────────────────────
    实例对象层
    ┌─────────────────────┐
    │ const p = new │
    │ Person("小明") │
    └─────────┬───────────┘
    │ __proto__
    ↓
    ┌────────────────────────────────┐
    │ Person.prototype │
    └────────────────────────────────┘

一张图解释:

img
img

function People(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
var xiaoming = new People('小名', 12, '男');
//证明xiaoming的__proto__是People.prototype
console.log(xiaoming.__proto__ === People.prototype);//true

原型链的查找

JavaScript规定:实例可以打点访问它的原型的属性和方法,这被称为“原型链查找

当访问一个对象的属性/方法时,如果对象本身没有,就会沿着 __proto__ 一层一层往上找,直到找到或到 null 为止。

function People(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
People.prototype.nationality = '中国';//在构造函数的prototype上添加nationality属性
var xiaoming = new People('小明', 12, '男');
console.log(xiaoming.nationality);//中国;实例可以打点访问原型的属性和方法

原型链会有遮蔽效应

function People(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
People.prototype.nationality = '中国';
var xiaoming = new People('小明', 12, '男');
console.log(xiaoming.nationality);
var tom = new People('汤姆', 10, '男');
tom.nationality = '美国';
console.log(tom.nationality);//美国

hasOwnProperty

  • hasOwnProperty方法可以检查对象是否真正“自己拥有” 某属性或者方法

    xiaoming.hasOwnProperty('name'); // true
    xiaoming.hasOwnProperty('age'); // true
    xiaoming.hasOwnProperty('sex'); // true
    xiaoming.hasOwnProperty('nationality');// false

in

  • in运算符只能检查某个属性或方法是否可以被对象访问,不 能检查是否是自己的属性或方法

    'name' in xiaoming // true
    'age' in xiaoming // true
    'sex' in xiaoming // true
    'nationality' in xiaoming // true

提前预习

prototype 是“公共区域”,但只有“挂在同一条原型链上的对象”才能访问这个公共区域

function A() {}
A.prototype.a = 10;
function B() {}
const b = new B();
console.log(b.a); // undefined

为什么是undefined,原型链是这样的:

b
↓ __proto__
B.prototype
↓ __proto__
Object.prototype
↓
null

注意:没有经过 A.prototype所以B 和 A 是两条“原型链分支”,默认互不相通

什么时候“可以访问到 A 的 a”?B 的原型链上包含 A.prototype,也是继承的相关

function A() {}
A.prototype.a = 10;
function B() {}
B.prototype = new A(); // 关键!!!b继承了A的原型链
const b = new B();
console.log(b.a); // 10

在prototype上添加方法#

这在构造函数章节有提到过:

function People(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
this.sayHello = function () {
console.log('我是' + this.name + ',我' + this.age + '岁了');
};
}
var xiaoming = new People('小明', 12, '男');
var xiaohong = new People('小红', 10, '女');
var xiaogang = new People('小刚', 13, '男');
xiaoming.sayHello(); //我是小明,我12岁了 规则1 this指向这个对象xiaoming
xiaohong.sayHello(); //我是小红,我10岁了
xiaogang.sayHello(); //我是小刚,我13岁了
console.log(xiaoming.sayHello === xiaohong.sayHello)//false

把方法直接添加到实例身上的缺点:每个实例和每个实例的 方法函数都是内存中不同的函数,造成了内存的浪费

解决办法:将方法写到prototype上

function People(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
People.prototype.sayHello = function () {
console.log('我是' + this.name + ',我' + this.age + '岁了');
};

原型链的终点#

原型链的终点是 null,null 表示“没有原型”,是整个原型链的终点。

验证一下

console.log(Object.prototype.__proto__); // null

先来看下这图:

img
img

var arr = [11, 22, 33];
console.log(arr.__proto__ === Array.prototype);//true
console.log(arr.__proto__.__proto__ === Object.prototype);//true

继承#

先来看个例子:

function People(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
People.prototype.sayHello = function () {
console.log('你好,我是' + this.name + '我今年' + this.age + '岁了');
};
People.prototype.sleep = function () {
console.log(this.name + '开始睡觉zzzz');
};
function Student(name, age, sex, scholl, studionumber) {
this.name = name;
this.age = age;
this.sex = sex;
this.scholl = scholl;
this.studionumber = studionumber;
}
Student.prototype.study = function () {
console.log(this.name + '在学习');
};
Student.prototype.exam = function () {
console.log(this.name + '在考试');
};
//关键语句实现继承,把People的原型链继承到Student上
Student.prototype = new People();
//constructor 是“告诉你这个对象是由哪个构造函数创建的标签”,使用上面的继承后console.log(s.constructor); // People这是错误的
Student.prototype.constructor = Student;
// 重写&复写 先继承后重写
Student.prototype.sayHello = function () {
console.log('敬礼,我是' + this.name + '我今年' + this.age + '岁了');
};
var lilei = new Student('李雷', 22, '初中', 10010);
//调用People类的方法
lilei.sayHello();
lilei.sleep();
lilei.sayHello();

People类和Student类的关系

  • People类拥有的属性和方法Student类都有,Student类还扩展了一些属性和方法
  • Student“是一种”People,两类之间是“isakind of”关系
  • 实现继承的关键在于<子类必须拥有父类的全部属性和方法>,同时子类还应该能定义自己特有的属性和方法
  • 使用JavaScript特有的原型链特性来实现继承,是普遍的做法
  • ES6有新的继承方法

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

JavaScript面向对象之类和实例
https://f.youzi177.cn/posts/2026102007javascript-mian-xiang-dui-xiang-zhi-lei-he-shi-li/
作者
小封鸭
发布于
2026-10-20
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
小封鸭
小楼一夜听春雨,深巷明朝卖杏花
公告
欢迎来到我的博客。这里主要记录前端全栈和日常内容
分类
标签
最新动态
站点统计
文章
60
分类
3
标签
12
总字数
107,734
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
EdgeOne Pages
博客版本
Firefly v6.16.8
文章许可
CC BY-NC-SA 4.0
文章目录