写给Java开发者看的JavaScript对象机制

帮助面向对象开发者理解关于JavaScript对象机制

本文是以一个熟悉OO语言的开发者视角,来解释JavaScript中的对象。

对于不了解JavaScript 语言,尤其是习惯了OO语言的开发者来说,由于语法上些许的相似会让人产生心理预期,JavaScript中的原型继承机制和class语法糖是让人迷惑的。

如果你已经对prototype机制已有了解,但是由于两者对象机制的巨大(本质)差异,对它和构造函数,实例对象的关系仍有疑惑,本文或许可以解答你的问题。

我们看下面的代码,可以看出和OO语言相比,语法上也有很大分别:

// 定义一个类
class Foo {constructor() {this.a = 'a';}
}//实例化对象
const foo = new Foo();//定义原型的属性
Foo.prototype.b = 'b';//实例可以访问属性
foo.b // "b"//修改原型的属性
Foo.prototype.b= 'B';//实例属性值没有被修改
foo.b // "b"

类已经定义了怎么还能修改呢?prototype又是什么?

不存在面向对象

对于熟悉了面向对象的开发者而言JS中种种非预期操作的存在,都是因为JavaScript中根本没有面向对象的概念,只有对象,没有类。

即使ES6新添了class语法,不意味着JS引入了面向对象,只是原型继承的语法糖。

原型是什么

什么是原型?如果说类是面向对象语言中对象的模版,原型就是 JS中创造对象的模版。

在面向类的语言中,实例化类,就像用模具制作东西一样。实例化一个类就意味着“把类的形态复制到物理对象中”,对于每一个新实例来说都会重复这个过程。

但是在JavaScript中,并没有类似的复制机制。你不能创建一个类的多个实例,只能创建多个对象,它们[[Prototype]]关联的是同一个对象。

//构造函数
function Foo(){
}
//在函数的原型上添加属性
Foo.prototype.prototypeAttribute0 = {status: 'initial'};const foo0 = new Foo();
const foo1 = new Foo();
foo0.prototypeAttribute0 === foo1.prototypeAttribute0 //true

对象、构造函数和原型的关系

当我们创建一个新对象的时候,发生了什么,对象、构造函数和原型到底什么。

先简单地概括:

原型用于定义共享的属性和方法。

构造函数用于定义实例属性和方法,仅负责创造对象,与对象不存在直接的引用关系。

我们先不用class语法糖,这样便于读者理解和暴露出他们之间真正的关系。

// 先创建一个构造函数 定义原型的属性和方法
function Foo() {this.attribute0 = 'attribute0';
}

当创建了一个函数,就会为该函数创建一个prototype属性,它指向函数原型。

所有的原型对象都会自动获得一个constructor属性,这个属性的值是指向原型所在的构造函数的指针。

clipboard.png

现在定义原型的属性和方法


Foo.prototype.prototypeMethod0 = function() {console.log('this is prototypeMethod0');
}Foo.prototype.prototypeAttribute0 = 'prototypeAttribute0';

好了,现在,新建一个对象,

const foo = new Foo();foo.attribute0 // "attribute0"
foo.prototypeAttribute0 //"prototypeAttribute0"
foo.prototypeMethod0() // this is prototypeMethod0

它拥有自己的实例属性attribute0,并且可以访问在原型上定义的属性和方法,他们之间的引用关系如图所示。

clipboard.png

当调用构造函数创建实例后,该实例的内部会包含一个指针(内部对象),指向构造函数的原型对象。

当读取实例对象的属性时,会在实例中先搜寻,没有找到,就会去原型链中搜索,且总是会选择原型链中最底层的属性进行访问。<!--原型对象自己也可以有原型对象,这样就构成了原型链。关于原型链这里不作过多介绍-->

对象的原型可以通过__proto__在chrome等浏览器上访问。

__proto__是对象的原型指针,prototype是构造函数所对应的原型指针。

语法糖做了什么

ES6推出了class语法,为定义构造函数和原型增加了便利性和可读性。

class Foo {constructor(){this.attribute0 = 'attribute0';}prototypeMethod0(){console.log('this is prototypeMethod0')}
}/* 相当于下面的声明*/
function Foo() {this.attribute0 = 'attribute0';
}Foo.prototype.prototypeMethod0 = function() {console.log('this is prototypeMethod0')
}

class中的constractor相当于构造函数,而class中的方法相当于原型上的方法。、

值得注意的特性

属性屏蔽 —— 避免实例对象无意修改原型

看这段代码,思考输出的结果。

class Foo {prototypeMethod0(){console.log('this is prototypeMethod0')}
}const foo0 = new Foo();
const foo1 = new Foo();foo0.prototypeMethod0 === foo0.__proto__.prototypeMethod0 // truefoo0.prototypeMethod0 = () => console.log('foo0 method');
foo0.prototypeMethod0(); //??
foo1.prototypeMethod0(); //??
foo0.prototypeMethod0 === foo0.__proto__.prototypeMethod0 // ??

输出的结果是

foo0.prototypeMethod0(); // foo0 method
foo1.prototypeMethod0(); // this is prototypeMethod0
foo0.prototypeMethod0 === foo0.__proto__.prototypeMethod0 // false

我们知道对象(即便是原型对象),都是运行时的。

创建之初,foo本身没有prototypeMethod0这个属性,访问foo0.prototypeMethod0将会读取foo0.__proto__.prototypeMethod0

直接修改foo0.prototypeMethod0没有改变__proto__上的方法原因是存在属性屏蔽

现在的情况是:想要修改foo0.prototypeMethod0prototypeMethod0foo中不存在而在上层(即foo.__proto__中存在),并且这不是一个特殊属性(如只读)。

那么会在foo中添加一个新的属性。

这便是为什么直接修改却没有影响__proto__的原因。

<!--更多属性屏蔽的场景也不做赘述-->

小结

再温习一遍这些定义:

原型用于定义共享的属性和方法。

构造函数用于定义实例属性和方法,仅负责创造对象,与对象不存在直接的引用关系。

__proto__是对象的原型指针,prototype是构造函数的原型指针。

在解释原型作用的文章或书籍中,我们会听到继承这样的术语,其实更准确地,委托对于JavaScript中的对象模型来说,是一个更合适的术语。

委托行为意味着某些对象在找不到属性或者方法引用时会把这个请求委托给另一个对象。对象之间的关系不是复制而是委托。


参考

《JavaScript高级程序设计》

《你不知道的JavaScript》

本文仅供解惑,要在脑袋里形成系统的概念,还是要看书呀。

有疑问欢迎大家一起讨论。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/389924.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Pythonic---------详细讲解

作者&#xff1a;半载流殇 链接&#xff1a;https://zhuanlan.zhihu.com/p/35219750 来源&#xff1a;知乎 著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。Pythonic&#xff0c;简言之就是以Python这门语言独特的方式写出既简洁又优美的代码…

大数据ab 测试_在真实数据上进行AB测试应用程序

大数据ab 测试Hello Everyone!大家好&#xff01; I am back with another article about Data Science. In this article, I will write about what is A-B testing and how to use it on real life data-set to compare two advertisement methods.我回来了另一篇有关数据科…

492. 构造矩形

492. 构造矩形 作为一位web开发者&#xff0c; 懂得怎样去规划一个页面的尺寸是很重要的。 现给定一个具体的矩形页面面积&#xff0c;你的任务是设计一个长度为 L 和宽度为 W 且满足以下要求的矩形的页面。要求&#xff1a; 你设计的矩形页面必须等于给定的目标面积。 宽度 …

node:爬虫爬取网页图片

前言 周末自己在家闲着没事&#xff0c;刷着微信&#xff0c;玩着手机&#xff0c;发现自己的微信头像该换了&#xff0c;就去网上找了一下头像&#xff0c;看着图片&#xff0c;自己就想着作为一个码农&#xff0c;可以把这些图片都爬取下来做成一个微信小程序&#xff0c;说干…

如何更好的掌握一个知识点_如何成为一个更好的讲故事的人3个关键点

如何更好的掌握一个知识点You’re launching a digital transformation initiative in the middle of the ongoing pandemic. You are pretty excited about this big-ticket investment, which has the potential to solve remote-work challenges that your organization fac…

centos 搭建jenkins+git+maven

gitmavenjenkins持续集成搭建发布人:[李源] 2017-12-08 04:33:37 一、搭建说明 系统&#xff1a;centos 6.5 jdk&#xff1a;1.8.0_144 jenkins&#xff1a;jenkins-2.93-1.1 git&#xff1a;git-2.9.0 maven&#xff1a;Maven 3.3.9 二、部署 2.1、jdk安装 1&#xff09;下…

638. 大礼包

638. 大礼包 在 LeetCode 商店中&#xff0c; 有 n 件在售的物品。每件物品都有对应的价格。然而&#xff0c;也有一些大礼包&#xff0c;每个大礼包以优惠的价格捆绑销售一组物品。 给你一个整数数组 price 表示物品价格&#xff0c;其中 price[i] 是第 i 件物品的价格。另有…

记录一次spark连接mysql遇到的问题

在使用spark连接mysql的过程中报错了&#xff0c;错误如下 08:51:32.495 [main] ERROR - Error loading factory org.apache.calcite.jdbc.CalciteJdbc41Factory java.lang.NoClassDefFoundError: org/apache/calcite/linq4j/QueryProviderat java.lang.ClassLoader.defineCla…

什么事数据科学_如果您想进入数据科学,则必须知道的7件事

什么事数据科学No way. No freaking way to enter data science any time soon…That is exactly what I thought a year back.没门。 很快就不会出现进入数据科学的怪异方式 ……这正是我一年前的想法。 A little bit about my data science story: I am a complete beginner…

python基础03——数据类型string

1. 字符串介绍 在python中&#xff0c;引号中加了引号的字符都被认为是字符串。 1 namejim 2 address"beijing" 3 msg My name is Jim, I am 22 years old! 那单引号、双引号、多引号有什么区别呢&#xff1f; 1) 单双引号木有任何区别&#xff0c;部分情况 需要考虑…

Java基础-基本数据类型

Java中常见的转义字符: 某些字符前面加上\代表了一些特殊含义: \r :return 表示把光标定位到本行行首. \n :next 表示把光标定位到下一行同样的位置. 单独使用在某些平台上会产生不同的效果.通常这两个一起使用,即:\r\n. 表示换行. \t :tab键,长度上相当于四个或者是八个空格 …

季节性时间序列数据分析_如何指导时间序列数据的探索性数据分析

季节性时间序列数据分析为什么要进行探索性数据分析&#xff1f; (Why Exploratory Data Analysis?) You might have heard that before proceeding with a machine learning problem it is good to do en end-to-end analysis of the data by carrying a proper exploratory …

TortoiseGit上传项目到GitHub

1. 简介 gitHub是一个面向开源及私有软件项目的托管平台&#xff0c;因为只支持git 作为唯一的版本库格式进行托管&#xff0c;故名gitHub。 2. 准备 2.1 安装git&#xff1a;https://git-scm.com/downloads。无脑安装 2.2 安装TortoiseGit(小乌龟)&#xff1a;https://torto…

496. 下一个更大元素 I

496. 下一个更大元素 I 给你两个 没有重复元素 的数组 nums1 和 nums2 &#xff0c;其中nums1 是 nums2 的子集。 请你找出 nums1 中每个元素在 nums2 中的下一个比其大的值。 nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比 x 大的元素。如果…

利用PHP扩展Taint找出网站的潜在安全漏洞实践

一、背景 笔者从接触计算机后就对网络安全一直比较感兴趣&#xff0c;在做PHP开发后对WEB安全一直比较关注&#xff0c;2016时无意中发现Taint这个扩展&#xff0c;体验之后发现确实好用&#xff1b;不过当时在查询相关资料时候发现关注此扩展的人数并不多&#xff1b;最近因为…

美团骑手检测出虚假定位_在虚假信息活动中检测协调

美团骑手检测出虚假定位Coordination is one of the central features of information operations and disinformation campaigns, which can be defined as concerted efforts to target people with false or misleading information, often with some strategic objective (…

869. 重新排序得到 2 的幂

869. 重新排序得到 2 的幂 给定正整数 N &#xff0c;我们按任何顺序&#xff08;包括原始顺序&#xff09;将数字重新排序&#xff0c;注意其前导数字不能为零。 如果我们可以通过上述方式得到 2 的幂&#xff0c;返回 true&#xff1b;否则&#xff0c;返回 false。 示例 …

org.apache.maven.archiver.MavenArchiver.getManifest

eclipse导入新的maven项目时&#xff0c;pom.xml第一行报错&#xff1a; org.apache.maven.archiver.MavenArchiver.getManifest(org.apache.maven.project.MavenProject, org.apache.maven.archiver.MavenArchiveConfiguration) 解决办法&#xff1a; help -> Install New…

杀进程常用命令

杀进程命令pkill 进程名killall 进程名 # 平缓kill -HUP pid # 平缓kill -USR2 pidkill pid &#xff08;-9 不要使用&#xff09;转载于:https://www.cnblogs.com/jmaly/p/9492406.html

CertUtil.exe被利用来下载恶意软件

1、前言 经过国外文章信息&#xff0c;CertUtil.exe下载恶意软件的样本。 2、实现原理 Windows有一个名为CertUtil的内置程序&#xff0c;可用于在Windows中管理证书。使用此程序可以在Windows中安装&#xff0c;备份&#xff0c;删除&#xff0c;管理和执行与证书和证书存储相…