JavaScript原型与原型链

 

要了解原型和原型链,首先来看看构造函数:

构造函数

function Person(name,age){
    this.name = name;
    this.age = age;
}
var person1 = new Person('Tom',5);
var person2 = new Person('Jack',3);

要创建 Person 的新实例,必须使用 new 操作符。new操作符的原理:

  • 创建一个新对象;

  • 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象);

  • 执行构造函数中的代码(为这个新对象添加属性);

  • 返回新对象。

上面的例子中,person1 和 person2 都是构造函数 Person 的实例,这两个实例都有一个 constructor(构造函数)属性,该属性包含一个指向构造函数 Person 的指针。

console.log(person1.constructor == Person);//true
console.log(person2.constructor == Person);//true

事实上,说实例拥有构造函数不太准确,具体的应该是:

 

 

 

原型与原型链

放一张经典的图和我自己的理解:

 

只要创建了一个新函数,就会为该函数创建一个 prototype 属性,这个属性指向函数的原型对象。

所有原型对象都会自动获得一个 constructor (构造函数)属性,这个属性包含一个指向 prototype 属性所在函数的指针。

当调用构造函数创建一个新实例后,该实例的内部将包含一个指针[[prototype]](内部属性__proto__),指向构造函数的原型对象。

function Foo(){};
var f = new Foo();
console.log(Foo.prototype.constructor == Foo);//true
console.log(f.__proto__ == Foo.prototype);//true

所有通过 new Function() 创建的对象都是函数对象,可以理解为 Function 的实例对象,其内部属性__proto__指向Function.prototype。

function Foo(){};
var Foo2 = function(){};
var Foo3 = new Function('str','console.log(str)');

console.log(Foo.__proto__ == Function.prototype);//true
console.log(Foo2.__proto__ == Function.prototype);//true
console.log(Foo3.__proto__ == Function.prototype);//true

 

var obj = {};

它等同于下面这样:

var obj = new Object();

obj 是构造函数(Object)的一个实例。所以:

obj.constructor === Object              
obj.__proto__ === Object.prototype

创建对象的构造器不仅仅有 Object,我们也可以构造函数来创建 Function、Array、Number、Boolean、String、Date、RegExp对象,这些构造器也是函数对象(用Function构造的是函数):

console.log(Object.__proto__ == Function.prototype);//true
console.log(Function.__proto__ == Function.prototype);//true
console.log(Array.__proto__ == Function.prototype);//true
console.log(Number.__proto__ == Function.prototype);//true
console.log(String.__proto__ == Function.prototype);//true
console.log(Boolean.__proto__ == Function.prototype);//true

 

默认的原型:

所有引用类型默认都继承了 Object,而 这个继承也是通过原型链实现的。

所有函数的默认原型 prototype 都是 Object 的实例,因此默认原型都会包含一个内部指针__proto__,指向 Object.prototype。这也正是所有自定义类型都会继承 toString()、 valueOf() 等默认方法的根本原因。

console.log(Foo.prototype.__proto__ == Object.prototype);//true

console.log(Function.prototype.__proto__ == Object.prototype);//true
console.log(Array.prototype.__proto__ == Object.prototype);//true
console.log(Number.prototype.__proto__ == Object.prototype);//true
console.log(String.prototype.__proto__ == Object.prototype);//true
console.log(Boolean.prototype.__proto__ == Object.prototype);//true

//null是原型链的顶端
console.log(Object.prototype.__proto__ == null);

 

原型链的基本概念:

每个函数都有一个原型对象,原型对象都自动包含一个指向函数的指针,而实例都包含一个指向原型对象的内部指针。

假如我们让原型对象等于另一个类型的实例,此时的原型对象将包含一个指向另一个原型对象的指针,相应地,另一个原型对象中也包含着一个指向另一个函数的指针。

假如另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实例与原型的链条。

连接是存在与实例与原型对象之间的,即原型链的实现是通过__proto__实现的

console.log(Foo.prototype.constructor == Foo);//true
console.log(f.__proto__ == Foo.prototype);//true
//下面这个关系是通过原型链的搜索实现的
console.log(f.constructor == Foo);//true

实例对象本身是没有 constructor 属性的,实例包含一个内部属性__proto__,该属性仅仅指向了 Person.prototype。对 constructor 的访问是通过查找对象属性的过程来实现的。先搜索实例对象 f 本身,没有找到,则搜索Foo.prototype,在Foo.prototype中找到了constructor属性,该属性指向构造函数Foo。

红宝书中给出的栗子的关系图:

 

原型搜索机制:

访问一个实例属性时,首先会在实例本身中搜索该属性。如果没有找到该属性,则会继续搜索实例的原型。在通过原型链实现继承的情况下,搜索过程就得以沿着原型链继续向上。

posted @ 2018-09-09 11:08  叶子叶子耶  阅读(111)  评论(0编辑  收藏  举报