透彻解读prototype与__proto__

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zeping891103/article/details/79215201

请先看一看关于 prototype 与 __proto__ 的一些使用情况代码:

function Person() {}

// (1).Fun.prototype.*
Person.prototype.a = function() {
	console.log("a");
}

// (2).Fun.prototype.__proto__.*
Person.prototype.__proto__.b = function() {
	console.log("b");
}

// (3).Fun.prototype
Person.prototype = object;

// (4).Fun.prototype.__proto__
Person.prototype.__proto__ = object;

// (5).obj.__proto__
var person = new Person();
person.__proto__ = object;

这5种情况是 prototype 与 __proto__ 最常见的一些用法,有没有晕掉?不用怕,本篇文章教你如何把它们区分开,并了解它们之间的联系。

首先,要千万记住一点:__proto__是每个对象都有的一个属性,而prototype是函数才会有的属性!!! 上例中,function Person() { } 是函数,var person 则是对象,我们先来看函数。

几乎所有的函数(除了一些内建函数)都有一个名为prototype(原型)的属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以有特定类型的所有实例共享的属性和方法。prototype是通过调用构造函数而创建的那个对象实例的原型对象。有点绕口?我们用例子说明一下。

var p = {
	c: function() {
		console.log('c');
	}
}

function Person() {}
Person.prototype.a = function() {
	console.log("a");
}
Person.prototype.__proto__ = p;
Person.prototype.__proto__.b = function() {
	console.log("b");
}
var person = new Person();
person.a(); // 自对象方法 a
person.b(); // 继承对象方法 b
person.c(); // 继承对象方法 c
例子中定义了一个带有c方法的p对象,定义了一个Person函数。重点,Person.prototype可以看做是一个自身构造,暂称为 自对象;而Person.prototype.__proto__则是一个继承构造,暂称为 继承对象自对象和继承对象的属性和方法的并集组成了 Person函数的属性和方法。简单公式就是:自对象(属性、方法)∪ 继承对象(属性、 方法)=new 函数(属性、方法)。因此,上例中new Person( )新建出来的var person对象同时包含了自对象和继承对象的属性和方法,能成功执行a()、b()、c()。

(注)当自对象与继承对象间有相同属性或方法名称时,自对象优先级较高。

若此时有一个函数POP想要继承Person函数所有的属性和方法呢?这时请使用Object.getPrototypeOf( )。实现如下:

function POP() {}
POP.prototype.__proto__ = Object.getPrototypeOf(person);
var pop = new POP();
pop.a(); //a
pop.b(); //b
pop.c(); //c
思考一下,此处为什么要赋值给POP.prototype.__proto__而不是POP.prototype? 

再看另外一个例子:

var p = {
	c: function() {
		console.log('c');
	}
}

function Person() {}
Person.prototype.a = function() {
	console.log("a");
}
Person.prototype = p;

var person = new Person();
person.c(); // c
person.a(); // Uncaught TypeError: person.a is not a function
这段代码显示,自对象Person.prototype先是定义了a方法,随即将p对象赋值给自对象实现替换(此时原带a 方法的自对象已被替换),且没有额外定义继承对象Person.prototype.__proto__ ,因此新建出来的var person可以c执行方法,但执行a方法时发生错误。

什么是__proto__?对象具有属性__proto__,可称为隐式原型,一个对象的隐式原型指向构造该对象的构造函数的原型,这也保证了实例能够访问在构造函数原型中定义的属性和方法。看个例子:

function Person() {}
Person.prototype.a = function() {
	console.log("a");
}
Person.prototype.__proto__.b = function() {
	console.log("b");
}

var p = {
	c: function() {
		console.log('c');
	}
}

var person = new Person();
console.log(person.__proto__ === Person.prototype); // true

person.__proto__ = p; //会破坏原有自对象属性和方法
person.c(); // c 自对象方法
person.b(); // b 继承对象方法
person.a(); // Uncaught TypeError: person.a is not a function

person.__proto__实质上就是Person.prototype,例子中person.__proto__ = p;重新赋值,已经破坏了原有自对象数据,但并不影响继承对象,因此b方法可以执行,a方法执行时将找不到。

关于__proto__还有一种特殊情况,例子如下:

var p = {
	c: function() {
		console.log('c');
	}
}

var person = new Object();
person.a = function() {
	console.log("a");
}
person.__proto__ = p; //不会破坏已有原型链数据
person.a(); // a
person.c(); // c
console.log(person.hasOwnProperty("a")); //true 是否包括自有属性
console.log(person.hasOwnProperty("c")); //false 继承而来非自有,故为false

这种情况,a方法和c方法均能正常执行。

总而言之,Fun.prototype 为自对象 , Fun.prototype.__proto__ 为继承对象,自对象和继承对象的并集,构成了新建函数对象的属性和方法。obj(new Fun).__proto__实质上等于Fun.prototype,技巧性记住这几点即可。关于 prototype 与 __proto__ 的解读到此结束,欢迎大家多多交流。

猜你喜欢

转载自blog.csdn.net/zeping891103/article/details/79215201