JavaScript 原型和对象创建底层原理
1. prototype/__proto__/constructor
JS原型链和继承网上已经烂大街了,5毛可以买一堆,这里只提一下:
constructor:普通对象和函数对象都有,指向创建它的函数
prototype: 函数对象才有,指向构造函数的原型对象(另一个普通对象)
__proto__: 普通对象和函数对象都有,指向创建它的构造函数的原型对象
function Fun1(){};
Fun1.prototype.constructor == Fun1 //true
var f2 = new Fun1();
f2.__proto__ == Fun1.prototype //true
2. 对象实例的创建过程
当实例化新对象时,JS将使用与来自相同构造函数的先前对象相同的初始隐藏类创建它们。
当添加属性时,对象从一个隐藏类转换为另外一个隐藏类,通常遵循所谓的“转换树”中的先前转换。例如,如果我们有以下构造函数:
function fun1(){
this.a = 1;
this.b = 2;
}
创建过程大致如下(对于普通对象{a:1, b:2}也会有类似的过程):
fun1: M0 {}
|
this.a: M1 {a:?}
|
this.b: M2 {a:?,b:?}
- 初始化一个对象时(如: var o = new fun1()),会使用一个附加在fun1上的没有任何属性的初始隐藏类M0。
- 当添加一个属性a时,会从隐藏类M0转变为M1以描述新的属性a。
- 当再次添加属性b时,会得到M2来体现属性a和b。
在第二次用同样的构造函数创建一个新的对象实例时(如: var o2 = new fun1()),会复用M0,M1和M2。
这种机制有下面几个优点:
- 只有第一次创建对象实例时会有比较大的性能开销,接下来如果再次创建实例时,将会复用之前的缓存,速度很快。
- 这种方法创建的对象实例通常比采用创建整个属性字典的对象实例要小,仅需要存储值在对象实例中而不必存储属性相关信息。
- 使用唯一的位置来存储隐藏类,可以很快的被再次使用。
可以参考如下:
M0
{}
|
M1
{a:?}
/ \
M2 M3
{a:?,b:?} {a:?,c:?}
3. prototype的创建过程
与实例的创建过程不同,原型的创建过程并不会使用隐藏类,因为原型prototypes通常情况下是一个唯一的对象,并且不会被其他的不同对象所共享结构。
- 不同对象将拥有不同的原型,共享原型并不会带来性能上的收益,因此没有必要。
- 原型通常只会创建一次,为原型创建隐藏类只会带来很多无用的内存碎片。
Prototype创建有二个主要的阶段:
- 建立
原型在建立阶段使用dictionary键值对存储,速度非常快。对比隐藏类的建立过程,使用键值对存储不会切换到底层运行环境。 - 使用
任何对原型的访问,或者通过原型链的访问,都会切换到使用阶段。
function Foo() {}
// Prototype在'建立'模式
var proto = Foo.prototype;
proto.method1 = function() { ... }
proto.method2 = function() { ... }
var o = new Foo();
// 从'建立'模式切换到'使用'模式
o.method1();
// 同样切换到'使用'模式
proto.method1.call(o);
4. prototype优化
那么了解上面的原型创建过程有什么用呢?
JS很难在编译阶段进行代码分析,即使某个类被用作原型。当我们发现一个类被当做原型,如:
var o = {x:1};
func.prototype = o;
因为原型是可以改变的,因此以上代码并不能确定O被用作原型,除非直到所有步骤结束。
所以:JS不得不首先进行隐藏类的创建过程,并转化为原型建立过程,这非常消耗性能。
那么应该怎么做呢?如下:
var o = {};
func.prototype = o;
o.x = 1;
如果一个对象要作为原型,那么尽量在给对象添加属性之前就把该对象赋给原型属性。
更好的方式是下面这种:
func.prototype = Object.create(…);
func.prototype.method1 = …
func.prototype.method2 = …
最后,优雅的写法是:
var proto = func.prototype = Object.create(…);
proto.method1 = …
proto.method2 = …
JavaScript 原型和对象创建底层原理的更多相关文章
- JavaScript 对象属性底层原理
对象属性类型 1. 数据属性 [[Configurable]]:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性,特性默认值为true [[Enum ...
- JS原型链与instanceof底层原理
一.问题: instanceof 可以判断一个引用是否属于某构造函数: 另外,还可以在继承关系中用来判断一个实例是否属于它的父类型. 老师说:instanceof的判断逻辑是: 从当前引用的proto ...
- 浅谈系列之 javascript原型与对象
在我学习与使用javascript三个月中,我一直对javascript的继承关系以及prototype理解不清,导致很多时候为什么这么用说不出个所以然来.截止到本周为止,通过之前的学习以及自己的再学 ...
- 【JavaScript回顾】对象创建的几种模式
组合使用构造函数模式和原型模式 创建自定义类型的常见方式,就是组合使用构造函数模式与原型模式.构造函数模式用于定义实 例属性,而原型模式用于定义方法和共享的属性.结果,每个实例都会有自己的一份实例属性 ...
- javascript中的对象创建与继承
js是一门基于原型的面向对象语言,与传统的面向对象如Java,C#相比,它在对象创建及继承上有自己独特的实现方式,本文主要描述js中对象创建及继承的一些实践. 1.对象创建 方式一:工厂模式创建对象 ...
- javascript ActiveXObject FileSystemObject 对象,创建、复制、删除、读取文件等
Javascript是网页制作中离不开的脚本语言,依靠它,一个网页的内容才生动活泼.富有朝气.但也许你还没有发现并应用它的一些更高级的功能吧?比如,对文件和文件夹进行读.写和删除,就象在VB.VC等高 ...
- 深入 JavaScript 中的对象以及继承原理
ES6引入了一个很甜的语法糖就是 class, class 可以帮助开发者回归到 Java 时代的面向对象编程而不是 ES5 中被诟病的面向原型编程. 我也在工作的业务代码中大量的使用 class, ...
- 【读书笔记】读《JavaScript模式》 - 对象创建模式
JavaScript是一种简洁明了的语言,其中并没有在其他语言中经常使用的一些特殊语法特征,比如命名空间(namespace).模块(module).包(package).私有属性(private p ...
- Javascript属性constructor/prototype的底层原理
在Javascript语言中,constructor属性是专门为function而设计的,它存在于每个function的prototype属性中. 这个constructor保存了指向function ...
随机推荐
- Fiddler抓不到https的解决办法
这个问题可能是证书的问题,操作步骤如下: Step 1:打开系统证书管理器certmgr.msc,点击操作–查找证书,然后输入fiddler查找所有相关证书,将查找出来的结果全部删除: Step 2 ...
- .net 服务因为GC时遇到的问题和解决办法
1.问题: .net单一服务中,大量的请求访问后台服务,多线程处理请求,但每个线程都可能出现超时的现象.记录超时日志显示,超时可能在序列化时,Socket异步发送AsyncSend数据时,普通业务处理 ...
- python学习第四次笔记
python学习第四次记录 列表list 列表可以存储不同数据类型,而且可以存储大量数据,python的限制是 536870912 个元素,64位python的限制是 1152921504606846 ...
- L2-001 紧急救援 (25 分)
L2-001 紧急救援 (25 分) 作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图.在地图上显示有多个分散的城市和一些连接城市的快速道路.每个城市的救援队数量和每一条连接两个城市的快 ...
- jcifs windows 域账户单点登录(转)
1.首先从http://jcifs.samba.org 这个站点下载 jcifs-1.3.2.jar包. 2.把这个包放到相应的lib文件下面. 3.对web.xml文件进行配置,添加如下内容 < ...
- Struts2 action 跳转到web-inf下,
初学,这个问题困扰了1天.刚开始以为代码或者是配置文件哪里写错了,仔细检查了好几遍确定代码没有错,实在没办法了,准备发帖求助.就在我截图的时候,突然发现我的@Action中有点别扭,即使他没有报错. ...
- 两款不错的Linux密码生成工具
先介绍最简单的方法,Linux自带的 $ strings /dev/urandom | | ; echo whucNWhr35W6ZP0MxrLQ $ /dev/random | base64 | t ...
- Mathematica求微分换元
[转载请注明出处]http://www.cnblogs.com/mashiqi 2017/12/16 有时我们需要对PDEs中的各项进行变量替换,比如把$\frac{\text{d}}{\text{d ...
- [转]JAVA 根据经纬度算出附近的正方形的四个角的经纬度
csv文件转化为geojson文件中,涉及到路测图的打点生成,打点是由一个个正方形组成,而正方形是由四个点组成的,这四个点根据经纬度和范围生成,具体的实现代码是从网上找来的: /** * * @par ...
- pymysql操作mysql的脚本示例
#!/usr/bin/env python#-*- coding:UTF-8 -*- from multiprocessing import Process , Queuefrom queue imp ...