老生常谈的 JavaScript 原型链

更新日期: 2020-10-29阅读: 1.4k

用了这么多年的 JavaScript,对于原型链这东西自觉是理解了,但是日常工作中很少使用的“继承”部分最近突然想起来竟觉得有点陌生,所以在这里稍微理一下思路。

本文90%不能让不懂原型链的人看懂原型链,但是可能可以给懂一点原型链的人一点提示


prototype

只有函数有 prototype,对象没有。

// 函数
function A(){}
A.prototype
// 会输出类似下面的东西
{
    constructor: A,
    __proto__: Object
}
// 对象
a = {}
a.prototype // 输出 undefined
// 而如果你给一个对象赋予 prototype,效果不过是多了一个 prototype 属性

proto

__proto__ 可以看成一种“连接”,所到之处的属性(property)都可以访问。刚进坑的时候听说 __proto__ 不是标准的属性,可能会有不同的实现,但这么多年过来了似乎也就只用 __proto__。

a = { c: 1 }
a.hasOwnProperty('c') // 输出 true

a 显然没有 hasOwnProperty 这个属性,于是他会往 __proto__ 找,在 Object.prototype 下找到 hasOwnProperty 并使用。

一般,一个 __proto__ 会被连接到一个 prototype,但你仍可以直接定义 __proto__

a.__proto__ = { b: 'b' }
a.b // 输出 b

所谓的原型链就是由 __proto__ 串成。

例如一只猫
let mona = new Feliscatus()

     __proto__                   __proto__                     __proto__
mona -----------> Cat.prototype -----------> Felis.prototype -----------> Felinae.prototype
|                  |                           |                             |
| 实例自己的属性      | constructor Cat           | constructor Felis           | constructor Felinae
                    | 其他绑定在 prototype 的属性  | 其他绑定在 prototype 的属性   | 其他绑定在 prototype 的属性

上一层找不到的东西就沿着 __proto__ 往下找,直到尽头,找不到就是 undefined 了

__proto__ 也可以直接连接到一个对象(换句话就是,继承一个对象的属性):

const person = {
  isHuman: false,
  printIntroduction: function() {
    console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`)
  },
}
let me1 = {}
me1.__proto__ = person
// 这样 me1 就能使用 printIntroduction
// 不过还有更方便一点的方法,使用 Object.create,这样就不用自己连接
let me2 = Object.create(person)

实例化

在 new 一个对象时,实际上做了什么,如何自己写一个 new(据说面试会考,不过我没遇到过):

function Person(name) {
  this.name = name
}
Person.prototype.say = words => words
// 这是一个人类
let person1 = new Person('Lilas')
// 正常用 new 的结果是
// 1. 返回一个对象,
// 2. 这个对象运行了构造函数,
// 3. 这个对象可以使用 Person 的 prototype 的属性

// 知道了这三步,下面写一个自己的 new
function createInstance(klass, ...arg) {
  // 返回一个对象
  let obj = {}
  // 以对象为 this 运行构造函数 klass
  klass.call(obj, ...arg)
  // 为了让 obj 可以使用 Person.prototype 使用 __proto__ 连接
  obj.__proto__ = klass.prototype
  // 返回这个对象
  return obj
}

继承

上面说过用 Object.create 继承一个对象的属性,这里的继承说的是“类”继承。

JavaScript 在 ES6 之前,其实没有类,所谓的继承就是沿着原型链访问父类(准确来说是 constructor)的变量和方法。

在 proto 部分说明了原型链的结构,下面讲讲实际上怎么构造原型链。

// 上面已经有一个 Person 了,下面继承 Person 写一个 Boxer
function Boxer(name, id) {
  // 在成为格斗者前他得是个人,所以先调用 Person 构造函数,相当于 super()
  Person.call(this, name)
  //   然后为这个格斗者加上自己的格斗者 id
  this.id = id
}
// 添加 Boxer 自己的方法
Boxer.prototype.punch = () => 'punch!'
// 接通到 Person.prototype
Boxer.prototype.__proto__ = Person.prototype

无论接着还要继承多少次,总之,记住 __proto__ 串通了父子的 prototype 就好了。

原文https://ssshooter.com/2021-01-28-prototype-chain/

链接: https://fly63.com/article/detial/10123

挑战常规--不要使用||赋予默认值

js是一种弱类型的编程语言,代表着传入的变量并不清楚作为何种类型使用。对js来说传入的任意参数都应该考虑不同类型的结果,而不是单单考虑一种情况。若传入0、false等,||所要实现默认值的功能完全错误的

YodaOS 中是如何生成 API 的

本文简单介绍了 YodaOS 在 API 设计过程中,如何利用 DSL,解决 YodaOS API 在多种应用形态保持一致性。以此,我们希望抛砖引玉:帮助读者更好地了解 YodaOS API 的生成过程,帮助读者了解到 DSL,也能将这种思路应用在自己的项目中

JavaScript 私有成员

JavaScript 一直没有私有成员并不是没有原因,所以这一提议给 JavaScript 带来了新的挑战。但同时,JavaScript 在 ES2015 发布的时候已经在考虑私有化的问题了,所以要实现私有成员也并非毫无基础。

web前端学习之路

对于程序员来说,如果哪一天开始他停止了学习,那么他的职业生涯便开始宣告消亡。这不是什么危言耸听的怪语,而是一位大牛几年前告诉我的,他的信条。

web前端开发好学吗?

随着互联网+时代的到来,移动互联网行业的发展也是突飞猛进。无论你是否承认,这个时代已经被网页所包围了,这所有一切,都是前端工程师的杰作。今天给大家聊的就是\"前端真的好学吗?\"

Web前端小白入门

Web前端开发怎么入门,主要都有哪些要素组成?Web前端开发是由网页制作演变而来的,主要由HTML、CSS、JavaScript三大要素组成。专业的Web前端开发入门知识也一定会包含这些内容,下面就给大家简单介绍一下。

highcharts 时间少8小时问题

Highcharts 中默认开启了UTC(世界标准时间),由于中国所在时区为+8,所以经过 Highcharts 的处理后会减去8个小时。如果不想使用 UTC,有2种方法可供使用:

巧妙利用引用,将数组转换成树形数组

笔者所做的一个项目需要做一个前端的树形菜单,后端返回的数据是一个平行的list,list中的每个元素都是一个对象,例如list[0]的值为{id: 1, fid: 0, name: 一级菜单},每个元素都指定了父元素,生成的菜单可以无限级嵌套

NodeJS/JWT/Vue 实现基于角色的授权

在本教程中,我们将完成一个关于如何在 Node.js 中 使用 JavaScript ,并结合 JWT 认证,实现基于角色(role based)授权/访问的简单例子。作为例子的 API 只有三个路由,以演示认证和基于角色的授权

forward和redirect的区别?http状态码301,302分别代表什么?

从地址栏显示来说:forward是服务器内部重定向,客户端浏览器的网址不会发生变化;redirect发生一个状态码,告诉服务器去重新请求那个网址,显示的的新的网址数据共享:forward使用的是同一个request

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!