高级JS 详解面向对象、构造函数、原型、实例与原型链、原型链继承

写在前面:

JS 面向对象 在前端学习来说 一直是个重点和难点,不过在前端学习中 是很重要的思想 在es6 还有框架中 都有广泛的使用,所以是前端开发者必须要学会的知识
我在工作中用面向对象的时候比较少 所以基本学完之后就废弃了 基本忘的差不多了 最近开始学习react了 发现react上来就开始用es6的class定义类(class是一个语法糖 能简化构造函数原型的写法更加清晰)里面就涉及到了面向对象的知识,发现自己已经忘的差不多了 构造函数 原型 实例的关系也搞不明白了 所以我就抽时间又查阅了一些资料 自己用我的理解总结一下 希望对大家能有所帮助

面向对象的概念:

在js的j世界里一般会把构造函数看成一个面向对象,其实面向对象从表面意思理解是面向全局的一个对象,构造函数就是那个面向全局的对象(函数本身就是一个对象)

说到构造函数 不得不先说下创建对象的方式:

ps:万物皆对象

1. 最简单的方式就是通过new Object()或者直接用简写形式 对象字面量 直接创建就好:
var person = new Object()
person.name = 'Jack'
person.age = 18

person.sayName = function () {
  console.log(this.name)
}

或者直接 ↓ (推荐)

var person = {
  name: 'Jack',
  age: 18,
  sayName: function () {
    console.log(this.name)
  }
}

对于上面的写法固然没有问题,但是假如我们要生成两个 person 实例对象呢?

var person1 = {
  name: 'Jack',
  age: 18,
  sayName: function () {
    console.log(this.name)
  }
}

var person2 = {
  name: 'Mike',
  age: 16,
  sayName: function () {
    console.log(this.name)
  }
}

通过上面的代码我们不难看出,这样写的代码太过冗余,重复性太高。

2. 简单方式的改进——工厂模式:

我们可以写一个函数,解决代码重复问题:

function createPerson (name, age) {
  return {
    name: name,
    age: age,
    sayName: function () {
      console.log(this.name)
    }
  }
}

然后生成实例对象:

var p1 = createPerson('Jack', 18)
var p2 = createPerson('Mike', 18)

这样封装确实爽多了,通过工厂模式我们解决了创建多个相似对象代码冗余的问题。

3. 更优雅的工厂函数:构造函数

一种更优雅的工厂函数就是下面这样,构造函数:

function Person (name, age) {
  this.name = name
  this.age = age
  this.sayName = function () {
    console.log(this.name)
  }
}

var p1 = new Person('Jack', 18)
p1.sayName() // => Jack

var p2 = new Person('Mike', 23)
p2.sayName() // => Mike

构造函数、原型、实例之间的关系

在这里插入图片描述

下面的话浓缩的都是精华

  • 这里的 function Person(){} 就是构造函数
  • 构造函数 new 出来的 person就是它的实例(对象)
  • 构造函数都有一个 prototype 属性,指向另一个对象(就是它的原型(对象)),这个对象的所有属性和方法,都会被构造函数的实例继承——就是说构造函数的原型和构造函数的实例 属性和方法可以共享
  • 这也就意味着,我们可以把所有对象实例需要共享的属性和方法直接定义在 prototype 对象上

拿Person举例

function Person(name, age) {
    this.name = name;
    this.age = age;
    this.getName = function() {
        console.log('this is lokka.');
    }
}

var p1 = new Person('tom', 5);
console.log(p1.name); // tom
console.log(p1.age); // 5
p1.getName(); // this is lokka.
构造函数
1. Person 就是构造函数(其实就是一个函数)
实例
1. 构造函数Person通过 var person = new Person(); 生成的person就是构造函数的实例 实例继承原型上的属性和方法

2. 实例上有个属性__proto__ (双下划线) 指向原型
person.__proto__指向的就是构造函数Person的原型,即: person.__proto__ === Person.prototype

原型
1. 每写一个构造函数 就会同时生成个原型(没有构造函数中的属性或者方法,可以理解为空对象)
2. 构造函数都有一个 `prototype` 属性,指向另一个对象(就是它的原型),这个对象的所有属性和方法,都会被`构造函数的实例继承`。——`就是说构造函数的原型和构造函数的实例 属性和方法可以共享`
3. 原型上有constructor属性指向构造函数 原型.constructor === 构造函数
构造函数和原型的关系:
  1. 每个构造函数都有一个 prototype 属性,指向另一个对象(就是它的原型),用白话讲就是每写一个构造函数 就会同时生成个原型(没有构造函数中的属性或者方法,可以理解为空对象)通过 构造函数.prototype可以设置和访问原型中的属性和方法
  2. 同时原型中也有个constructor属性可以指向构造函数
构造函数和实例的关系:

用 new 关键字创建 Person 实例时,内部执行了4个操作:
1. 创建一个新对象
2. 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象)
3. 执行构造函数中的代码
4. 返回新对象

所以就把构造函数中的指针this 指向了新创建的实例中 实例中就可以调用构造函数中的属性和方法了

实例和原型的关系:

实例上有个属性proto (双下划线) 指向原型
person.proto指向的就是构造函数Person的原型,即: person.proto === Person.prototype

实例继承原型上的属性和方法

更简单的原型写法

根据前面例子的写法,如果我们要在原型上添加更多的方法,可以这样写:

function Person() {}

Person.prototype.getName = function() {}
Person.prototype.getAge = function() {}
Person.prototype.sayHello = function() {}
... ...
function Person() {}

Person.prototype = {
    constructor: Person,
    getName: function() {},
    getAge: function() {},
    sayHello: function() {}
}

这种字面量的写法看上去简单很多,但是有一个需要特别注意的地方。Person.prototype = {}实际上是重新创建了一个{}对象并赋值给Person.prototype,这里的{}并不是最初的那个原型对象。因此它里面并不包含constructor属性。为了保证正确性,我们必须在新创建的{}对象中显示的设置constructor的指向。即上面的constructor: Person

完整的原型链:

在这里插入图片描述

1. 首先我们先看左上角的 Function ,Function 比较特殊

  1. 我们要知道所有函数都是 Function 的实例,所有函数的原型都指向 Function.prototype, Function 即是构造函数 同时也是 Function 的实例,就是 Function 实例化了自己本身,所以我们可以得出:
Function.__proto == Function.prototype
  1. 因为 Object 也是函数,所以我们可以得出:
Object.__proto == Function.prototype
  1. 一切对象都最终继承自Object对象,Object对象直接继承自根源对象null

其中 personPerson 构造函数对象的实例。而 Person 的原型对象同时又是构造函数对象 Object 的实例。这样就构成了一条原型链。原型链的访问,其实跟作用域链有很大的相似之处,他们都是一次单向的查找过程。因此实例对象能够通过原型链,访问到处于原型链上对象的所有属性与方法。这也是 person1 最终能够访问到处于 Object 构造函数对象上的方法的原因。

基于原型链的特性,我们可以很轻松的实现继承。

2. 再说一下 Funtion 和 Object 的关系

从图上我们可以知道 Function 和 Object 是互相继承关系,因为
Function 通过原型链继承了 Object.prototype,而 Object 又继承了 Function.prototype 所以说 Function 和 Object 是互相继承关系

3. 我们再看这个等式

Function.constructor == Function

刚开始看到这个等式我也很疑惑,看了 MDN 也没看明白,后来看了好多文档才理解
因为 .constructor 属性是原型上的属性,构造函数上没有就去原型链上查找,所以:

Function.constructor == Function.prototype.constructor

又由于

Function.prototype.constructor == Function

所以

Function.constructor == Function

所以我们可以得出结论 Function 继承自己

原型链继承

有关原型链继承的内容可以参考我的这篇文档——ES5中的继承和ES6中的类继承模式详解

写在后面:

面向对象中的知识点一直都是很难理解的,这篇文章是我很用心写的一篇文章,用我自己能理解的话术来解释构造函数 原型 实例 原型链 继承等 可能涉及到的底层原理没有那么深刻 不过通过仔细理解文章中的重点 就应该可以很好地使用面向对象了 大家都加油 文章中有什么不对的 也请大家帮我指正出来 大家共同进步!ღ( ´・ᴗ・` )比心

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容