文章目录
- 原型
- 原型链:
- 实际应用
- 面试题回答
原型
原型:每个函数都有prototype属性 称之为原型
因为这个属性的值是个对象,也称为原型对象
只有函数才有prototype属性
作用:
1.存放一些属性和方法
2.在Javascript中实现继承
const arr = new Array(1, 2, 3, 4)
console.log(arr.reverse)
为什么创建好了就可以使用这些方法呢? 就是原型的存在
Array构造函数也是一个函数 , 只要是函数就有个原型 就是Array.prototype
打开mdn
在这个原型身上已经挂载了很多方法, 这些方法是可以给所有的数组实例去使用的
为什么实例可以去使用这些方法呢 , 是因为_porto_
每个对象都有_proto_属性
作用: 这个属性指向它的原型对象
原型链:
关系总结:
构造函数 -> prototype -> 构造函数的原型对象
构造函数的原型对象 -> proto -> Object.prototype
Object.prototype -> proto -> null(原型链的终点)
代码演示:
function Person(name, age) {
this.name = name;
this.age = age;
}
const p = new Person("张三", 25);
// p 的 __proto__ 指向 Person.prototype
console.log(p.__proto__ === Person.prototype); // true
// Person.prototype 的 __proto__ 指向 Object.prototype
console.log(Person.prototype.__proto__ === Object.prototype); // true
// Object.prototype 是原型链的终点
console.log(Object.prototype.__proto__); // null
解释:
Person.prototype.proto 确实指向 Object.prototype。
Object.prototype 没有更上层的原型,所以它的 proto 是 null。
所以,构造函数的原型对象最终的 proto 是指向 Object.prototype,而 Object.prototype 的 proto 是 null,这是原型链的终点。
实际应用
- 共享方法,减少内存开销
在大型项目中,如果每个实例都独立创建相同的方法,会占用大量内存。原型可以让多个实例共享方法,从而提高性能。
示例:创建对象的优化
function User(name) {
this.name = name;
}
// 将方法放在 prototype 上,所有实例共享
User.prototype.sayHello = function() {
console.log("Hello, my name is " + this.name);
};
let user1 = new User("Alice");
let user2 = new User("Bob");
user1.sayHello(); // 输出: Hello, my name is Alice
user2.sayHello(); // 输出: Hello, my name is Bob
✅ 优点:所有 User 实例共享 sayHello 方法,而不是每次创建实例都生成新的方法,节省内存。
- 继承与扩展
在项目中,多个对象可能有相似的行为,我们可以利用原型继承来减少代码重复,提高复用性。
示例:父类与子类继承
function Animal(name) {
this.name = name;
}
// 父类原型方法
Animal.prototype.speak = function() {
console.log(this.name + " makes a sound");
};
// 子类
function Dog(name, breed) {
Animal.call(this, name); // 继承属性
this.breed = breed;
}
// 继承父类的方法
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
// 添加子类特有方法
Dog.prototype.bark = function() {
console.log(this.name + " barks!");
};
let dog1 = new Dog("Buddy", "Labrador");
dog1.speak(); // 输出: Buddy makes a sound
dog1.bark(); // 输出: Buddy barks!
✅ 优点:
Dog 继承了 Animal 的 speak 方法,无需重复定义。
Dog.prototype 还能额外扩展 bark 方法,增强灵活性。
3. 扩展原生对象
有时候我们需要扩展 JavaScript 自带的对象,比如 Array、String 等。
示例:扩展 Array.prototype
Array.prototype.sum = function() {
return this.reduce((total, num) => total + num, 0);
};
let numbers = [1, 2, 3, 4, 5];
console.log(numbers.sum()); // 输出: 15
✅ 优点:给所有数组对象增加 sum 方法,方便项目中复用。
⚠️ 注意:扩展原生对象可能会影响第三方库,应谨慎使用!
- 实现类的私有属性
在 ES5 时代,我们可以通过原型和闭包结合,模拟私有变量,防止外部直接访问。
示例:使用闭包模拟私有属性
function Person(name) {
let _age = 25; // 私有变量,外部无法直接访问
this.name = name;
this.getAge = function() {
return _age;
};
}
let p1 = new Person("Alice");
console.log(p1.name); // Alice
console.log(p1.getAge()); // 25
console.log(p1._age); // undefined,无法直接访问
✅ 优点:保护 _age 变量不被外部随意修改,提高安全性。
- 结合原型模式和单例模式
在实际开发中,我们可能希望某个对象只存在一个实例(比如配置管理、数据库连接池等)。可以结合原型模式和单例模式来实现。
示例:全局配置管理
const Config = (function() {
function Config() {
this.theme = "dark";
this.language = "en";
}
let instance;
Config.getInstance = function() {
if (!instance) {
instance = new Config();
}
return instance;
};
return Config;
})();
let config1 = Config.getInstance();
let config2 = Config.getInstance();
console.log(config1 === config2); // true,说明是同一个实例
✅ 优点:
只创建一个 Config 实例,避免重复初始化配置,提高效率。
总结
应用场景 | 代码示例 | 主要作用 |
---|---|---|
方法共享 | User.prototype.sayHello | 让多个实例共享方法,节省内存 |
继承与扩展 | Dog.prototype = Object.create(Animal.prototype) | 复用父类方法,减少代码重复 |
扩展原生对象 | Array.prototype.sum | 添加全局通用方法,增强功能 |
私有属性 | let _age = 25; | 保护数据,防止外部修改 |
单例模式 | Config.getInstance() | 确保对象全局唯一 |
在实际项目中,合理运用原型能有效提高代码复用性、减少冗余,并优化性能! 🚀
面试题回答
- 1.原型:函数都有prototype属性,称之为原型,也称为原型对象N原型可以放一些属性和方法,共享给实例对象使用
原型可以做继承 - 2.原型链:对象都有_proto 属性,这个属性指向它的原型对象,原型对象也是对象,也有_proto 属性,指向原型对象的原型对象,这样一层一层形成的链式结构称为原型链,最顶层找不到则返回 null