目录
- 原型
- 1. 对象原型
- 作用
- 特殊
- 获取
- 2. 函数原型
- 定义
- 作用
- 获取
- `constructor`属性
- 重写`prototype`
- Object
- 原型链
- 总结练习
- 原型关系图
JavaScript中是支持面向对象编程的,那么我们知道 面向对象有三大特性:
- 封装:将属性和方法封装到一个类中,称为封装的过程
- 继承:它是非常重要的,不仅可以减少重复代码,也是多态前提(纯面向对象中),它帮助我们将重复的代码和逻辑抽取到父类中,子类只需要直接继承过来使用即可
- 多态:不同的对象在执行时表现出不同的形态
原型和原型链构成了 JavaScript
的继承机制,使得对象能够共享属性和方法,我们必须理解原型和原型链才能明白继承的实现,继承实现具体学习这篇文章:https://juejin.cn/post/7399986979735781391
原型
1. 对象原型
JavaScript
当中每个对象都有一个特殊的内置属性 [[prototype]]
,这个特殊的对象可以指向另外一个对象,这就是对象的原型,也称隐式原型
- 在
JavaScript
中,任何对象的原型最终都会指向Object.prototype
,除非你明确地指定了另一个原型
作用
不管如何创建只要是对象都会有这样的一个内置属性,那么这个对象有什么用呢?
- 当我们通过引用对象的属性
key
来获取一个value
时,它会触发[[Get]]
的操作 - 首先检查该对象是否有对应的属性,如果有的话就使用它
- 如果对象中没有该属性,那么会访问对象
[[prototype]]
内置属性指向的对象里的属性
特殊
函数也是对象,它们也有 __proto__
属性,在 JavaScript
中,所有的函数都是 Function
构造函数的实例,这意味着每个函数的 __proto__
属性都指向 Function.prototype
function foo() {}
console.log(foo.__proto__);
console.log(foo.__proto__ === Function.prototype); // true
获取
- 方式一:通过对象的
__proto__
属性可以获取到(但是这个是早期浏览器自己添加的,虽然大多数现代浏览器都支持__proto__
,但它不是标准属性,应该尽量避免直接使用) - 方式二:通过
Object.getPrototypeOf(obj)
方法获取
var obj = {
name: "obj",
age: 18,
};
console.log(obj, obj.__proto__);
console.log(Object.getPrototypeOf(obj));
2. 函数原型
请先理解构造函数相关知识点,具体学习这篇文章https://juejin.cn/post/7399179026801868863
所有的函数都有一个prototype
的属性(注意:不是__proto__
),也称显式原型
定义
prototype
是一个对象,该对象在使用构造函数创建新的实例对象时会被赋值给实例对象的 __proto__
属性(具体学习new
操作符这篇文章:https://juejin.cn/post/7397399723601215488
- 当定义一个函数时,
JavaScript
引擎会自动为该函数创建一个prototype
属性 - 会将其初始化为一个包含
constructor
属性的对象,constructor
属性指向函数本身
function foo() {}
console.log(foo.prototype);
作用
使用 prototype
实现方法共享:当使用构造函数创建实例时,可以通过 prototype
属性为所有实例共享方法,这避免了在每个实例上都创建相同的方法,从而节省内存,参考学习总结练习代码
获取
function foo() {}
console.log(
foo.prototype,
foo["prototype"],
foo["prototype"] === foo.prototype
);
constructor
属性
- 每个函数的
prototype
对象默认都有一个constructor
属性,指向这个函数自身,这意味着你可以通过实例对象的原型找到它们的构造函数function Person(name, age) { this.name = name; this.age = age; } // 默认情况下,Person.prototype.constructor 指向 Person console.log(Person.prototype.constructor === Person); // 输出: true const john = new Person('John', 30); // john.__proto__ 是 Person.prototype console.log(john.__proto__ === Person.prototype); // 输出: true // john.constructor 指向 Person console.log(john.constructor === Person); // 输出: true
- 如果你重写
prototype
或者修改了构造函数的prototype
对象时,你需要手动修正constructor
属性,否则它会指向错误的构造函数 - 作用:
- 使用
constructor
确定对象的类型:function Car(make, model) { this.make = make; this.model = model; } const myCar = new Car('Toyota', 'Corolla'); // 使用 constructor 属性确定对象的类型 console.log(Car.prototype.constructor === Car); // 指向本身 console.log(myCar.constructor === Car); // 输出: true
constructor
用在运行时动态创建对象:function Person(name) { this.name = name; } const person1 = new Person('Alice'); const person2 = new person1.constructor('Bob'); console.log(person2.name); // 输出: Bob console.log(person2 instanceof Person); // 输出: true
- 使用
重写prototype
function Person() {}
Person.prototype = {
// constructor: Person, // 手动修正 `constructor` 属性, 但这样指向constructor会被枚举到
message: "hello",
info: {},
running: function () {
this.name + '在running'
},
eating: function () {
this.name + '在eating'
},
};
Object.defineProperty(Person.prototype, "constructor", {
enumerable: false,
configurable: true,
writable: true,
value,
});
Object
Object
是一个函数,是所有对象的构造函数
Object.prototype
- 当你创建对象时,例如通过
{}
或new Object()
,你实际上是在使用Object
这个构造函数 Object
作为一个函数,有自己的prototype
属性(这是一个对象),所有通过Object
构造的实例都共享这个原型对象,任何对象的原型最终都会指向Object.prototype
- 可以修改
Object.prototype
来为所有对象添加新的方法或属性,但这种做法不推荐,因为它会影响所有对象,可能导致意外的行为和兼容性问题
- 当你创建对象时,例如通过
Object.__proto__
Object
是一个函数,函数也是对象,因此它有__proto__
属性Object.__proto__
实际上指向Function.prototype
,因为在JavaScript
中,所有函数都是由Function
构造的
- 原型链的末端是
Object
构造函数的原型对象的原型(Object.prototype.__proto__
)即是null
// Object和Foo是一个函数也是对象,所以它有 __proto__ 属性,函数的__proto__都指向Function.prototype
function Foo() {}
console.log(Foo.__proto__ === Function.prototype)
console.log(Object.__proto__ === Function.prototype); // 输出: true
console.log(Function.prototype.__proto__ === Object.prototype); // 输出: true
// 字面量创建一个普通对象相当于const obj = new Object()
const obj = {};
console.log(obj.__proto__ === Object.prototype); // 输出: true
// 最顶层的 Object.prototype.__proto__ 为 null
console.log(Object.prototype.__proto__ === null); // 输出: true
// 可以修改,但不建议修改
Object.prototype.newMethod = function() { console.log('This is a new method'); };
原型链
原型链是指对象通过其原型属性([[Prototype]]
或 __proto__
)形成的链条
- 当访问对象的属性时,如果该属性在对象自身不存在,
JavaScript
引擎会沿着原型链向上查找 - 原型链的末端是
Object
构造函数的原型对象的原型(Object.prototype.__proto__
)即为null
总结练习
function Person(name, age, height) {
this.name = name;
this.age = age;
this.height = height;
}
Person.prototype.running = function () {
console.log(this.name + "在running");
};
Person.prototype.jumping = function () {
console.log(this.name + "在jumping");
};
var p1 = new Person("ablice", 20, 188);
var p2 = new Person("bob", 18, 180);
p1.running();
p2.jumping();
// 练习 打印什么 记得__proto__不是标准,不是练习用时要判断
Person.prototype.address = "中国";
p1.__proto__.info = "中国很大";
p1.height = 190;
p1.address = "深圳";
p2.isAdmin = true;
console.log(p1.address);
console.log(p2.address);
console.log(p1.isAdmin);
console.log(p2.isAdmin);
console.log(p2.info);
这个原型链图将整篇文章的知识点串联了起来,看图答案就呼之欲出了,图示如下:
- 14行打印:
ablice
在running
- 15行打印:
bob
在jumping
- 24行打印:深圳
- 25行打印:中国
- 寻找过程:
p2 --> p2.__proto__(Person.prototype) --> 中国
- 寻找过程:
- 26行打印:
undefined
- 寻找过程:
p1 --> p1.__proto__(Person.prototype) --> p1.__proto.__proto__(Object.prototype) --> p1.__proto.__proto__.__proto__ --> null(没找到)
- 寻找过程:
- 27行打印:
true
- 28行打印:中国很大
- 寻找过程:
p2 --> p2.__proto__(Person.prototype) --> 中国很大
- 寻找过程: