摘要:构造函数自身属性是直接赋值给它的所有,也就是说本身的属性,不管是直接类型还是引用类型都是分别复制一份给和,因此修改了的属性并不会影响到。
最基本的构造函数与实例的关系:
var Sub = function (color,list) { this.color = color; this.list = list } var sub1 = new Sub("red",[1]); var sub2 = new Sub("green",[2]); sub1.color = "new"; alert(sub1.color);//"new" alert(sub2.color);//"green",说明了sub2.color不会被sub1影响 sub1.list.push(4); alert(sub1.list);//[1,4] alert(sub2.list);//[2],说明引用类型array仍然不会被sub1影响
可以看出构造函数自身的属性(无论直接类型还是引用类型),都是赋值一份copy给它的所有instance,因此每一个instance 的修改互相不影响。我们继续看:
自身属性和prototype属性的区别:
var Super = function(color,list) { this.color = color; this.list = list } Super.prototype.newList = [10,10,10,10]; //创建Super的instance:①super1;②我们可以把Sub.prototype看成是Super的instance。 var super1 = new Super("red",[1,1,1,1]); var Sub = function () {}; Sub.prototype = new Super("green",[1]);//到这里我们可以看成 Sub.prototype和super1都是Super的instance var sub1 = new Sub(); var sub2 = new Sub(); //修改Super的`自身属性` Sub.prototype.list.push(4);//不影响兄弟(super1),但会影响自身的instance(sub1,sub2) alert(sub1.list);//[1,4] alert(sub2.list);//[1,4] alert(super1.list);//[1,1,1,1] 构造函数`自身属性`是直接赋值给它的所有instance,也就是说Super本身的属性(color,list,不管是直接类型还是引用类型)都是分别复制一份给super1和Sub.prototype,因此修改了Sub.prototype的属性(list)并不会影响到super1。反之亦然。 //修改Super的`prototype属性` Sub.prototype.newList.push(2,2,2,2); sub1.newList;//[10,10,10,10,2,2,2,2] super1.newList;//[10,10,10,10,2,2,2,2];构造函数的prototype里的属性只是提供一个指针给所有的instance,因此修改了Sub.prototype的属性(newList:引用类型; 直接类型是无法修改的,只能覆写)会影响到super1。修改Sub.prototype相当于是直接修改Super.prototype属性,因为它们通过原型链引用着同一个属性。
我们在来理清关于构造函数自身属性和prototype属性与instance之间的关系,先看图:
我们来总结一下:
Sub.prototype 和 super1 都是通过 new Super()产生的,我们把它们两个叫做兄弟;同理sub1和sub2也是兄弟
自身属性(图中a,B):无论是直接类型还是引用类型,兄弟间互不影响,各自拥有一份父函数的copy。修改只是在自身作用域里修改。比如说③和⑤都是①的实例,都拥有①中所有属性的copy,修改③的属性相当于在③中修改,不会影响其他人。
var Super = function(color,list) { this.color = color; this.list = list } Super.prototype.newList = [10,10,10,10]; //创建Super的instance:①super1;②我们可以把Sub.prototype看成是Super的instance。 var super1 = new Super("red",[1,1,1,1]); var Sub = function () {}; Sub.prototype = new Super("green",[1]);//到这里我们可以看成 Sub.prototype和super1都是Super的instance var sub1 = new Sub(); var sub2 = new Sub(); //修改Super的`自身属性` Sub.prototype.list.push(4);//不影响兄弟(super1),但会影响自身的instance(sub1,sub2),因为对于sub1,sub2来说list是原型属性而不是自身属性了,这里理解起来可能有点乱。 alert(sub1.list);//[1,4] alert(sub2.list);//[1,4] alert(super1.list);//[1,1,1,1] 构造函数`自身属性`是直接赋值给它的所有instance,也就是说Super本身的属性(color,list,不管是直接类型还是引用类型)都是分别复制一份给super1和Sub.prototype,因此修改了Sub.prototype的属性(list)并不会影响到super1。反之亦然。 //覆写 Sub.prototype.list = [0]; sub1.list;//[0] sub2.list;//[0] super1.list;//[1,1,1,1]
prototype属性(图中XXXXX):* 修改:比如说通过⑥去修改①的prototype的属性——sub1.newList.push(2,2,2,2),那么因为原型属性是引用而非复制,因此sub1.newList.push(2,2,2,2) ==> Sub.prototype.newList.push(2,2,2,2) ==> Super.prototype.newList.push(2,2,2,2),也就是sub1会沿着原型链一直查找到最终的Super.prototype,在Super.prototype里去修改newList属性,因此原型链上所有引用了改属性的实例都会被影响。
var Super = function(color,list) { this.color = color; this.list = list } Super.prototype.newList = [10,10,10,10];//创建Super的instance:①super1;②我们可以把Sub.prototype看成是Super的instance。 var super1 = new Super("red",[1,1,1,1]); var Sub = function () {}; Sub.prototype = new Super("green",[1]);//到这里我们可以看成 Sub.prototype和super1都是Super的instance var sub1 = new Sub(); var sub2 = new Sub(); //修改Super的prototype属性
Sub.prototype.newList.push(2,2,2,2); sub1.newList;//[10,10,10,10,2,2,2,2] super1.newList;//[10,10,10,10,2,2,2,2];构造函数的prototype里的属性只是提供一个指针给所有的instance,因此修改了Sub.prototype的属性(newList:引用类型)会影响到super1。修改Sub.prototype相当于是直接修改Super.prototype属性,因为它们通过原型链引用着同一个属性。
覆写:假如我们不是修改属性,而是直接覆写属性,那么情况也会不一样:
1.instance方法重载:
var Super = function(color,list) { this.color = color; this.list = list } Super.prototype.newList = [10,10,10,10]; //创建Super的instance:①super1;②我们可以把Sub.prototype看成是Super的instance。 var super1 = new Super("red",[1,1,1,1]); var Sub = function () {}; Sub.prototype = new Super("green",[1]);//到这里我们可以看成 Sub.prototype和super1都是Super的instance var sub1 = new Sub(); var sub2 = new Sub(); sub1.newList = [2,2,2,2];//覆写 sub1.newList;//[2,2,2,2] sub2.newList;//[10,10,10,10] super1.newList;//[10,10,10,10] sub2和super1不受影响,实例覆写方法只会在sub1自身的作用域里添加此方法,而不会修改到Super.prototype的方法
由于是覆写而不是修改,因此不会沿着原型链查找,而是在当前的作用域里添加该属性,而原来原型链上的那个属性依然还在,不受影响。这就实现了方法的重载。
2.父函数方法覆写:假如是在Super.prototype里对newList进行覆写,那么所有引用该属性的实例都将被影响。
var Super = function(color,list) { this.color = color; this.list = list } Super.prototype.newList = [10,10,10,10]; //创建Super的instance:①super1;②我们可以把Sub.prototype看成是Super的instance。 var super1 = new Super("red",[1,1,1,1]); var Sub = function () {}; Sub.prototype = new Super("green",[1]);//到这里我们可以看成 Sub.prototype和super1都是Super的instance var sub1 = new Sub(); var sub2 = new Sub(); Super.prototype.newList = [2,2,2,2]; sub1.newList;//[2,2,2,2] sub2.newList;//[2,2,2,2] super1.newList;//[2,2,2,2]
3.完全对prototype覆写:当我们使用XXX.prototype = YYY;对XXX.prototype进行完全覆写时,会彻底改变原型链。但是我们应该注意一点,覆写前的instance依然保持着对原有prototype的引用,因此原有的prototype中的属性不会被GC,依然保存在内存中,完全覆写后我们依然可以访问原来的instance所引用的属性和方法;而新创建的instance会指向新的prototype,因此无法再访问覆写前prototype中的属性。
var Super = function(color,list) { this.color = color; this.list = list } Super.prototype.newList = [10,10,10,10]; new Super; var Sub = function () {}; Sub.prototype.sayHello = function(){return "hello"}; var sub1 = new Sub();//完全覆写Sub.prototype前创建的instance Sub.prototype = new Super("green",[1]);//这里可以理解为对Sub的prototype进行完全覆写,因此会重新创建一个新的prototype指向Super.prototype var sub2 = new Sub();//完全覆写Sub.prototype后创建的instance //验证 sub1.__proto__.constructor == Sub;//true。依然引用着原来的原型链 sub2.__proto__.constructor == Super;//true。新的instance引用了新的原型链 sub1.sayHello();//"hello",依然能访问原有的属性,说明还保存在内存中。 sub2.sayHello();//"对象不支持sayHello",新的实例无法再引用原有的prototype
到这里整个关系就理清了,相信还是很多人看不懂,但假如你真的希望学好javascript,这篇文章足够你读10遍,理清原型关系将是你能否跨上另一个台阶的关卡,不理清这一层关系的话,后患无穷。我画了一张神一样的图,看得懂的话基本就能理清原型继承关系了。
ps: 图说明的是覆写的过程,请区分覆写和修改的区别。(Sub.prototype.list.push(1)是修改,Sub.prototype.list = [1]是覆写)
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/87601.html
摘要:前言作为前端高频面试题之一,相信很多小伙伴都有遇到过这个问题。 前言 作为前端高频面试题之一,相信很多小伙伴都有遇到过这个问题。那么你是否清楚完整的了解它呢? 国际惯例,让我们先抛出问题: 什么是原型、原型链 它们有什么特点 它们能做什么 怎么确定它们的关系 或许你已经有答案,或许你开始有点疑惑,无论是 get 新技能或是简单的温习一次,让我们一起去探究一番吧 如果文章中有出现纰...
摘要:每个原型对象都有一个属性指向关联的构造函数为了验证这一说话,举个例子。 本文共 1475 字,读完只需 6 分钟 一、概述 在 JavaScript 中,是一种面向对象的程序设计语言,但是 JS 本身是没有 类 的概念,JS 是靠原型和原型链实现对象属性的继承。 在理解原型前,需要先知道对象的构造函数是什么,构造函数都有什么特点? 1. 构造函数 // 构造函数 Person() ...
摘要:这正是我们想要的太棒了毫不意外的,这种继承的方式被称为构造函数继承,在中是一种关键的实现的继承方法,相信你已经很好的掌握了。 你应该知道,JavaScript是一门基于原型链的语言,而我们今天的主题 -- 继承就和原型链这一概念息息相关。甚至可以说,所谓的原型链就是一条继承链。有些困惑了吗?接着看下去吧。 一、构造函数,原型属性与实例对象 要搞清楚如何在JavaScript中实现继承,...
摘要:深入理解原型与继承看过不少书籍,不少文章,对于原型与继承的说明基本上让人不明觉厉,特别是对于习惯了面向对象编程的人来说更难理解,这里我就给大家说说我的理解。 深入理解:JavaScript原型与继承 看过不少书籍,不少文章,对于原型与继承的说明基本上让人不明觉厉,特别是对于习惯了面向对象编程的人来说更难理解,这里我就给大家说说我的理解。 首先JavaScript是一门基于原型编程的语言...
摘要:探索是如何判断的表达式如果函数的显式原型对象在对象的隐式原型链上,返回,否则返回是通过自己产生的实例案例案例重要注意的显示原型和隐式原型是一样的。面试题测试题测试题报错对照下图理解 原型与原型链深入理解(图解) 原型(prototype) 函数的 prototype 属性(图) 每个函数都有一个prototype属性,它默认指向一个Object空对象(即称为:原型对象) 原型对象中有...
阅读 2845·2021-11-11 10:58
阅读 1875·2021-10-11 10:59
阅读 3463·2019-08-29 16:23
阅读 2303·2019-08-29 11:11
阅读 2766·2019-08-28 17:59
阅读 3791·2019-08-27 10:56
阅读 2024·2019-08-23 18:37
阅读 3085·2019-08-23 16:53