构造函数、原型链、继承简单理解(创建对象的三个方法是什么)

1. 创建对象的三个方法

创建一个对象一般有三种方法:

  1. 字面量创建, var obj = {};
  2. 通过Object创建, var obj = new Object();
  3. 通过构造函数创建:
function Person(name, age) {
    this.name = name;
    this.age = age;
}
const jack = new Person('Jack', 18);

上面代码中的 new 在执行时会做四件事情:

  1. 在内存中创建一个新的空对象。
  2. 让 this 指向这个对象。
  3. 执行构造函数的代码。
  4. 返回这个对象(所以构造函数不需要 return)。

2. 静态成员与实例成员

function Person(name, age) {
  this.name = name; // 实例成员
  this.age = age;
  this.sing = function () {
    console.log('我在唱歌');
  };
}

Person.height = 180; // 静态成员

const jack = new Person('Jack', 22);
const lily = new Person('Lily', 22);

console.log(Person.height); // 180
console.log(jack.height); // undefined
console.log(jack.sing === lily.sing); // false  这里可以看出,多个实例调用相同的方法,会造成内存浪费

上面代码可以看出:
静态成员只能使用构造函数调用,不能通过实例调用。
多个实例调用同样的实例成员函数,会各自存储一份,造成内存浪费。
那么,如何解决呢?

3. 原型和原型链

构造函数存在内存浪费的情况,可以通过原型对象上定义属性、方法解决。构造函数原型上的方法,能够被构造函数实例调用。
每一个构造函数都有一个 prototype 属性,指向一个对象。这个对象的所有属性和方法都会被构造函数所拥有。
我们可以把那些不变的方法直接定义到 prototype 上,这样所有的实例都可以共享这些方法。
代码如下:

Person.prototype.say = function () {
  console.log('我在说话');
};
jack.say();
lily.say();
console.log(jack.say === lily.say);

每个对象都会有一个__proto__属性,指向其构造函数的原型对象。也就是说,对象的__proto__属性和构造函数的 prototype 属性是等价的。
实例对象和原型对象都有一个constructor属性,指向构造函数本身。
我们调用一个函数的属性时,编译器会先看对象本身是否有这个属性,如果没有就到对象的__proto__属性上去找,如果还找不到,继续找__proto__的__proto__属性,直到 null 为止。
把构造函数、实例对象、原型对象的关系用图画出来,如下:

构造函数、原型链、继承简单理解

这就是原型链。

其实构造函数是Function的实例,Function的原型是一个对象,是Object的实例。我们继续拓展,把图画下来。如下:

构造函数、原型链、继承简单理解

4. 使用原型扩展内置对象方法

我们可以使用原型来扩展内置对象的方法。比如,我们可以这样扩展数组的内置方法:

Array.prototype.sum = function () {
  let sum = 0;
  for (let i = 0; i < this.length; i++) {
    sum += this[i]; // prototype 里的 this 指向调用原型方法的那个对象。
  }
  return sum;
};

console.log([2, 3, 4].sum()); // 9

5. 继承

  • 构造函数继承:只能继承构造函数里的属性和方法
    其实就是在子构造函数里面调用父构造函数(需要修改this指向),这样就把父构造函数里的属性和方法放到了子构造函数里了。
function Person(name, age) {
  this.name = name;
  this.age = age;
  this.say = function () {
    console.log('你好');
  };
}
Person.prototype.sing = function () {
  console.log('唱歌');
};

function Student(name, age, score) {
  Person.call(this, name, age); // 由于Person里的this指向为Person的实例,这里修改this指向为Student的实例
  this.score = score;
}

const jack = new Student('Jack', 18, 100);
console.log(jack.name); // Jack
jack.say(); // 你好
jack.sing(); // 无结果,Student没有继承Person原型上的方法
  • 原型链继承
    通过上面的例子我们可以看出,构造函数继承并不能继承原型链上的方法。那我们怎么才能继承父构造函数原型上的方法呢?我们可以把子构造函数原型指向父构造函数的原型。
Student.prototype = Person.prototype; // - 原始版 缺陷:Student的原型改变导致Person原型改变,因此不可取

原型一样,自然能够继承原型上的方法。
但是这样一来就会出现问题,原型是个对象,是引用类型数据,我们再修改Student.prototype会导致Person.prototype的变化。这是不合理的。比如:

Student.prototype.dance = function () {
  console.log('跳舞');
};

const jack = new Person('Jack', 18);
jack.dance(); // 跳舞    Person的原型上是没有dance方法的。这里是因为我们扩展了Student的原型导致Person原型变化

常用的解决办法是,我们将子构造函数的原型指向福构造函数的一个实例。

Student.prototype = new Person();
构造函数、原型链、继承简单理解

前面说过,调用一个函数的属性时,编译器会先看对象本身是否有这个属性,如果没有就到对象的__proto__属性上去找,如果还找不到,继续找__proto__的__proto__属性,直到 null 为止。这样一来,Student实例就能够调用Person实例的方法,从而调用Person原型的方法。

  • 组合继承
    把构造函数继承和组合继承结合起来就是组合继承了。整体代码如下:
function Person(name, age) {
  this.name = name;
  this.age = age;
  this.say = function () {
    console.log('你好');
  };
}
Person.prototype.sing = function () {
  console.log('唱歌');
};

function Student(name, age, score) {
  Person.call(this, name, age);
  this.score = score;
}
Student.prototype = new Person();
Student.prototype.constructor = Student; // 修改constructor指向

组合继承调用了两次父构造函数,且每创建一个子构造函数,都会生成一个父构造函数实例。还是不够完美。

  • 寄生组合继承
function Person(name, age) {
  this.name = name;
  this.age = age;
  this.say = function () {
    console.log('你好');
  };
}
Person.prototype.sing = function () {
  console.log('唱歌');
};

function Student(name, age, score) {
  Person.call(this, name, age);
  this.score = score;
}
Student.prototype = Object.create(Person.prototype); // Object.create(proto)创建一个对象,这个对象的__proto__属性为proto
Student.prototype.constructor = Student; // 修改constructor指向
构造函数、原型链、继承简单理解

Student实例能够调用Student原型上的方法,而Student原型又可以通过__proto__获取Person原型上的方法。这样就实现了Student继承Person原型上的方法。
也可以自己写一个类似Object.create(proto)的方法。

function objectCreate(proto) {
  function Temp() {}
  Temp.prototype = proto;
  return new Temp();
}
Student.prototype = objectCreate(Person.prototype);
Student.prototype.constructor = Student; // 修改constructor指向

6. 类的本质

当然,要实现继承最好写最好用的还是ES6里面的类。探究一下会发现:

  • 类的本质还是函数。
  • 类也有prototype属性
  • 类创建的实例,也有__proto__属性,且指向类的 prototype 属性
  • 类创建的实例的constructor指向类本身
  • 类的prototype的constructor也是指向类本身
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  say() {
    console.log('说话');
  }
}

const jack = new Person('Jack', 18);

console.log(typeof Person); // function
console.log(Person.prototype); // {constructor: ƒ, say: ƒ}
console.log(jack.__proto__ === Person.prototype); // true
console.log(jack.constructor); // class Person{ ... }
console.log(Person.prototype.constructor); // class Person{ ... }

总结:ES6 的类就是ES5继承的语法糖。

本文链接:https://www.dzdvip.com/35881.html 版权声明:本文内容均来源于互联网。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 395045033@qq.com,一经查实,本站将立刻删除。
(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022年7月25日 23:14
下一篇 2022年7月25日 23:35

相关推荐

  • 广点通账户怎么搭建(腾讯广点通账户搭建流程)

    广点通是腾讯社交广告的核心数据和技术系统,支持多种类型的广告投放,服务腾讯内外部流量。通过对QQ、微信用户所产生的数据进行深入分析,为广告主提供众多的标签类目,以在广告投放中精确锁定目标人群。同时,不断精进的跨屏定向、人群拓展和智能出价等技术,也将助力广告主持续提升投放效率与效果。 广点通跟信息流广告有什么区别? 信息流又叫feed流,是主动型推送广告。信息流的广告有很多种, 比如:今日头条、一点咨询、智汇推(腾讯信息客户端、天天快报)、新浪扶翼(新浪新闻客户端)、搜狐汇算(搜狐新闻客户端)、网易新闻客户端, 这些都是以资讯为主的信息流广告。微博粉丝通/粉丝头条、广点通、陌陌,这些是以社交媒体为主的信息流广告。所以信息流与广点通是一种包含与被包含的关系。 广点通账户怎么搭建?我们做任何推广前, 都需要对自己的产品做人物画像和需求分析, 越了解受众越能做好营销。我之前写过一片文章《广点通推广 你的产品适合做广点通吗?》, 是关于人物画像和需求分析的,大家不清楚的可以回顾下。人物画像9要素:性别、职业、学历、兴趣、收入、年龄、地区、时段、设备,举个例子:我们以ui培训为例,假如是大学生刚毕业,可能最迫切的就是工作、就业,你推送的广告就可以展示“保障就业“,假如是上班族想提升,看中的就是升职加薪,你推送的广告就可以展示“学员高薪就业“,对应的文案素材、对应的页面、对应的客服解与答、成单。是不是我们基本的账户结构就出来了?回顾下我们做竞价账户的思路,是不是雷同?所以我们做账户的思路是一样的,不一样的是呈现的数据不一样,我们需要去测试各个广告位的效果,找到好的合适的广告位再不断去优化数据。 新建计划第一步:设置计划基础信息 点击新建计划后,会进入下面这个页面。 1. 计划类型 在一开始,系统就会让你选择计划类型,主要分为:“展示广告计划”和“搜索广告计划”。 一般搜索广告计划主要是依靠关键词,所以建议选择“展示广告计划”。 2. 推广目标 推广目标这项根据你自身需求进行选择即可。你要推广的是网页?电商?应用? 根据需求相应选择即可。 通常情况下,推广目标用得比较多的为:网页着陆页推广。 3. 预算 设置预算时,一定要注意:要比实际预算高出20%! 比如,你的实际预算为1000元,那设置预算时就要为1200元。 当我们的预算快花完时,系统会自动缩减流量,这样设置正好可以保证我们…

    2022年1月7日
    9
  • 田园女的特征(田园女是指什么意思)

    熟知田园森系风格的女生都知道森女系和。仙女系,但是今年突然出现了田园女,你是否了解呢?其实,田园女的特征非常明显。只要熟悉了之后,你绝对可以一眼认出来!

    2021年12月29日
    166
  • ASO优化的核心问题有哪些?

    一般来说APP推广从业人员都经过上面的几种推广方法,其中积分墙虽然下载量大,但存活率较低。刷榜可以带来较大的真实利益,但风险较大,而地推主要是单个用户成本不断上升,至于社会化营销需要靠创意,更重要的是需要靠运气。综合以上推广方法,搜索优化堪称最省钱,最有效的方法了。

    2021年5月8日
    34
  • 世界十大名画(世界名画排行榜前十名)

    在艺术界,每年都有价值数十亿美元的作品在国际拍卖行中流转,而每家博物馆都收藏着数十万件艺术品。但真正能做到家喻户晓的艺术品却少之又少。 由于“著名”是一个主观的术语,于是我们转向网络,看看在过去五年中哪些画作在全球搜索结果中名列前茅。 我们比较了几十幅热门名作——从《蒙娜丽莎》、《神奈川外的巨浪》、《萨拉瓦托·蒙迪》等经典作品,到《夜鹰》等更现代的作品,甚至是《打扑克的狗》系列。 根据这些结果,以下是世界上搜索量最大的10幅画作。 1.蒙娜丽莎 艺术家:达芬奇创作时期:1503年~1519年在哪里可以看到它:卢浮宫博物馆(巴黎) 世界上最著名的画作是那个带着神秘微笑的女人,这大家都知道。蒙娜丽莎有什么特别之处? 画中的坐像被认为是佛罗伦萨商人的妻子,但专家们并不确定。它确实代表了艺术的创新,根据卢浮宫的说法,这幅画是已知最早的意大利肖像画。 你知道吗?历史学家说,20世纪之前,“蒙娜丽莎”在艺术圈外鲜为人知。但在1911年,有一个卢浮宫雇员偷了这幅画,并私藏了两年。那次盗窃助攻了这幅画在大众文化中的地位,从此让数百万人接触到了文艺复兴时期的艺术。 2.最后的晚餐 艺术家:达芬奇创作时期:1495年~1498年在哪里可以看到它:圣玛丽亚修道院(意大利米兰) “文艺复兴天才”达芬奇是唯一一位两次出现在这份名单上的艺术家。 这幅《最后的晚餐》是在宗教艺术时代绘制的,它描绘了耶稣在被钉死在十字架上之前最后一次与门徒们一起擘饼的情景。 这幅画实际上是一幅巨大的壁画——高4.6米(15英尺),宽8.8米(28.9英尺),让人看了十分震撼。 你知道吗?这幅壁画经历了两次战争时期的威胁——拿破仑的军队将画有壁画的食堂墙壁当作靶子练习。二战期间的轰炸摧毁了米兰圣玛丽亚修道院的屋顶,使壁画暴露在残垣断壁中长达数年。 3.星夜 艺术家:文森特·梵高创作时期:1889年在哪里可以看到它:现代艺术博物馆(美国纽约) 这幅比较抽象的画作是梵高创新大胆地使用粗笔画的标志性例子。画中醒目的蓝色和黄色,以及梦幻般的漩涡氛围,几十年来一直吸引着艺术爱好者。 纸张将梵高在伦敦的时光展现在世人面前。 你知道吗?梵高画《星夜》的时候,他住在法国圣雷米的一家精神病院里,正在接受精神疾病的治疗。他的灵感来自于他房间窗外的景色。 4.呐喊 艺术家:爱德华·蒙克创作时期:1893年在哪里可以看到它:国家博物馆或蒙奇…

    2022年7月16日
    112
  • 深圳一男婴出生时四手四脚(新生儿四只脚四个手现在怎样)

    近日,在广东深圳。严女士成功了生下了一个男婴,刚出生的小天使重2.9公斤,体重与正常出生的孩子并无多大区别,但是小天使的胸部与自己的寄生胎相连,寄生胎没有头、没有心脏,却有一双手脚及外生殖器,而且外生殖器有泌尿功能,导致小男孩刚出生就拥有了4只手4只脚,这要是在古代,这种异常的身体状态,要么被认为是改变世界的英雄,要么就被认为是带来厄运的怪胎。 像严女士这种小孩的异常现象,全球罕见,据医生介绍,这种现象发生的概率仅有50万分之一。在严女士怀孕期间,多次的产检就已经发现了,医学评估应该是产后可以手术解决。在小孩出生后,医院经过多学科讨论,深圳市儿童医院专家团队为患儿进行手术。术后患儿恢复良好,目前已顺利出院。 近日,深圳一男婴四手四脚,现在已经手术状况良好 2022-09-17 17:01·柠檬茶向右行 来源D视频 目前患儿术后恢复良好,已经顺利出院。 医生说,寄生胎生长在患儿的胸部,是有骨心连接的,还有一些血管,肠管相连接,肝脏可疑是有进入寄生胎的,必须手术。 寄生胎有泌尿系统,没有头和心脏。 该寄生胎的发生概率是五十五分之一,全球报道的只有两百例。 经过三个小时的手术,患儿手术良好。 寄生胎去掉后,男婴跟正常孩子无异。在这里,我们要对严女士夫妇表示敬佩之情,为人父母其实挺伟大的,坚持生下来了,给他一个生命,挺好的。最后我们也祝福孩子健康茁壮成长。

    2022年9月17日
    68
  • 支付宝借呗的利息该怎么算?

    借呗,是支付宝旗下的一款消费信贷产品,支持随借随还、按天计息。根据用户不同的资质,借呗会给出不同的额度和贷款利率。 那么,支付宝借呗的利息该怎么算呢?我们一起来了解下。 如果我们是短期使用的话,那么借呗只会按天计息。假设我们的贷款金额为1万元,日利率为万分之3,如果借款3天的话,那么所产生的利息就是9元了。 如果我们选择按月归还月供的话,那么借呗将按剩余本金,以月利率计算利息。具体的计算公式如下: 月供 = (贷款本金÷还款月数)+(贷款本金-已归还本金累计额) × 月利率 其中,月利率=日利率*30 看到这里,大家应该了解,支付宝借呗的利息该怎么算了吧?

    2021年6月2日
    54