修身养性,知行合一

  • 首页
  • 爱码
    • 系统
    • 数据库
    • JavaScript
    • CSharp
    • Python
  • 生活
    • 文化
    • 美食
  • 杂谈
  • 关于
修身养性,知行合一
码字,杂谈
  1. 首页
  2. 爱码
  3. 前端
  4. JavaScript
  5. 正文

真丶深入理解 JavaScript 原型和原型链(一):两个属性

2020年12月9日 1012点热度 0人点赞 0条评论

网上有很多相关的文章、视频等资料,但很多都是片面的,不完全的。我也为了自身加深理解,所以对其进行一下简单的总结。

本来想写一篇文章进行总结,发现越写越多,还是分成几篇,分别总结吧。

两个属性

可以说 JavaScript 的很多特性都是基于原型和原型链展开的,这就要提到两个属性:

  • __proto__
  • prototype

下面先理解这两个属性。

__proto__ 属性

首先,它不是一个 JavaScript 的规范属性,只是浏览器方便获取对象的原型而创建的一个属性,但是它仍然需要理解。

它并不被推荐直接使用,而是使用其他方法代替,这个后面说。

该属性服务于对象实例,指向创建实例的构造函数的原型对象

我们从下面几个方面理解:

1、它保存着继承关系

大多数情况下,每一个对象都包含 __proto__ 属性,我们可以通过它来查看一个对象的从属关系。

一个简单的例子:

let arr = [];

arr example

其中,arr 是我们创建的一个对象,它是一个数组。我们都知道数组有内置方法,但是我们创建的 arr 并没有给出,于是它调用了父级的 Array 构造器成功创建了数组对象。而其父级内容,还有父级,它是一个 Object 对象。

至此,一个简单的 arr 对象实例创建完成,它具有三层继承关系。

2、对象中也可以不包含该属性

上面讲到了绝大对数情况。但有时候,一个对象也可以不包含该属性,比如:

// 1
var obj = { name: "jeremyjone" }; // 包含 __proto__

// 2
var obj2 = Object.create(null, { name: { value: "jeremyjone" } }); // 纯属性对象,只有 name 字段

类似上述方法 2,创建的对象是没有原型的,它仅仅是一个具有 name 字段的属性对象。

没有_proto__属性

3、它是一个对象

在创建一个对象时,__proto__ 会引用父级原型的 prototype 属性。对于对象继承时,直接使用它的属性;而对于函数继承时,可以通过调用它的 apply 方法来执行函数。

__proto__属性的引用

function User() {}

__proto__ apply属性

在创建对象的过程中,__proto__ 属性本身会做判断,如果给该属性赋一个非对象的值,它将是无效的,因为该属性在顶层可以看到,它是一个属性访问器。

所以,它本身可以算作一个对象。更严格的说,它是一个对象属性访问器(getter / setter)。

__proto__ 属性是一个对象

prototype 属性

它是 ECMAScript 的一个标准属性,也是继承机制里面非常重要的一个属性。

该属性服务于原型(构造器),包含了可以被继承的所有属性/方法

我们从下面几方面理解:

1、它是函数的一个属性

每一个 JS 函数都包含一个 prototype 属性。我们都知道 JS 的类是基于 Function 的,所以该属性也成为了继承机制的重要方式。

function User() {}

console.log(User.prototype);

Function's prototype

可以看到 User 对象中的原型 prototype 中包含一个构造方法。

这个构造方法 constructor 尤为重要,尤其是在手动编写继承代码时,一定要注意它。

2、它是一个可以被继承的对象

当初 Brendan Eich 为了解决继承问题,设置了两个属性:

  • constructor:将不需要共享的属性和方法,放在构造函数中。
  • prototype:将需要共享的属性和方法,放在 prototype 对象中。

也就是说,我们现在使用 new 关键字创建的对象,其实 new 后面跟的是对象的构造函数,而不是类。

而创建的对象,直接引用了 prototype 中的属性,也就相当于“继承”了父类属性和方法。

所以,当使用 new 关键字时,使用的都是该属性。这也是我们在往原型中添加属性时,为啥使用该属性的原因。

它们之间的关系

上面提到,实例的对象,直接引用 prototype,同时 __proto__ 保存着该对象的构造函数的 prototype。

那么,实例对象则会有如下等式:

obj.__proto__ === obj.constructor.prototype; // true

那么扩展开来,对于系统构造函数,它有如下等式:

let arr = [];
arr.__proto__ === Array.prototype;

let str = "";
str.__proto__ === String.prototype;

// ...Object、RegExp等都同理

针对自定义的对象,则有:

function User() {}
let user = new User();

user.__proto__ === user.constructor.prototype; // true
user.__proto__ === User.prototype; // true

这也验证了上面着重要理解的两句话:

  • __proto__ 属性作用于对象,它服务于对象实例
  • prototype 属性作用于原型(构造器)

constructor 的作用

它是一个构造方法,在 JS 的每一个函数中都会默认有这样一个构造方法。每当 new 出来一个对象,这个函数就会被当成一个原型,每一个实例对象的 __proto__ 属性也都会指向该函数的原型。

我们也可以通过实例对象的 __proto__ 属性找到构造函数,从而继续 new 出来其他相关对象。

function User() {}

let user1 = new User();

let constructor = user.__proto__.constructor;
let user2 = new constructor();

console.log(User === constructor); // true
console.log(user1.__proto__ === user2.__proto__); // true

上例中,其实也就说明了原型的构造函数就是原型本身。

// 接上例
console.log(User.prototype.constructor === User); // true

这在平时我们需要通过对象实例创建新对象时,将会很有用。

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可
标签: JavaScript 原型
最后更新:2020年12月9日

jeremyjone

这个人很懒,什么都没留下

打赏 点赞
< 上一篇
下一篇 >

文章评论

取消回复

文章目录
  • 两个属性
    • __proto__ 属性
    • prototype 属性
    • 它们之间的关系
最新 热点 随机
最新 热点 随机
我的开源组件 @xpyjs/gantt 100颗星星啦 volar 检查 element 表格的 slot-scope 错误 关于 *.vue 文件中使用 TypeScript 声明类型报错的解决方案 element table 加载时宽度闪烁问题 windows 无法登录便签、OneNote等应用 vue2 中 vuex 对 ts 的支持
volar 检查 element 表格的 slot-scope 错误我的开源组件 @xpyjs/gantt 100颗星星啦
在 CentOS 上安装 PHP7 .net core 3.x使用mysql EntityFramework 群辉 RAID1 数据恢复小记 css实现跳动的文字 真丶深入理解JavaScript异步编程(二):Promise 原理 利用MVC5 Filter实现登录状态的判断

(っ•̀ω•́)っ✎⁾⁾ 开心每一天

COPYRIGHT © 2021 jeremyjone.com. ALL RIGHTS RESERVED.

THEME KRATOS MADE BY VTROIS

京ICP备19012859号-1

京公网安备 11010802028585号