一篇文章搞懂原型、原型链、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
作者:牧羊狼
出处:https://www.cnblogs.com/edwardwzw/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利,谢谢您的配合。
Freedom is not let you do whatever you wanna but teach you not to do the things that you donnot wanna do.