基于原型编程

在面向对象的编程语言中,类和对象的关系是铸模和铸件的关系,对象总是从类创建而来,比如Java中,必须先创建类再基于类实例化对象。

而在基于原型编程的思想中,类并不是必须的,对象都是通过克隆另外一个对象而来,这个被克隆的对象就是原型对象。

基于原型编程的语言通常遵循下面的规则:

  • 所有的数据都是对象
  • 要得到一个对象,不是通过实例化类,而是找到一个对象作为原型并克隆它
  • 无法自己直接创建一个对象,对象都是克隆产生,必须有一个根对象,然后克隆它才可以创建对象
  • 对象会记住它的原型
  • 如果对象无法响应某个请求,它会把这个请求委托给自己的原型

函数和对象

JavaScript是基于原型的语言,但JavaScript并非严格遵循原型编程的第一条规则,其中基本类型undefined、number、string、boolean就不是对象,其模仿Java,分为了基本类型和对象类型,当然和Java一样,基本类型可以使用包装的形式变为对象。

这里先探讨一个问题,有助于后面理解原型和原型链,那就是“所有的数据都是对象,函数也是对象“,虽然函数是对象,但是函数拥有很多普通对象不具备的特性:

  1. 可执行性:函数可以被调用执行
  2. 闭包:函数可以创建闭包
  3. 构造函数:函数可以作为构造函数,去创建对象实例,理论上,除了 Object.create 创建的对象,都是通过构造函数创建的对象。
  4. 原型对象属性:除了箭头函数,每个函数都有一个 prototype 属性,它是一个对象,用于存储通过该构造函数创建的所有实例的共享属性和方法。

JavaScript原型

从上面可以得知,函数都有 prototype 属性,称之为原型,也称为原型对象,原型对象里面可以存放属性和方法,共享给实例对象使用,用于实现继承效果。

这里的函数指的是构造函数,即可以通过new创建对象的函数,在JavaScript中普通函数都可以是构造函数,除了箭头函数。

箭头函数没有 prototype 属性是为了保持它们的设计简洁性,避免与原型链相关的复杂性和潜在问题。箭头函数同时不能使用new实例化对象。如果你需要定义一个构造器函数,应该使用普通函数而不是箭头函数。

举例说明:

const arr = new Array(1, 2, 3)
arr.reverse()
arr.sort()

在上面的代码中 Array 就是一个构造函数,reverse和sort都是 Array.prototype 原型对象上的函数。

这里有个问题,为何要将这些方法放在 prototype 原型属性上,而不是放在构造函数内部呢?

我们来看下面的代码:

function Person() {
this.getName = function () {
console.log('person')
}
}
Person.prototype.getProtoName = function () {
console.log('proto person')
}
const person = new Person()
const person1 = new Person() console.log(person.getName === person1.getName) // false
console.log(person.getProtoName === person1.getProtoName) // true

很明显,虽然在实例化对象上都可以调用这些方法,但两者有本质的不同,将方法放在 prototype 原型对象上,之后实例化的对象使用的都是同一个方法,类似于Java的类方法,而构造函数内的方法,则会重新创建,也就是实例对象方法。

JavaScript原型链

了解完原型之后,再来看原型链就非常简单了。

  1. 对象可以调用其构造函数的 prototype 原型对象的属性和方法
  2. 原型对象也是对象,同样也可以调用其构造函数的prototype原型对象的属性和方法
  3. 一层一层的形成一条链路就是原型链

如图所示:

解读:

  1. 在浏览器中,通常使用对象的 __proto__ 即可找到对象的原型对象,每个对象都有 __proto__ 属性(还是要去除Object.create创建的),用于指向它的原型对象。
  2. 前面基于原型编程有一个规则是,必须有一个根对象,JavaScript中根对象是:Object.prototype,其是一个空对象。

原型链经典图片

最后结合上面的内容,来看看下面的经典原型链图:

从上图可以看出左边是实例对象,中间是构造函数,右边是原型对象,图中还包含了一些其它内容:

函数是一个函数,同时也是一个对象:

构造函数作为一个函数时,拥有原型对象属性 prototype

同时函数也是一个对象,函数都是由构造函数 Function 构造出来的,包括 Function 函数本身,可以看上图中 function Foo()function Object()function Function()__proto__ 都是指向 Funtion.prototype,这是函数比较特殊的一点。

这个地方有点绕,再理一下,函数的 prototype 属性是这个函数自己的属性,而函数的 __proto__ 指向的是其构造函数的原型对象,可以理解为父级的属性,两者是不同的。

总结

  1. JavaScript是基于原型编程,创建对象是通过克隆对象的形式,不是通过类创建。
  2. 函数都拥有 prototype 原型属性,实例化对象的 __proto__ 属性指向这个原型属性,对象可以直接调用原型对象的方法和属性,不用写 __proto__ 再调用,两者效果一致。
  3. 对象的 __proto__ 指向构造函数的 prototype,构造函数的 prototype 同样是对象,其 __proto__ 指向上一层原型对象,直到 Object.prototype,形成原型链。

彻底搞懂JavaScript原型和原型链的更多相关文章

  1. 一张图搞懂 Javascript 中的原型链、prototype、__proto__的关系 转载加自己的总结

    1. JavaScript内置对象 所谓的内置对象 指的是:JavaScript本身就自己有的对象 可以直接拿来就用.例如Array String 等等.JavaScript一共有12内置对象    ...

  2. 来一轮带注释的demo,彻底搞懂javascript中的replace函数

    javascript这门语言一直就像一位带着面纱的美女,总是看不清,摸不透,一直专注服务器端,也从来没有特别重视过,直到最近几年,javascript越来越重要,越来越通用.最近和前端走的比较近,借此 ...

  3. 一张图彻底搞懂JavaScript的==运算

    一张图彻底搞懂JavaScript的==运算 来源 https://zhuanlan.zhihu.com/p/21650547 PS:最后,把图改了一下,仅供娱乐 : ) 大家知道,==是JavaSc ...

  4. 一张图带你搞懂Javascript原型链关系

    在某天,我听了一个老师的公开课,一张图搞懂了原型链. 老师花两天时间理解.整理的,他讲了两个小时我们当时就听懂了. 今天我把他整理出来,分享给大家.也让我自己巩固加深一下. 就是这张图: 为了更好的图 ...

  5. 彻底搞懂JavaScript中的继承

    你应该知道,JavaScript是一门基于原型链的语言,而我们今天的主题 -- "继承"就和"原型链"这一概念息息相关.甚至可以说,所谓的"原型链&q ...

  6. 彻底搞懂Javascript的“==”

    本文转载自:@manxisuo的<通过一张简单的图,让你彻底地.永久地搞懂JS的==运算>. 大家知道,==是JavaScript中比较复杂的一个运算符.它的运算规则奇怪,容让人犯错,从而 ...

  7. 三张图搞懂JavaScript的原型对象与原型链

    对于新人来说,JavaScript的原型是一个很让人头疼的事情,一来prototype容易与__proto__混淆,二来它们之间的各种指向实在有些复杂,其实市面上已经有非常多的文章在尝试说清楚,有一张 ...

  8. 三张图搞懂JavaScript的原型对象与原型链 / js继承,各种继承的优缺点(原型链继承,组合继承,寄生组合继承)

    摘自:https://www.cnblogs.com/shuiyi/p/5305435.html 对于新人来说,JavaScript的原型是一个很让人头疼的事情,一来prototype容易与__pro ...

  9. 一张图搞懂javascript原型链

    js高级里面原型链对于新手来说并不友好,总的来说就是 任何函数都有自己的原型对象(prototype),任何实例对象都__proto__指向构造函数的原型 先来个最简单的原型三角关系 var fn = ...

  10. 一次搞懂JavaScript对象

    索引 目录 索引 1. 对象与类 2.对象使用 2.1 语法 2.2 属性 3.对象特性 4.对象的创建 4.1 字面量 4.2 工厂函数 4.3 构造函数 4.4 class类 4.5 对象与单例模 ...

随机推荐

  1. 深度解析 Spring Security:身份验证、授权、OAuth2 和 JWT 身份验证的完整指南

    Spring 安全框架 Spring Security 是一个用于保护基于 Java 的应用程序的框架.它是一个功能强大且高度可定制的身份验证和访问控制框架,可以轻松地集成到各种应用程序中,包括 We ...

  2. Python 集合(Sets)3

    Python - 合并集合 在 Python 中,有几种方法可以合并两个或多个集合.您可以使用union()方法,该方法返回一个包含两个集合中所有项的新集合,或使用update()方法,将一个集合中的 ...

  3. Centos环境部署SpringBoot项目

    centos JDK Jenkins maven tomcat git myslq nginx 7.9 11.0.19 2.418 3.8.1 9.0.78 2.34.4 5.7.26 1.24.0 ...

  4. openGauss Cluster Manager RTO Test

    一.环境介绍 软件环境 类别 版本 下载链接 备注 OS openEuler 20.03 (LTS) https://repo.openeuler.org/openEuler-20.03-LTS/IS ...

  5. Android 开发入门(1)

    0x01 准备 (1)概述 安卓(Android)基于 Linux 内核开发的操作系统,由 Google 等领导开发. (2)版本 Android 版本号 API 发布时间 Android 14 - ...

  6. 重新整理 .net core 实践篇——— 测试控制器[四十九]

    前言 其实就是官方的例子,只是在此收录整理一下. 正文 测试控制器测试的是什么呢? 测试的是避开筛选器.路由.模型绑定,就是只测试控制器的逻辑,但是不测试器依赖项. 代码部分: https://git ...

  7. 实训篇-Css-跳动的红心

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  8. 《c#高级编程》第2章C#2.0中的更改(四)——空值类型

    一.概念 在C#中,空值类型(Nullable Types)是一种特殊的数据类型,它可以包含正常值或者一个null值.当我们需要在程序中处理可能不存在的值时,空值类型就会派上用场. 空值类型的语法形式 ...

  9. iLogtail社区版使用入门 - 采集MySQL Binlog

    简介: MySQL Binlog记录了MySQL的变更日志,业界也有一些方案来同步Binlog的数据,如Canal.MaxWell.DTS等.不同的工具可以实现不同的目标,iLogtail也提供了便捷 ...

  10. 如何开发 Node.js Native Add-on?

    简介: 来一起为 Node.js 的 add-on 生态做贡献吧~ 作者 | 吴成忠(昭朗) 这篇文章是由 Chengzhong Wu (@legendecas),Gabriel Schulhof ( ...