原型链是JavaScript的基础性内容之一。其本质是JavaScript内部的设计逻辑。
首先看一组代码:
1 <script type="text/javascript"> 2 function parent(){ 3 this.name="cehis"; 4 } 5 6 function son(){ 7 this.age=11; 8 } 9 10 function sub(){ 11 this.sex="nan"; 12 } 13 14 //简单实现下继承 15 son.prototype=new parent(); 16 sub.prototype=new son(); 17 18 var sunfromsub=new sub(); 19 var sonformson=new son(); 20 console.log(sunfromsub.name,sunfromsub.age,sunfromsub.sex); //cehis 11 nan 21 console.log(sonformson.name,sonformson.age,sonformson.sex); //cehis 11 undefined 22 23 //看一下__proto__这个属性 24 console.log(sunfromsub.__proto__); 25 console.log(sunfromsub.__proto__.__proto__); 26 console.log(sunfromsub.__proto__.__proto__.__proto__); 27 console.log(sunfromsub.__proto__.__proto__.__proto__.__proto__); 28 console.log(sunfromsub.__proto__.__proto__.__proto__.__proto__.__proto__);//null 29 30 console.log(sonformson.__proto__); 31 console.log(sonformson.__proto__.__proto__); 32 console.log(sonformson.__proto__.__proto__.__proto__); 33 console.log(sonformson.__proto__.__proto__.__proto__.__proto__);//null 34 </script>
总结一下知识点:
prototype是函数的一个属性(每个函数都有一个prototype属性),这个属性是一个指针,指向一个对象。它是显示修改对象的原型的属性。
__proto__是一个对象拥有的内置属性(请注意:prototype是函数的内置属性,__proto__是对象的内置属性),是JS内部使用寻找原型链的属性。
简单来说通过级联访问__proto__这个属性,就往js内部原型追溯。一直到null为止。
JavaScript原型链就是__proto__形成的链条。是一种静态的结构。
而JavaScript的继承是JavaScript内部的一种访问机制。这种机制就是回溯__proto__形成的链条。
结果:构造函数的实例可以继承构造函数自身的和原型上属性值。一直继承到null为止。
需要指出的是,JavaScript继承的方式有不止一种,上述代码中,构造函数实例既继承了构造函数的原型数据也继承了构造函数数据。
如果使用下面的代码实现继承:
1 function parent(){ 2 this.name="cehis"; 3 } 4 function son(){ 5 this.age=11; 6 } 7 8 son.prototype=new parent(); 9 son.prototype.id=111; 10 //只是继承构造函数原型上属性也就是继承id 11 var audience1=Object.create(son.prototype); 12 //既不会继承构造函数自身属性也不会继承构造函数原型属性 13 var audience2=Object.create(son); 14 15 console.log(audience1.id,audience1.age,audience1.name); //111 undefined "cehis" 16 console.log(audience2.id,audience2.age,audience2.name); //undefined undefined "son"
使用Object.create创建实例,实现继承,如果参数是构造函数本身则会继承构造函数的属性,但是不会继承其父元素的原型对象的属性。
如果参数是原型prototype则会继承父元素属性以及构造函数原型属性。
如果使用apply或者call方式实现继承。
1 function parent(name,age) { 2 this.name =name; 3 this.age=age; 4 } 5 parent.prototype.id=222; 6 function son(name,age,sex) { 7 parent.call(this,name,age); 8 this.sex =sex; 9 } 10 11 12 var example=new son("jack",12,"nan"); 13 console.log(example.name,example.age,example.sex,example.id);//jack 12 nan undefined
以上只是继承了构造函数自身的属性,而没有继承构造函数原型的属性。apply或者call本身是借用,而不是继承。
严格来说只有new 和 Object.create()才是继承。才会回溯。
更新2018年7月25 一个有趣的想象
1 function test(name){ 2 this.name=name; 3 } 4 5 console.log(test.__proto__.__proto__);//会连接到null 6 console.log(test.prototype);//含有constructor属性 7 console.log(test.prototype.prototype);//undefined 8 console.log(test.prototype.constructor);//自身 9 console.log(test.constructor.prototype.constructor); //这是一个无限循环
结论:只有沿着__proto__追溯才能追溯到根null。而不是沿着prototype,所以继承是因为__proto__。