js图解之-图解静态方法、私有方法、公有方法区别
- 首先,在方法之前,我们需要先铺垫一下我们的基础知识,从一砖一瓦开始,最终解决我们的问题
一、什么是面向对象
- 面向对象是一个伟大的编程思想,
- 本质:创建多个类,然后让类的实例去工作
- 思想:万事万物皆对象,小到数字、字符串,大到Function、Object内置类,他们的本质都是对象
- 三个重要概念:对象、实例、类(构造函数),我们需要注意的就是面向对象的这三个重要概念,接下来,我们就聊聊他们。
二、面向对象的三大重要概念
- 类(构造函数):用于构造出对象实例。
- 在我们日常生活中,我们其实经常会提到类这个概念。比如,生物被分为了植物类、动物类,植物类又能够被分为蔬菜类和水果类,动物类又能被分为哺乳类、昆虫类…。
- 而js中的类,也是这样,物以类聚,人以群分,类是复数。
- 划重点:每个构造函数中都会存在一个
prototype
属性
- 实例:类中的每一个成员,都是这个类的实例,每一个实例都具有私有属性和方法,共有属性和方法。
- 对象:在js中,万事万物皆对象,js中,除了Object.create(null)生成的对象,其他对象都会默认存在一个
__proto__
属性。- 划重点:每个对象的
__proto__
属性,都会指向构造这个实例的构造函数的prototype
属性
- 划重点:每个对象的
-
且看上图,我们能够得到一些结论
- 类是复数,狼是一种类,因为狼有很多只,当我说一只狼,你的内心只能刻画出狼的大致特性,但是这只狼是公的还是母的,你能确定吗?
- 对象是单数,灰太狼属于狼,但是当我说灰太狼时,你的内心能够刻画出这个狼的所有特点,它是公狼,它脸上有颗痣…,这就是对象
- 划重点:js中用new关键字进行构造函数的实例化
当我们了解了以上的基础概念后,我们再了解一下,类和对象在内存中存储的
三、类和对象在堆内存中的存储
- 类作为构造函数,在堆内存中存储时分为了三部分:[[scope]]、函数字符串、键值对
- 复习重点:构造函数中会默认存在prototype属性,prototype属性是一个对象(箭头函数、对象里面用ES6写法写的函数不能作为构造函数,这里不展开说明)
- 对象在栈内存中存储,会存放私有属性和方法
- 复习重点:只要是对象,就默认存在
__proto__
属性,这个属性默认指向构造它的构造函数的prototype属性
- 复习重点:只要是对象,就默认存在
根据以上的重点,上图:
- 因为prototype是对象,所以它就要开辟一个新内存
- 因为f1是Fn的实例,所以f1的
__proto__
指向Fn的prototype属性,所以他们两个指向同一个内存0x111
- 新问题:Fn.prototype也是对象,所以它也存在默认的属性
__proto__
,而它指向哪呢?- 划重点:默认情况下,任何函数的原型属性
__proto__
都是window.Object.prototype.
,因为构造函数的prototpe是一个对象,它是Object构造函数的实例。
- 划重点:默认情况下,任何函数的原型属性
---------------------------------------------------------了解了这些后,我们步入正题---------------------------------------------------
四、静态属性
-
先看代码:请问 fnName属性被放到了哪?
-
function Fn(){ } Fn.fnName="sjh"; let f1=new Fn();
-
看图:它被存到了Fn构造函数的键值对中对吧
-
五、私有属性
-
再看代码:slname,被存放到了哪里
-
function Fn(){ this.slname="xiaoming"; } Fn.fnName="sjh"; let f1=new Fn();
-
看图:只要是this.xxx的代码,都是在new这个构造函数是给它实例的私有属性,所以slname属性放到了f1实例里面
-
六、公有属性
-
再再看代码:请问prName属性被放到了哪里?
-
function Fn(){ this.fname="siyou"; } Fn.fnName="jingtai"; Fn.prototype.prName="gongyou"; let f1=new Fn();
-
上图片:我们就找到Fn.prototype对应的堆内存,把它放到哪里就好了
七、说区别
- 我们想找到fnname怎么找?:console.log(Fn.fnName),对吧
- 我们想找到slname怎么找?:console.log(f1.fnName),对吧
- 我们想找到prname怎么找?:看图片
- console.log(Fn.Prototype.prname)能不能拿到,
- console.log(f1.prname),也能拿到对吧
fnName就是Fn构造函数的静态属性
slname就是f1实例的私有属性
prname就是f1的公有属性
总结:
- 静态、私有、公有属性和方法他们最本质的区别是什么?
- 最本质的区别他们在堆内存中的存取位置不同。
- 存储方式:
- 静态属性和方法:被存放在Fn构造函数中,与prototype同级
- 私有属性和方法:被存放在实例化对象中
- 公有属性和方法:被存放在Fn的prototype属性中
- 调用方式:
- 静态属性和方法:构造函数.xxx
- 私有属性和方法:实例对象.xxx
- 公有属性和方法:
- 方式一:构造函数.prototype.xxx
- 方式二:实例对象.xxx
再举个例子:
var arr=[1,2,3,4];
arr.push(6);
Array.isArray(arr);
- .push()和.isArray()都是数组常用的方法,那么请问大家,为什么一个用arr调用,一个用Array调用呢。
- 这就是静态方法和公有方法的区别
- 因为.isArray()方法是被定义在Array内存中,与prototype平级的。
- 而.push()方法是被定义在Array.protoype对象属性中的,所有的Array类型的实例,都可以通过
__proto__
原型链找到它,而arr就是Array的实例,所以就能调用它。
补充:
- 图片中存在的constructor等属性方法,由于与主要问题关系不大,没有展开分析
- 对于所有对象都存在
__proto__
属性这句话,其实是不严谨的,因为当用Object.create(null)生成对象时,它不存在任何属性,包括__proto__
属性,这一特例大家记住就可以
——————以上仅代表我个人对问题的独特见解,可能有不严谨地方,也希望大家能指出,有问题也可以随时在评论中留言—————
——————————————————————实现共同富裕是我们最崇高的目标———————————————————————