【学习打卡】JavaScript原型的自我总结与疑惑(一)
初学原型是从老师的视频入门的,根据老师的讲解,我理解到的是,构造函数里面定义复杂对象或者函数的时候,每次new一个新对象就得复制一遍,浪费空间;为了解决这个问题呢,就有了原型。通过引入原型,使得每个对象拥有一个原型对象,我的理解是原型对象是共享的,对象可以访问但是不能修改他的原型对象。但是这些都仅仅是我的猜想,那么针对这些猜想,我做了一些实验。
猜想一:new对象的时候,构造函数里面的变量会复制给新对象,对象改变自身方法或者属性,并不会影响构造函数;【是的】
猜想二:原型对象可自身修改自身;【是的】
猜想三:原型对象是共享的,对象只能访问但不能修改原型对象;【共享,对象可以修改原型对象】
【实验之前的准备:知识储备】来源:https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Objects/Object_prototypes
定义一个构造函数(例如Father),那么他自带一个原型对象(即Father.prototype),new一个Father对象时(例如son),那么son可以访问这个原型对象的属性和方法,每个浏览器访问的方式都不同,文中用的时son.__proto__,这不是一个标准,不推荐用作开发。每个原型对象也会拥有原型,并且从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链 (prototype chain),这些属性和方法定义在Object的构造器函数(constructor functions)之上的prototype
属性上,而非对象实例本身。在 JavaScript 中并不如此复制——而是在对象实例和它的构造器之间建立一个链接(它是__proto__属性,是从构造函数的prototype
属性派生的),之后通过上溯原型链,在构造器中找到这些属性和方法。
【开始实验咯】
猜想一,实验如下:
第一步:Father的gender的值直接固定为“m”,最后console.log(son.gender)的输出结果为m;
1 <script> 2 function Father(first, last, age, gender) { 3 this.name = { 4 'first': first, 5 'last': last 6 }; 7 this.age = age; 8 this.gender = "m" 9 } 10 var son = new Father('Mei', 'Lee', 18, 'f'); 11 console.log(son.name); 12 console.log(son.age); 13 console.log(son.gender); 14 </script>
第二步:son.gender重新赋值,比较Father.gender和son.gender的异同。代码如下:son.gender的输出为f,Father的gender值并未改变。
也就是说,改变对象并不会影响构造函数。即:猜想一成立。
var son = new Father('Mei', 'Lee', 18, 'f'); son.gender = 'f'; console.log(Father); console.log(son.gender);
猜想二:原型对象可自身修改自身;实验如下:
这个其实可以不用证明的,如果不能修改自身,那它怎么定义嘛,但是为了后文的发展,简单验证一下吧。第一段代码中,Father.prototype是没有speak属性的,第二段代码里面是有的。那么猜想二是正确的。
<script> function Father(first, last, age, gender) { this.name = { 'first': first, 'last': last }; this.age = age; this.gender = 'm' } console.log(Father.prototype); </script>
<script> function Father(first, last, age, gender) { this.name = { 'first': first, 'last': last }; this.age = age; this.gender = 'm' } Father.prototype.speak = 'chinese'; console.log(Father.prototype); </script>
另外附上一点疑惑,Father.prototype增加speak属性的时候,增加speak之前和之后分别打印Father.prototype,之前是要展开函数才能看到Chinese,之后是函数最外层就能看到;而且之前访问speak是访问不到的,不知道是什么原因。后来在挚友的帮助下,用IE执行了这段代码就不会有这种令人迷惑的问题,可能时chrome的内部执行顺序的问题,不知道会不会在项目执行中出啥问题。
<script> function Father(first, last, age, gender) { this.name = { 'first': first, 'last': last }; this.age = age; this.gender = 'm' } console.log(Father.prototype); console.log(Father.prototype.speak); Father.prototype.speak = 'chinese'; console.log(Father.prototype);
console.log(Father.prototype.speak);
</script>
运行结果如图:
猜想三:原型对象是共享的,对象只能访问但不能修改原型对象;共享是正确的,MDN文档中有提到【原型链中的方法和属性没有被复制到其他对象】,此处做个验证。但是对象可以修改其原型对象
分别通过对象son1和son2修改原型对象的speak属性,会发现属性真的会变化!也正如文档所说,原型链中的方法和属性没有被复制到其他对象,其他对象可以修改这些对象方法。
1 <script> 2 function Father(first, last, age, gender) { 3 this.name = { 4 'first': first, 5 'last': last 6 }; 7 this.age = age; 8 } 9 Father.prototype.speak = 'chinese'; 10 var son1 = new Father('Mei', 'Lee', 18); 11 son1.__proto__.speak = 'english'; 12 console.log(Father.prototype.speak); 13 console.log(son1.speak) 14 var son2 = new Father('Lei', 'Lee', 18); 15 son2.__proto__.speak = 'japanese'; 16 console.log(Father.prototype.speak); 17 console.log(son2.speak) 18 </script>
运行结果:
针对猜想3的一点延申:如果一个对象真的想用原型对象的一个属性或者方法,如果要修改,请自己重新定义,不要影响别的对象哦。如下面,如果真的想学外语,可以给自己定义个speak。
Father.prototype.speak = 'chinese'; var son1 = new Father('Mei', 'Lee', 18); son1.speak = 'english'; console.log(Father.prototype.speak); console.log(son1.speak) var son2 = new Father('Lei', 'Lee', 18); son2.speak = 'japanese'; console.log(Father.prototype.speak); console.log(son2.speak)
本文之外的一点疑惑,如果Father在函数体之外定义了一个属性或者方法,那么要怎样才能访问到这个方法?new的对象是否可以访问到这个方法,chrome的运行结果如下,菜鸡很疑惑。
1 function Father(first, last, age, gender) { 2 this.name = { 3 'first': first, 4 'last': last 5 }; 6 this.age = age; 7 this.gender = 'm' 8 } 9 console.log(Father); 10 console.log(Father.prototype); 11 var son1 = new Father('Mei', 'Lee', 18, 'f'); 12 Father.speak = 'chinese'; 13 var son2 = new Father('Lei', 'Lee', 18, 'f'); 14 console.log(Father); 15 console.log(Father.speak); 16 console.log(son1); 17 console.log(son1.speak); 18 console.log(son2); 19 console.log(son2.speak);