测试目录:

在html文件中引入js文件


以下所有文件都在base.js中玩~

原型(Prototype)-构造器(Constructor)

一说到原型,一定和对象直接相关。
以前我们这样声明一个对象

当对象是一个用户时

当我们又需要一个用户时

可以看到两个对象的结构无区别。

那么问题来了,如果我们需要创建100个用户时,也要这么做吗?当然不要
两个原因:

  1. 用户的信息是动态的,我们不能在代码中把它们写死。

所以想到一种办法简化我们的工作:创建一个function专门为我们生成user

可以看出,打印的结果与前面的没有任何区别~
但是我们却少写了很多代码。

这个function的本质就是生成对象。
在这个function中,先新建一个空对象,然后将它填满,最后返回这个对象。
这样的函数我们称它为工厂函数(Factory Function),专门生产对象。

在原生JS中,有一种更加简便的生产对象的方式

这个function中的this就代表它即将生成的对象。
注意:一旦使用这种方式来定义一个工厂函数时,就要用new关键词来叫它。

打印结果依然与前面的没区别~

这种写法相当于原生JS帮我们省去了声明对象和返回对象的步骤。

像这样用new关键词叫的function通常我们称它为构造器/构造函数(Constructor),构造函数的第一个字母一般大写。
不大写也没关系,大写不过是为了让其他人知道这是一个构造函数。

Constructor的概念应该理解的差不多了,下面我们再造几个构造器作为练习。

实例一:

实例二:

原型-prototype和__proto__

生成一个对象的过程叫实例化(Instantistion)。不管在哪种语言里面都一样。

我们再给这个构造器加一些功能,比如greet和eat。

实例化一个对象whh

运行whh.greet();

当我们还需要一个用户

可见两位用户都可以greet。但是注意,它们两都实例了this.greet这个能力。也就是说,whh这个对象中有greet这个能力(方法),lsd这个对象中也有同样的方法。这两个方法是完全独立的,我们来验证这一点,在控制台中测试:

如图,说明它每实例化一次都会给自己创造一份与其它对象同样的方法。在本例中也就是greet和eat方法。这里可以理解为拷贝代码。将构造器中的方法拷贝到实例化的对象当中。

这个时候我们想 有必要一次次拷贝吗?能不能想一种办法把它们放到一个地方?当实例化的对象要用的时候用一个东西就可以了。这就引入了原型(prototype)。

创建一个function。

在控制台输入a.prototype

发现它是一个对象。
原生JS就有这样一个机制,在我们创建一个function的时候,它就首先给我们prototype这样一个对象,放到function下。

为什么要这么做呢?prototype有什么用呢?
prototype就是用于给它即将生成的对象继承下去的属性。也就是说我们一眼看不出来,但它实际拥有的能力。我们来举个栗子

它没有在自有的属性中显示name属性,而在继承的属性中显示。但当我们使用的时候是相当于自己的属性在用的。而且不管我们new了几个对象,它们中的__proto__都是一样的(其实是指向同一对象的)。下图可验证。

既然如此,我们在以下例子中就可以把greet方法放到prototype中了。

修改为

在控制台中

也就是说,现在的function只会存在一个地方,它不再需要每次都拷到实例的对象中去。

用原型的方式来指定一个方法还有一个好处,可以动态更新。

当我们想知道一个对象来自于那个工厂函数,可以在控制台中输入 对象.constructor
如图

那么就意味着,如果我们想复制一个对象的结构,可以这样做:

这就相当于

以上就是有关prototype和__proto__的逻辑。

原生对象的原型

学到这里也许我们会有个疑问,当我们就这样创建一个对象时,它的原型是什么呢?或者说它有没有父亲呢?是谁创建的它呢?

我们来打印一下就知道了~

它的constructor是一个叫Object的函数,也就是说,它是有父亲的。
也就是说,以下两种写法是等价的。

我们来测试一下以上说法是否正确。

声明两个对象

然后再控制台中测试

也就是说上面两种创建对象的方法是等价的~

还有一种情况,当我们想创建一个不继承任何东西的对象时该怎么做呢?
我们可以这样:

这样我们就创建了一个最干净的对象~

在参数中设置原型,比如

看到这里,我们应该不再恐惧对象这个东西了,它的继承关系我们已经搞的很清楚了。
下面我们做个练习:
a是一个数组,我们打印它

可以清楚的看到,它的constructor是一个叫Array的function

也就相当于

两种写法是等价的。

我们继续往下看

Array()也有继承的属性,它的constructor是Object()。

综上,如果Object()是爷爷,那么Array()就是爸爸,而我们声明的数组对象a就是儿子。

我们来试一下从爸爸那继承来的方法:(拿push方法来举例)

证明它从爸爸那继承下来的方法确实能用。

我们再来试一下从爷爷那继承来的方法:(拿toString方法来举例)

总结:在JS中只要我们不明确的用Object.create()来创建对象,其余都是继承Object.prototype的。

如何实现多级继承链

有这样一条继承链

这样的一条继承链在代码中如何体现? 我们可以应用到之前说过的构造器。

创建三个构造器,并且使用Person构造器创建一个叫lsd的人。

我们从最顶级开始看
实例两个对象并打印

前面我们提到过,实例出来的对象会各自拷一份能力。所以打印结果是false。

事实上这些纯逻辑(纯能力)的东西是没有必要拷贝的,那我们为什么不将它们放到原型中去呢?

刷新网页

此时的eat都是指向prototype的。相当于借了一个能力过来,而非拷贝一个能力过来。

接下来我们看Mammal这个构造器

那么此时实例化的对象m有没有eat和sleep这种能力呢?

可见是没有的。因为此时三个构造器是独立的,我们还没有指定它们之间的继承关系。那么如果指定这条继承链呢?

拿Mammal和Animal说,Mammal不仅要继承Animal的prototype,而且还要有自己的东西。

我们可以用到前面提到的create方法。

这种写法就相当于 {__proto__:Animal.prototype}

现在我们再来测试一下实例化的对象m

这样我们就可以说它继承成功了。

不过现在还有一个问题,我们在控制台调用一下m对象的constructor。

打印结果是Animal而非Mammal。而事实上Mammal才是m对象的constructor。这是为啥呢?

仔细看对象m的打印结果,发现并没有constructor。

这是因为我们在前面把它的prototype重写了,覆盖掉了。

所以才会继承了上一级的constructor。

补救方法是,我们再明确指定一下就可以啦。

大功告成~

接下来我们看Person

到这里我们就完成了原型的三级继承~~~~撒花!

现在还有一个问题,不同的动物毛色可能不一样,体重可能也不一样,等等等。也就是说,它们会共有一些属性,不过值不一样。
我们可以这样做:

此时实例化的对象中就有了我们用this指定的两个显性属性"color"和"weight"

但是当我们实例一个Mammal的对象并传入参数,然后打印。

我们发现,打印出来的是一个空对象。
我们希望的是,只要是动物(不管是哺乳动物还是人),都存在毛色,体重等等属性。
这个功能如何实现呢?
我们可以这样做:

我们希望Mammal中的this与Animal中的this绑定到一块。然后正常传入参数。

在Person中同理

我们来打印一个Mammal中实例化的对象m做测试,看看它是否继承Animal中的两个显性属性。

我们再来试一下Person

我们再new一个Person。

可见,显性属性是拷贝的。它们之间互不影响。这就是显性属性的继承方式。

JS中那些拧巴的概念-原型相关推荐

  1. js中函数对象的方法,原型方法apply、call、bind、toString、toLocaleString、valueOf

    全栈工程师开发手册 (作者:栾鹏) js系列教程4-函数.函数参数教程全解 js中函数也是一种对象,因此有自己的原型对象,可以作为其他对象的属性,也可以作为其他函数的参数. 函数方法 [apply() ...

  2. JS中__proto__和prototype都是什么?原型链继承解读

    首先要知道,prototype是函数才有的属性,__proto__是每个对象都有的属性 随后,先谈一下 1.什么是prototype? prototype对象是JS实现面向对象的一个重要机制. 在很早 ...

  3. new 实例化对象是啥意思_二. 初步认识JS中的类和对象

    1 构造函数的定义 在JS中, 没有类(class)的概念, 主要是通过构造函数来模拟的. 语法 function 构造函数名 () {// 函数体} 使用function关键字表示定义一个构造函数 ...

  4. java js中 function函数报错_浅析JS中对函数function的理解(基础篇)

    正文:我们知道,在js中,函数实际上是一个对象,每个函数都是Function类型的实例,并且都与其他引用类型一样具有属性和方法.因此,函数名实际上是指向函数对象的指针,不与某个函数绑定.在常见的两种定 ...

  5. JS中的可枚举属性与不可枚举属性的学习以及扩展

    最近在学习对象遍历的方法时总是能看到的两个词,一个是"原型",一个是"枚举属性".一开始感觉自己大概明白"枚举属性"的意思,但是叫我解释却又 ...

  6. Reflect.ownKeys()与Object.keys()区别 以及 JS中的可枚举属性与不可枚举属性

    代码test1: var obj = {} Object.defineProperty(obj, 'method1', {value: function () {alert("Non enu ...

  7. JS中的事件基础知识

    本文首发于个人博客:www.wyb.plus JS作为一门事件驱动型的语言,了解与事件有关的知识是十分必要的. JS中与事件有关的概念非常多,本文尽量整理完善. 作者:王雨波 qq:760478684 ...

  8. JS中关于构造函数、原型链、prototype、constructor、instanceof、__proto__属性

    在Javascript不存在类(Class)的概念,javascript中不是基于类的,而是通过构造函数(constructor)和原型链(prototype chains)实现的.但是在ES6中引入 ...

  9. js中的对象、原型链机制、构造函数

    一.在js中创建对象的方式 //一.字面量或直接量创建对象var obj1 = {name:"zs",age:12}; //二.通过new来创建对象var obj2 = new O ...

最新文章

  1. 被“嫌弃”的AI药物设计
  2. 深入探讨Java中的异常与错误处理
  3. 安装linux 系统报错:No DEFAULT or UI configuration directive found 解决方法
  4. 天池大赛, Storm
  5. CF1415E New Game Plus(贪心)
  6. golang的指针和切片
  7. 2018.12.2 频谱分析
  8. 终于购入Mac mini,发现HDMI接口与显示器不兼容,网购了一个VGA转换插头
  9. LU分解的矩阵逆运算
  10. 2020年考研数学一解析.pdf
  11. LeetCode-70.爬楼梯
  12. 服务器搭建免流系统,国内服务器搭建免流
  13. 旋转向量解法(罗德里格公式推导及理解)
  14. Chrome游览器下载
  15. 利用pypdf2 安装包 基于 python 制作的PDF 文档合并脚本
  16. centos-linux(64位)安装与配置
  17. 解决VS2010使用mscomm控件无法接收数据的问题【转】
  18. 2020年中国羊肉行业供需现状、进出口情况及产业链分析,新西兰为羊肉主要进口国「图」
  19. Razor 一知半解
  20. Linux之poll/select/epoll代码示例

热门文章

  1. 回归并行!芯片到芯片的最新超高速通信方式:超短距(USR)接口
  2. 几百块的投影仪靠谱吗?难道没有便宜又好用的吗?
  3. Flutter知识点总结
  4. 程序控制打印word文档超出页边距的问题
  5. 关于抖音、快手采集/爬虫的一些思路,采视频、采评论、采用户喜欢等通用办法.
  6. 中级药师职称要考计算机,太好了!有执业药师【评上】中级职称啦!
  7. 神策数据宣布 4400 万美元融资 ,首家迈入 C 轮的用户行为分析服务商
  8. Linux下Desktop文件入门,解析Deepin Linux系统中的Desktop文件,附实例讲解
  9. 关于@Transactional(readonly = false)注解不起作用的可能性
  10. 我来图书馆小程序一键签到和一键抢位置工具