前言
许多刚入门前端的朋友可能会难以理解js中的原型和原型链,对这个概念非常模糊,然而,理解原型链是成为JavaScript高手的必备技能之一 。下面,我将详细讲解这些概念,希望能帮助你更好地掌握它们。
在讲原型之前,我们先了解下js的对象
对象
对象是数据结构的一种基本形式,用于存储各种类型的数据。对象可以被视为一组无序的键值对(key-value pairs),其中键(key)通常是字符串或符号(Symbol),值(value)可以是任何数据类型,包括数字、字符串、布尔值、数组、函数或其他对象。
-
创建对象的方式
-
对象字面量
这是最简单且常见的创建对象的方式,只需要用一个{}来包含键值对就行,它可以在创建对象后随时添加、修改或删除属性和方法。但只适合创建单个对象,不适合批量创建多个具有相同属性和方法的对象。
jslet mei = { name:'小美', age:18, hometown:'北京', sayHi(){ console.log(`我叫${this.name},来自${this.hometown}`) } } mei.sayHi() // 我叫小美,来自北京
-
class类
ES6 引入了
class
关键字,使得面向对象编程更加简洁直观,比较类似于传统面向对象语言(如Java或C#)中的类。
jsclass Person { constructor(name, age) { this.name = name; this.age = age; console.log(this.name + this.age); } greet() { console.log('你好,我是' + this.name); } } let person1 = new Person('张三', 28); //张三28 let person2 = new Person('李四', 30); //李四30 person1.greet(); // 你好,我是张三
- 构造函数
- 构造函数通常用于实现面向对象编程中的类(尽管js并没有真正的类,直到ES6才引入了class关键字)。构造函数通过new运算符调用,通常是首字母大写用以区分普通函数,但这并不是强制性的,只是大家约定俗成的一种规矩
jsfunction Person(name,age){ console.log(this); // 会打印global this.name = name this.age = age } Person('小明',18) // 普通函数调用 let xiaoming = new Person('小明',18) // 构造函数调用
- 构造函数,可以创建多个具有相同属性和方法的对象实例,由this指针指向实例对象。
jsfunction Person(name, age) { this.name = name; this.age = age; } let person1 = new Person('张三', 28); let person2 = new Person('李四', 30); // 检查 this 指向 console.log(person1.name); // 输出: 张三 console.log(person2.name); // 输出: 李四
-
原型和原型链
原型
JavaScript对象都有一个内部属性 [[Prototype]]
,它指向该对象的原型对象。 __proto__
是一个访问器属性,允许你在对象上读取或设置 [[Prototype]]
属性,通过构造函数创建的对象拥有构造函数内部定义的属性和方法
scss
```js
function Person(name, age) {
this.name = name;
this.age = age;
}
// 为原型对象添加一个方法
Person.prototype.greet = function() {
console.log('你好,我是' + this.name);
};
let person1 = new Person('张三', 28);
// 访问 person1 的原型对象
console.log(person1.__proto__); // 输出: Person { greet: [Function: greet] }
// 检查 person1 的原型对象是否等于 Person.prototype
console.log(person1.__proto__ === Person.prototype); // 输出: true
// 调用实例上的方法
person1.greet(); // 输出: 你好,我是张三
// 动态添加一个新方法到原型对象
Person.prototype.sayHello = function() {
console.log('你好,世界!');
};
// 调用新添加的方法
person1.sayHello(); // 输出: 你好,世界!
// 创建另一个 Person 实例
let person2 = new Person('李四', 30);
// 调用新添加的方法
person2.sayHello(); // 输出: 你好,世界!
// 修改 person1 的原型对象
person1.__proto__ = {
greet: function() {
console.log('你好,我是新的' + this.name);
},
sayBye: function() {
console.log('再见!');
}
};
// 再次调用 person1 上的方法
person1.greet(); // 输出: 你好,我是新的张三
person1.sayBye(); // 输出: 再见!
```
原型链
试访问一个对象的某个属性或方法时,如果该对象自身没有这个属性或方法,JavaScript引擎会沿着原型链向上查找,直到找到该属性或方法为止,或者到达原型链的末端(即 null)。
ini
```js
let baseObject = {
baseMethod: function() {
console.log('这是基础方法');
}
};
// 定义一个中间对象,继承自 baseObject
let middleObject = Object.create(baseObject);
middleObject.middleMethod = function() {
console.log('这是中间方法');
};
// 定义一个最终对象,继承自 middleObject
let finalObject = Object.create(middleObject);
finalObject.finalMethod = function() {
console.log('这是最终方法');
};
// 尝试访问 finalObject 的属性和方法
finalObject.finalMethod(); // 输出: 这是最终方法
finalObject.middleMethod(); // 输出: 这是中间方法
finalObject.baseMethod(); // 输出: 这是基础方法
// 尝试访问一个不存在的属性
console.log(finalObject.someProperty); // 输出: undefined
// 检查原型链
console.log(finalObject.__proto__ === middleObject); // 输出: true
console.log(middleObject.__proto__ === baseObject); // 输出: true
console.log(baseObject.__proto__ === Object.prototype); // 输出: true
console.log(Object.prototype.__proto__ === null); // 输出: true
```
通过原型对象,可以实现方法的共享,提高性能,增强了代码的灵活性和可维护性,希望本文能帮助你更好地理解原型。