再和“面向对象”谈恋爱 - 对象相关概念(二)

上一篇文章把对象的概念讲解了一下,这篇文章要重点解释最让大家犯迷糊的一些概念,包括

  • 构造函数
  • 实例
  • 继承
  • 构造函数的属性与方法(私有属性与方法)
  • 实例的属性与方法(共享属性与方法)
  • prototype(原型)
  • __proto__(原型)

构造函数

构造函数依然是个函数,用来生成对象。所有的对象都是由构造函数创建的

对象哪来的?构造函数生的。而普通函数不能生成对象(不孕不育),构造函数可以生成对象(有生育能力)。每个对象都会有一个自己对应的构造函数,但是不代表每个构造函数都会生(生成实例),有不会生的构造函数,例如Math对象(不孕不育)

console.dir(Array);     //数组的构造函数
console.dir(Function); //函数的构造函数
console.dir(Object);   //对象的构造函数

new Math();     //报错,不能生成实例
new Window();   //报错,不能生成实例

唐僧说过一句话,用来解释构造函数与实例再合适不过了“人是人他妈生的,妖是妖他妈生的”那这里的人他妈与妖他妈就是构造函数。人与妖其实是下面要说的概念“实例”

实例

实例就是对象,由构造函数生成

平时用的实际的东西都是实例(获取的DOM元素、声明的数组、声明的函数、声明的对象),有时候需要new关键字生成(不是绝对的)

  • 实例身上有一个constructor属性,它指向对应构造函数(生母)
  • 判断一个对象是否是某个构造函数的实例,用instanceof运算符
//实例
[];             //不用new生成
new Array();    //用new生成

[].constructor===Array; //true
[] instanceof Array;    //true

继承

生成的实例具有构造函数身上的属性与方法(就像老鼠的儿子会打洞)
一个对象身上有另一个对象身上的属性或方法,这种具有的方式就叫继承

说到继承首先想到的就是遗产。老人去世了,在二环里留了一套四合院,那这套房子归谁呀?不可能归我吧,归我的话我就不用在这吭哧吭哧写文章了。那也不能归你吧,归你了你也不能座这看我吭哧吭哧写的文章。得归人家儿子,这个就是继承。那从这个例子中得出几个重要信息,继承者与被继承者之间是有一定关系的,最简单的就是父子或者母子关系。

那回到程序中,构造函数就是老人,实例就是儿子。那实例的属性或者方法哪来的?构造函数的,不过它可以继承过来,这是合理合法的。但是也会有特殊情况,假如老人膝下无子,那这房子怎么办?充公?不能吧,要你你干么?不过这个老人有一个唯一的亲人,就是弟弟。那弟弟能否拿到这个房子呢,应该是可以的。再回到程序中,刚才这种情况其实程序中也存在,就是一个对象可以有另一个对象身上的东西。

//arr身上是并没有push方法,这个方法来自于构造函数Array,是arr继承了构造函数身上的这个方法
const arr=[1,2];
arr.push(3);
console.log(arr);   //[1, 2, 3]

//Array对象身上并没有valueOf方法,这个方法来自于Object对象,是Array对象继承了Object对象的这个方法
Array.valueOf===Object.valueOf;   //true

构造函数的属性与方法(私有属性与私有方法)

构造函数身上的属性与方法,只有构造函数能用,实例不能用

再回到这1个亿的例子中来,这个老人为啥有个四合院呢,这位老人原来是个老兵,立过一等功,战功赫赫,获得了无数勋章。那我问你他的这些荣誉,儿子有么?或者弟弟有么?没有吧。这个就是构造函数特有的属性与方法,实例身上是没有的

//构造函数的私有属性与方法
console.log(Array.name);    //Array
console.log(Array.of(5));   //[5]

//实例不能用
const arr=[];   //实例
console.log(arr.name);      //undefined
arr.of(6);  //报错

##实例的属性与方法(共享属性与共享方法)

实例身上的属性与方法,只有实例能用,构造函数不能用(放在prototype里)

老人有两个孩子,这俩孩子跟我学会了js,又学会了ES6,现在在百度上班做开发,这你说扯不扯,我都快编不下去了。那这些技能老人会么,他不会。所以这个就叫实例的属性与方法,只能实例去用,构造函数用不了

//实例的方法
const arr=[1,2,3];
console.log(arr.concat([‘a‘,‘b‘])); //[1, 2, 3, "a", "b"]

Array.concat([‘a‘,‘b‘]));   //报错。你的就是你的,我的就是我的,不能互用。就跟媳妇是一样

//但构造函数可以间接用
console.log(Array.prototype.concat([‘a‘,‘b‘])); //["a", "b"]

prototype(原型)

1、构造函数身上的一个属性,它的类型为对象,这个属性的值就是原型
2、这个对象里放的属性与方法,就是构造函数的共享属性与共享方法,所有实例都能用

还得回到那一个亿的例子里,我们说儿子能继承老子的遗产,为什么呢?因为他有个神器叫户口本,国家给发的,能证明他们是父子关系。你去办手续的时候肯定要拿着户口本。现在都得要证件,当然有的时候你可能需要到派出所开个证明,证明你就是你。虽然有点扯哈,但是有真实发生过,如果派出所不给你证明,那你就不是你。回到程序中,虽然你知道实例是构造函数生的,那实例就能有构造函数身上的方法,为什么呢?其实他们也有证,跟户口本一样,这个证就是prototype

//prototype 原型
console.log(Array.prototype);   //数组的原型对象

//如果把原型上的方法删除了,那实例就不能用了。证明原型里放的属性与方法都是实例的属性与方法
const arr=[];
Array.prototype.push=null;
arr.push(6);    //报错

__proto__(原型)

1、这个属性是浏览器自己部署的,到了ES6也没有正式写入标准里,建议大家不要用它,用Object.getPrototypeOf()方法替代
2、它也是指原型对象,与prototype一样。但是有区别:归属不同,prototype是函数身上的属性,__proto__是对象身上的属性

这个属性与prototype属性往往让大部分人都百思不得其解,看得是一头雾水,脑袋拧成了麻花,网上的资料是一堆一堆,但往往大家看得是一愣一愣。其实这俩东西很简单,是一道推算题,首先,你要明白原型的结果只有一个,就是构造函数的prototype属性的值。那为什么浏览器看热闹不嫌事大,给我们找麻烦,又部署了一个__proto__呢?其实浏览器也是好心,但没成想办了坏事。在没有这个属性以前,只能从函数身上找到原型。为了能从实例对象身上也能找到原型,浏览器就部署了这个属性。以字符串为例推算如下:

字符串构造函数的原型放在String.prototype里。那现在我能否通过实例找到这个原型呢?也就是str ? ===String.prototype

const str=new String(‘kaivon‘);
console.dir(str);   //打印出实例,点开后看到__proto__


要通过实例找到原型的话,首先要通过实例找到构造函数(因为原型在构造函数身上)。前面有说过,实例身上都有一个属性叫constructor,这个就指向构造函数

console.log(str.constructor===String);  //true 通过实例找到了构造函数


那找到了构造函数,原型不就放在构造函数身上么?所以变成了这样

console.log(str.constructor.prototype===String.prototype);  //true


到这里就推出来如何通过实例找到原型,但是这么写是不是有点长呢?天空一声巨响,__proto__闪亮登场,浏览器为了简化操作,就让__proto__等于constructor.prototype,也就是变成下面这样

console.log(__proto__===constructor.prototype); //true


所以整个语句其实就可以变成这样

console.log(str.__proto__===String.prototype);  //true


到这里就明白了吧,再总结一下

  • prototype属性是构造函数身上的,指向原型
  • __proto__属性是对象身上的,指向原型
  • 实例的.__proto__===构造函数.prototype


现在是不是恍然大悟、如梦初醒、豁然开朗,但是我不得不浇你一头冷水啊,还没有完,看下面

console.dir(String);//打印出构造函数,点开也看到了__proto__



刚才不是说这玩意是实例身上的么,现在怎么跑到构造函数身上了?童话里都是骗人的吗?别急,构造函数是函数么?函数是对象么?是吧,这就对了。其实上面的推理题都好说,都简单。大家弄不懂的是在这。网上的文章就在这里让你的麻花越拧越紧。构造函数是函数,是函数它就是对象,__proto__既然是对象身上的,那这个构造函数身上就一定会有。点开__proto__看一下


里面的内容不应该是String原型的内容么?好像不是哎,这就×××了,刚搞清楚的东西现在全乱了。别急,记住那句话__proto__永远指向实例对象对应的构造函数的prototype,那就先看实例对象是谁。我们点开的这个String它是什么?它是构造函数,它的类型是个函数。虽然它是一个构造函数,但在这里它就是一个实例对象,并且它的类型是函数,所以它是Function构造函数的实例,那Function的实例对象身上的__proto__不应该指向Functionprototype么?所以这里面的内容为Function对象的原型

console.log(String.__proto__===Function.prototype); //true


接着看,这里还有一个__proto__


真是害怕什么来什么呀!这是__proto__来自于Functionprototype,它是个原型对象,原型对象的数据类型当然为对象了,所以它是Object构造函数的实例,那Object的实例对象身上的__proto__不应该指向Objectprototype么,所以点开这个__proto__里面的内容是Object.prototype的值

console.log(String.__proto__.__proto__===Object.prototype); //true


因为Object已经是顶层对象了,所以在它的prototype里不会再出现__proto__了,但是有人说Object其实还有继承,继承于null。但是我不太认同这种说法,__proto__的值是个对象类型数据,而Object已经是顶层对象了,它原型对象的__proto__肯定没有值了,在ECMAScriptnull的数据类型又为对象,所以就呼应上了,而不是继承于null

Object.prototype.__proto__===null;  //true


到这里我把面向对象当中关键的一些概念算是说清楚了,下一往篇文章来说一下真正的面向对象概念!

原文地址:http://blog.51cto.com/13258913/2133669

时间: 2024-11-09 06:40:37

再和“面向对象”谈恋爱 - 对象相关概念(二)的相关文章

再和“面向对象”谈恋爱 - 对象简介(一)

JavaScript中的面向对象是个老生常谈的话题,但是依然有很多小伙伴处于懵逼的状态.面试时候最担心的就是被问到面向对象相关的内容,自己看过无数的资料,依然对面向对象百思不得其解.到底什么是原型?什么是构造函数?什么是继承...一提到这些概念那真是欲哭无泪.悲愤交加,甚至恨的直咬牙!所以有必要重新再谈一次面向对象.同时我也希望通过这几篇文章让你对面向对象由恨生爱,再到爱恨交加,最后爱得死去活来! 对象 ???各位老铁们应该听过一个词叫"一切皆对象",而"面向对象"

再和“面向对象”谈恋爱 - 图片预加载组件(七)

再和"面向对象"谈恋爱 - 对象简介(一)再和"面向对象"谈恋爱 - 对象相关概念(二)再和"面向对象"谈恋爱 - 面向对象编程概念(三)再和"面向对象"谈恋爱 - class(四)再和"面向对象"谈恋爱 - 继承(五)再和"面向对象"谈恋爱 - super(六) 通过前面的六篇文章已经把ES6的面向对象跟大伙说清楚了,大家最关心的应该是来个例子实战一下,别担心自行车都会有.那这篇文章通

再和“面向对象”谈恋爱 - class(四)

在上一篇文章里我介绍了一下面向对象编程的概念,在最后终于喜出望外看到了ES6提供了类的概念了.那这个类如何去用,是这篇文章的主题.ES6给我们提供了一个class关键字.这个关键字跟以前的var let const很像,它们都是用做声明的,而class就是用来声明一个类的. 语法 class name [extends]{ //extends是用来继承的,可选参数 //class body }; 注意 class不能重复声明(与let.const一样) 类的本质还是一个构造函数 class Di

再和“面向对象”谈恋爱 - 继承(五)

通过上一篇文章想必各位老铁已经熟悉了class了,这篇文章接着介绍继承.面向对象里最大的特点应该就属继承了.一个项目可能需要不断的迭代.完善.升级.那每一次的更新你是要重新写呢,还是在原有的基础上改吧改吧呢?当然,不是缺心眼的人肯定都会在原来的基础上改吧改吧,那这个改吧改吧就需要用到继承了. 在第二篇文章里说过原型实例跟构造函数之间的继承,并且还讲了一道推算题.最终我们明白,实例为什么能继承原型上的内容是因为prototype,所以在ES5里面想要继承的话就得通过原型,需要对prototype进

再和“面向对象”谈恋爱 - super(六)

在上一篇文章里介绍了继承,那其中说过一个很关键的东西想要继承子类里里必需先调用一个super方法.而super的作用绝对是价值连城!同时super的作用还有多种,并且跟你的使用环境有关系. 1.当作函数使用 super被当作函数使用,这种情况是最普遍的,上一篇文章里已经使用过.它有以下几种作用: super作为函数调用时,代表父类的构造函数 调用super后,this会被改成子类 只能用在构造函数里,用在其它地方报错 { class Father{ constructor(){ console.

面对对象 谈恋爱五大原则浅析!

面向对象编程其实无时无刻不在向我们阐释婚恋观 面向对象编程5大原则,想来跟处对象是一样一样的: 单一职责原则(SRP): 做一个单纯的人,才会有简单<稳稳的幸福-陈奕迅>: 一个类应该仅有一个引起它变化的原因 开放封闭原则(OCP):悟以往之不谏,知来者犹可追: 既开放又封闭,对扩展是开放的,对更改是封闭的! 里氏替换原则(LSP):花心大萝卜才能永远立于不败之地: 子类可以替换父类并且出现在父类能够出现的任何地方,面向接口编程gof 依赖倒置原则(DIP): 谈恋爱经济也要独立呀: 传统的结

浅谈Java回收对象的标记和对象的二次标记过程_java - JAVA

文章来源:嗨学网 敏而好学论坛www.piaodoo.com 欢迎大家相互学习 一.对象的标记 1.什么是标记?怎么标记? 第一个问题相信大家都知道,标记就是对一些已死的对象打上记号,方便垃圾收集器的清理. 至于怎么标记,一般有两种方法:引用计数和可达性分析. 引用计数实现起来比较简单,就是给对象添加一个引用计数器,每当有一个地方引用它时就加1,引用失效时就减1,当计数器为0的时候就标记为可回收.这种判断效率很高,但是很多主流的虚拟机并没有采用这种方法,主要是因为它很难解决几个对象之间循环引用的

摇摇开门:支付宝的痼疾、做产品与谈恋爱

好久没有打开支付宝了.一是因为自己平时没多少时间购物,二是因为这支付宝,现在每次打开都特别的慢,很多的加载项,整个支付宝几十M的大小,就算打开,里面的弹窗以及各种提示一大堆,几分钟之内不能好好的使用...产品做到这个程度,也是蛮拼的.不过就购物角度来说,支付宝是蛮受人欢迎的,一是因为竞争对手财付通实在不争气,将自己产品封闭得死死的,而且还复杂:另一个原因就是淘宝上购物,人家商家都默认支付宝,以及支付宝的扩张策略-很多线下店铺都认同支付宝支付...从这几个原因来讲,支付宝拥有了最多的支付用户群体.

CSDN日报20170517 ——《怎样和虐死人的老项目谈恋爱》

程序人生 | 怎样和虐死人的老项目谈恋爱 作者:安晓辉 说实话,作为开发者,我们都讨厌接手老项目,可是,开发者的宿命就是:你别无选择,终归要和一个老项目相爱相杀. 点击阅读全文 微服务 | 自己动手扩展分布式调用链 作者:小程故事多 在做微服务框架选择的时候,spring Cloud 无疑是当下最火的,但是因为 Spring Cloud 是近二年的后起新秀,以及在使用方式上面的差别,目前在很多中小企业还是以 dubbo 为主,不过遗憾的是,dubbo 从官方来讲已经不维护了,很多公司都是自己再去