文章目录
- [原型 (prototype)、原型链 (prototype chain) 、原型继承 (prototypal inheritance)](#原型 (prototype)、原型链 (prototype chain) 、原型继承 (prototypal inheritance))
-
- 1.原型是什么?
- [2.[[Prototype]] 又是什么? 和__proto__ 的差別是什么?](#2.[[Prototype]] 又是什么? 和__proto__ 的差別是什么?)
- [3.proto 属性和 prototype属性的差別是什麼?](#3.proto 属性和 prototype属性的差別是什麼?)
- 4.原型链是什么?
- 5.什么是原型链继承?
原型 (prototype)、原型链 (prototype chain) 、原型继承 (prototypal inheritance)
1.原型是什么?
在 JavaScript 中,每个对象都包含了一个 [[Prototype]] 内部隐藏属性,这个属性对应到的就是该对象的原型 (prototype) ,值有可能是 null 或是指向另一个对象。但因为 [[Prototype]] 是内部属性并无法直接被访问到,所以浏览器提供了__proto__ 的访问方法,可参考以下代码。
但要注意, __proto__ 方法并不在 ECMAScript 规范中,实际上开发中要取得对象的原型会使用 Object.getPrototypeOf。
__proto__方法这是一个历史遗留的坑
参考代码如下:
javascript
// Person 是一个构造函数
function Person(){}
//通过 Person 构造函数南湖,创建一个 personA对象
const personA=new Person();
//通过__proto__方法,查看 personA 的原型
console.log(personA.__proto__);
//给 Person 的原型增加一个属性sayHi
Person.prototype.sayHi =function(){
console.log('hi')
}
console.log(personA.__proto__);
//personA通过原型链"链接"到了 Person.prototype
console.log(personA.__proto__===Person.prototype)
// 使用 Object.getPrototypeOf API 查看原型,和__proto__方法所等价的
console.log(Object.getPrototypeOf(personA))
nodejs 环境输出如下:
javascript
{}
{ sayHi: [Function (anonymous)] }
true
{ sayHi: [Function (anonymous)] }
因为 nodejs 环境下只会输出可枚举属性,所以第一个只输出{},而不是浏览器中看到的{constructor: ƒ}
2.[[Prototype]] 又是什么? 和__proto__ 的差別是什么?
上面第一個問題已有提到,[[Prototype]] 是在 JavaScript 中物件的特殊隱藏屬性,但因為無法直接被訪問到,因此可以透過 __proto__ 的訪問方法。
3.proto 属性和 prototype属性的差別是什麼?
__proto__和prototype 是不同的属性。
-
__proto__是"每一个对象上的属性",它指向的是这个对象的"原型对象",也就是规范里的 [[Prototype]]。
-
prototype 是"构造函数"上的属性,用来指定,将来 new 出来的示例,他们的原型是谁
-
它们两个会指向同一个地方,这个地方就叫做原型对象
javascript
// Person 是一個構造函式
function Person() {}
// 透過 Person 構造函式,創建了一個 personA 對象
const personA = new Person();
personA.__proto__ === Person.prototype; // true
4.原型链是什么?
原型对象([[Prototype]])本身是一个对象,因此它也拥有自己的原型。当我们试图访问某个对象的属性时,如果该对象没有所需的属性,它会在其原型 (prototype) 中寻找。如果原型 (prototype) 中仍然没有找到,它将会继续往上一层查找,直到找到,或者到 null 为止。这条连续的路径被称为原型链 (prototype chain) ,;链的终点值为 null。
javascript
personA.__proto__.__proto__.__proto__ === null;
例如,我們经常使用数组的 filter 方法。假设现在有一个数组 「list」,我们在这个数组上使用 filter 方法。但事实上,filter 方法并不存在于这个 list,它存在于 Array 这个构造函数上。我们今天能使用 filter 方法,也是通过原型链 (prototype chain) 实现的。
5.什么是原型链继承?
假设有一个对象 「animal」,这个对象拥有自己的属性和方法。同时我们又想建立两个基于 「animal」 的对象,分別为「cat」和「dog」,这两个对象会有一些独特的方法和属性,但同时又需要用到「animal」对象的方法和属性。在 JavaScript 里,不需要通过复制或重新实现,就可以通过原型继承(Prototypal inheritance) 达成这个目的。
简而言之,「cat」和「dog」对象本身虽然没有「animal」对象的方法,但可以从他们的原型中继承方法来使用。实际操作上,我们会将属性或方法加到原型 (prototype) 上,那么所有从这个对象中实例出来的对象,都有办法使用这个方法或属性。
javascript
// 构造函数 Animal
function Animal() {}
// 实例
const cat = new Animal();
// 往原型对象加上方法
Animal.prototype.sleep = function () {
console.log("sleep");
};
// 使用构造函数的 prototype 的方法
cat.sleep(); // sleep