弄清楚原型链相等关系和原型、原型链存在的意义
前言
- JS 内函数即对象,分为函数对象和普通对象,每个对象都有
__proto__
属性,但只有函数对象才有prototype
属性。 - Object、Function 都是 js 内置的函数, 类似的还有常用到的 Array、RegExp、Date、Boolean、Number、String。
- 属性
__proto__
是一个对象,它有两个属性,constructor
和__proto__
。 - 原型对象 prototype 有一个默认的 constructor 属性,用于记录实例是由哪个构造函数创建。
原型、原型链相等关系理解
有以下构造函数 Person,他的原型上有所属国属性 motherland=‘China’
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.motherland = "China";
通过 new Person()创建的 person01 实例
let person01 = new Person("小明", 18);
设计 js 原型、原型链的时候遵从以下两个准则:
- 准则 1:原型对象(即
Person.prototype
)的constructor
指向构造函数本身。即Person.prototype.constructor == Person
- 准则 2:实例(即
person01
)的__proto__
和原型对象指向同一个地方。即person01.__proto__ == Person.prototype
// 从上方 function Foo() 开始分析这一张经典之图
function Foo()
let f1 = new Foo();
let f2 = new Foo();
f1.__proto__ = Foo.prototype; // 准则2
f2.__proto__ = Foo.prototype; // 准则2
Foo.prototype.__proto__ = Object.prototype; // 准则2 (Foo.prototype本质也是普通对象,可适用准则2)
Object.prototype.__proto__ = null; // 原型链到此停止
Foo.prototype.constructor = Foo; // 准则1
Foo.__proto__ = Function.prototype; // 准则2
// 从中间 function Object()开始分析这一张经典之图
function Object()
let o1 = new Object();
let o2 = new Object();
o1.__proto__ = Object.prototype; // 准则2
o2.__proto__ = Object.prototype; // 准则2
Object.prototype.__proto__ = null; // 原型链到此停止
Object.prototype.constructor = Object; // 准则1
// 所有函数的__proto__ 都和 Function.prototype指向同一个地方
Object.__proto__ = Function.prototype // 准则2 (Object本质也是函数);
// 此处有点绕
Function.prototype.__proto__ = Object.prototype; // 准则2 (Function.prototype本质也是普通对象,可适用准则2)
// 从下方 function Function()开始分析这一张经典之图
function Function()
Function.__proto__ = Function.prototype // 准则2
Function.prototype.constructor = Function; // 准则1
function foo() { }
f1 = new foo();
f2 = new foo();
foo.prototype.x = "hello";
f1.x => "hello"
f2.x => "hello";
f1.x = "goodbye"; //setting f1.x hides foo.prototype.x
f1.x => "goodbye" //hides "hello" for f1 only
f2.x => "hello"
delete f1.x
f1.x => "hello"; //foo.prototype.x is visible again to f1.
除了
Object
的原型对象(Object.prototype
)的__proto__
指向null
,其他内置函数对象的原型对象(例如:Array.prototype
)和自定义构造函数的__proto__
都指向Object.prototype
, 因为原型对象本身是普通对象。
Object.prototype.__proto__ = null;
Array.prototype.__proto__ = Object.prototype;
Foo.prototype.__proto__ = Object.prototype;
分析了这么多可以很清晰的画出下面这幅图。
原型、原型链的意义
原型对象的作用,是用来存放实例中共有的那部份属性、方法,可以大大减少内存消耗。
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.motherland = "China";
let person01 = new Person("小明", 18);
打印person01
, 他有自己属性 name = '小明'
,age = 18
; 同时通过原型链关系,他有属性motherland = 'China'
;
let person02 = new Person("小花", 20);
console.log(person02);
打印person02
, 他有自己属性 name = '小花'
,age = 20
; 同时通过原型链关系,他有属性motherland = 'China'
; 看出来了没有,原型对象存放了person01
、person02
共有的属性所属国motherland = 'China'
. 我们不用在每个实例上添加motherland
属性,而是将这一属性存在他们的构造函数原型对象上,对于人类Person
这样的构造函数。相同的属性、方法还有很多很多,比如我们是黑头发,我们都有吃,睡这样一个方法,当相同的属性、方法越多,原型、原型链的意义越大。
Person.prototype.hairColor = "black";
Person.prototype.eat = function () {
console.log(
"We usually eat three meals a day."
);
};
console.log(person01);
console.log(person02);