一篇文章搞懂原型、原型链、prototype、__proto__

在上一讲中,我们简单提到过 prototype 这一抽象化概念,那么在本讲中,我们来具体看一下到底什么是prototype原型,什么是原型链以及大家经常会遇到的问题之prototype/__proto__区别......blablaba

 

首先,需要大家明确一个概念 :

任意一个函数(包括构造函数)都有一个prototype属性,指向该函数的原型对象
任意一个构造函数实例化的对象,都有一个__proto__属性,指向构造函数的原型对象。
 
换言之:
每个构造函数(constructor)都有一个原型对象(prototype),
原型对象都包含一个指向构造函数的指针,
而实例(instance)都包含一个指向原型对象的内部指针.
 
看到这里,是不是有那么一点点绕~~ 下面上代码:
 
----------------------------------------------------------    一条分割线,千军万马来相见  (#_ #)  ---------------------------------------------------------
 
function Father(){
    this.property = true;
}
Father.prototype.getFatherValue = function(){
    return this.property;
}
function Son(){
    this.sonProperty = false;
}
// //继承 Father
Son.prototype = new Father();//Son.prototype被重写,导致Son.prototype.constructor也一同被重写var instance = new Son();
alert(instance.getFatherValue());//true

 

prototype 和 __proto__ :

function Person(name,age){
    this.name = name;
    this.age = age;
    this.sex = 'male';
}

var p1 = new Person('zhangsan','22');
var p2 = new Person('lisi','22');

console.log(Person.prototype);
console.log(p1.__proto__)
console.log(Person.prototype === p1.__proto__)//true
console.log(p1.__proto__ === p2.__proto__)//true


console.log(p1.constructor)//Person构造函数
console.log(p1.constructor === p2.constructor)//true
console.log(p1.age === p2.age)//true
console.log(p1.sex === p2.sex)//true

new出来的person1对象此时已经和 Person 再无联系了!

也就是说每一个new出来的实例都有自己的属性和方法的副本,是独立的的!修改其中一个不会影响另一个!此时,我们修改 p1 的 sex 属性值,结果如下:

p1.sex = 'female';
console.log(p1.sex);//female
console.log(p2.sex);//male
 
现在,我们再次回过头来看刚刚的Person构造函数,
function Person(name,age){
    this.name = name;
    this.age = age;
    this.sex = 'male';
}
var p1 = new Person('zhangsan','22');
var p2 = new Person('lisi','22');

我们现在希望构造函数中的 sex 属性是一个共有属性,因为此时用这样的方法,每个实例中都有一个相同的 sex 属性,会造成资源极大的浪费!

 

那么原型对象就即将登场了!
Brendan Eich决定给每一个构造函数都设置一个 prototype 属性,这个属性就指向原型对象。其实原型对象就只是个普通对象,里面存放着所有实例对象需要共享的属性和方法!
 

所以,我们把需要共享的放到原型对象里,把那些不需要共享的属性和方法存在在构造函数里!
function Person(name,age){
    this.name = name;
    this.age = age;
}
Person.prototype.sex = 'male';
var p1 = new Person('zhangsan','22'); // p1此时有name,age 两个属性
var p2 = new Person('lisi','22');
Person.prototype.home = 'Shanxi';
console.log(p1.home);
console.log(p2.home);
console.log(Person.prototype === p1.__proto__)//true
js引擎在读取一个实例化后的对象的属性或方法的时候(如p1)先要看这个对象是否具有该属性或方法,
如果没有,则会继续找它的原型(p1.__proto__),(如上面的例子)
 
如果有,则找该对象自身的,(如下面的例子),p1找自身的 sex 属性值,而p2要去原型链上找 sex 属性值。
p1.sex = 'female';   // p1此时有name,age,sex 三个属性,而p2还是只有name,age两个属性
console.log(p1.sex); // female
console.log(p2.sex); // male

 

 

function test(name) {
  this.name = name
}
test.prototype.getName = function (){
  return this.name
}
function test2(){

}
test2.prototype = new test('lisisi')
将test2的原型对象的指向改为test的实例对象,
test2在后续逻辑中就可以通过原型对象prototype继承(访问):
来自test构造器本身的属性和方法,以及来自test的原型对象prototype的属性和方法

!!!!!
如果要访问test2的属性或方法,那么就要去test里面去找,
但是这些属性和方法在test的构造器constructor内也找不到,
那么就会顺着test的prototype原型去test.prototype里面去找

 

console.log(test2.prototype.constructor)//test函数体,此时并不包括getName方法
//如果此时test2要访问getName,会通过test的prototype找到getName,因为test2的原型指向了test的实例


console.log(test2)
//test2原本的空的函数体。这说明继承并没有影响它本身的函数,只是改变了函数的prototype指向

var a = new test2();
console.log(a.getName())//lisisi

 

 

posted @ 2019-10-13 14:21  牧羊狼  阅读(416)  评论(0编辑  收藏  举报