Mar*_*nik 17 javascript constructor google-chrome google-chrome-devtools
在Chrome的JavaScript控制台中:
> function create(proto) {
function Created() {}
Created.prototype = proto
return new Created
}
undefined
> cc = create()
Created {}
> cc
Created {}
Run Code Online (Sandbox Code Playgroud)
Created是函数私有的create函数; create完成后,没有(我知道)引用Created.然而,Chrome可以随时显示该功能的名称,从它创建的对象开始.
Chrome没有通过遵循"天真"方法实现这一目标:
> cc.constructor
function Object() { [native code] }
> cc.toString()
"object [Object]"
Run Code Online (Sandbox Code Playgroud)
无论如何,我没有constructor把proto争论传递给create:
> cc.__proto__.hasOwnProperty("constructor")
false
Run Code Online (Sandbox Code Playgroud)
我猜测的是,Created为了instanceof机制,JavaScript VM仍然存在.有人说instanceof
测试一个对象在其原型链中是否具有构造函数的prototype属性.
但是在我输入的上面的代码中create(),有效地传递undefined了原型; 因此Created甚至没有prototype设置到实际cc.__proto__.如果我们破解create公开Created函数,我们可以验证这个:
function create(proto) {
function Created() {}
Created.prototype = proto
GlobalCreated = Created
return new Created
}
Run Code Online (Sandbox Code Playgroud)
现在让我们输入
> cc = create()
Created {}
> GlobalCreated
function Created() {}
> GlobalCreated.prototype
undefined
> cc instanceof GlobalCreated
TypeError: Function has non-object prototype 'undefined' in instanceof check
Run Code Online (Sandbox Code Playgroud)
Chrome的JavaScript引擎究竟保留了什么来使控制台中的对象呈现工作?它是构造函数,还是函数名?
是否需要保留比控制台打印输出更重要的东西?
这种保留对内存消耗有什么影响?例如,如果构造函数(甚至它的名称)异常巨大,该怎么办?
它只是Chrome吗?我已经对Firebug和Safari进行了重新测试,他们的控制台不会以这种方式呈现对象.但是,出于其他可能的目的,它们是否仍然保留相同的数据(例如,由于JavaScript VM固有的真正关注)?
Eli*_*gem 12
我最近重新回答了这个问题/答案,我想我已经弄清楚为什么chrome似乎"挂在"这个Created名字上.它并不是V8独有的东西,但我认为这是V8如何在幕后工作的结果(我在初始答案中解释的隐藏物体),以及V8需要做什么(符合ECMAScript标准) .
默认情况下,任何函数,构造函数或其他函数共享相同的构造函数和原型链:
function Created(){};
console.log(Created.constructor);//function Function() { [native code] }
console.log(Object.getPrototypeOf(Created));//function Empty() {}
console.log(Created.__proto__);//same as above
console.log(Created.prototype);//Created {}
Run Code Online (Sandbox Code Playgroud)
这告诉我们一些事情:所有函数共享本机构Function造函数,并从function Empty(){}用作原型的特定函数instance()继承.但是,函数的prototype属性必须是一个对象,如果它被作为构造函数调用,函数将返回(参见ECMAScript标准).
在调用Function对象作为新创建的对象的构造函数之前,prototype属性的值用于初始化新创建的对象的[[Prototype]]内部属性.此属性具有属性{[[Writable]]:true,[[Enumerable]]:false,[[Configurable]]:false}.
我们可以通过以下方式轻松验证Created.prototype.constructor:
console.log(Created.prototype.constructor);//function Created() {}
Run Code Online (Sandbox Code Playgroud)
现在让我们暂时列出V8需要创建的隐藏类,并且可能会创建,以使其符合标准:
function Created(){}
Run Code Online (Sandbox Code Playgroud)
隐藏课程:
Object当然是所有物体的母亲,其中Function有一个特定的孩子Function:正如我们演示的那样,这个本机对象是构造函数function Empty:原型,我们的函数将从中继承Created 我们的空函数将继承上述所有内容在这个阶段,没有发生任何异常,并且不言而喻,当我们返回此 Created构造Created函数的实例时,该函数将因其原型而暴露.
现在,因为我们正在重新分配该prototype属性,你可以争辩说这个实例将被丢弃并丢失,但据我所知,这不是V8将如何处理这种情况.相反,它会创建一个额外的隐藏类,prototype在遇到此语句后简单地覆盖其父级的属性:
Created.prototype = proto;
它的内部结构最终会看起来像这样(这次编号,因为我将更深入地回顾这个继承链中的某些阶段):
Object当然是所有物体的母亲,其中Function有一个特定的孩子Function:正如我们演示的那样,这个本机对象是构造函数function Empty:原型,我们的函数将从中继承Created 我们的空函数将继承上述所有内容Created2:扩展前一个类(Created),并覆盖prototypeCreated仍然可见?那是百万美元的问题,我认为我现在有了答案:优化
V8根本不能,也不应该被允许优化Created隐藏的类(第4阶段).为什么?因为覆盖的prototype是一个参数.这是无法预测的.V8可能会对优化代码执行的操作是存储一个隐藏对象4,每当create调用该函数时,它将创建一个扩展第4阶段的新隐藏类,prototype使用传递给函数的任何值覆盖该属性.
因此,Created.prototype将始终存在于每个实例的内部表示内部.同样重要的是要注意你可以prototype用一个实际引用一个实例Created(具有一个混乱的原型链,但仍然)的属性替换该属性:
cc = create();
console.log(Object.getPrototypeOf(cc))//Object {}
cc = create(new GlobalCreated);
console.log(Object.getPrototypeOf(cc));//Created {}
Run Code Online (Sandbox Code Playgroud)
对于心灵弯曲者来说,这是怎么回事?创作剧本作家,吃掉你的心...
无论如何,我希望这一滴运动对这里的某些人有所帮助,如果没有,我会对评论作出回应,所以对我可能犯过的错误进行更正,或者有关此更新的某些部分的问题有点不清楚,这是值得欢迎的. ..
我会尝试逐个回答问题,但正如你所说,它们都是密切相关的,所以答案重叠到一定程度.
在读这篇文章时,请记住我一次性写这篇文章,同时感觉有点发烧.我不是V8专家,而是基于我前段时间在V8内部进行挖掘的回忆.底部的链接是官方文档,当然包含有关该主题的更准确和最新信息.
发生了
什么chrome的V8引擎实际上是为每个对象创建一个隐藏类,并且该类被映射到对象的JS表示.
或者谷歌的人自己说:
为了减少访问JavaScript属性所需的时间,V8不使用动态查找来访问属性.相反,V8在幕后动态创建隐藏类.
在你的情况下发生的事情,扩展,从特定实例创建一个新的构造函数并覆盖constructor属性实际上只是你在这个图上看到的内容:

隐藏类C0可以视为标准Object类.基本上,V8会解释您的代码,构建一组类似C++的类,并在需要时创建一个实例.每当您更改/添加属性时,您拥有的JS对象都将设置为指向不同的实例.
在你的create功能中,这很可能 - 正在发生的事情:
function create(proto)
{//^ creates a new instance of the Function class -> cf 1 in list below
function Created(){};//<- new instance of Created hidden class, which extends Function cf 2
function Created.prototype = proto;//<- assigns property to Created instance
return new Created;//<- create new instance, cf 3 for details
}
Run Code Online (Sandbox Code Playgroud)
Function是本土构造.V8的工作方式意味着所有函数都引用了一个Function类.但是,它们间接引用了这个类,因为每个函数都有自己的特性,这些特性在派生的隐藏类中指定.create那么,应该被视为对create extends HiddenFunction类的引用.class create : public Hidden::Function{/*specifics here*/}Create函数引用了与之相同的隐藏函数create.然而,在声明它之后,该类接收1个专有属性,称为prototype,因此创建另一个隐藏类,指定此属性.这是构造函数的基础.因为create所有这些都发生的函数体,这是给定的,并且V8可能会足够聪明地预先创建这些类:无论如何:在C++伪代码中,它看起来类似于下面的代码清单1.Created名称,该名称是本地create的范围.当然,返回的实例create仍然保留了对这个实例的引用,但这就是JS作用域的工作方式,所以这适用于所有引擎......想想闭包,你会得到我的意思(我真的很挣扎)这种讨厌的发烧...抱歉唠叨这个)Create指向这个隐藏类的一个实例,它扩展了一个扩展类的类(我试图在第2点解释).当然,使用new关键字触发Function类定义的行为(因为它是JS语言结构).这导致创建一个隐藏类,对于所有实例可能都是相同的:它扩展了本机对象,并且它有一个constructor属性,它引用了Created我们刚刚创建的实例.create尽管返回的实例都是相似的.当然,他们的构造函数可能具有不同的prototype属性,但它们生成的对象看起来都是一样的.我相当确信V8只会为对象create返回创建一个隐藏类.我不明白为什么实例应该需要不同的隐藏类:它们的属性名称和计数是相同的,但是每个实例引用另一个实例,但这就是类的用途无论如何:第2项的代码清单Created,隐藏类术语中可能看起来像的伪代码表示:
//What a basic Function implementation might look like
namespace Hidden
{//"native" JS types
class Function : public Object
{
//implement new keyword for constructors, differs from Object
public:
Function(...);//constructor, function body etc...
Object * operator new ( const Function &);//JS references are more like pointers
int length;//functions have a magic length property
std::string name;
}
}
namespace Script
{//here we create classes for current script
class H_create : public Hidden::Function
{};
class H_Created : public Hidden::Function
{};//just a function
class H_Created_with_prototype : public H_Created
{//after declaring/creating a Created function, we add a property
//so V8 will create a hidden class. Optimizations may result in this class
// being the only one created, leaving out the H_Created class
public:
Hidden::Object prototype;
}
class H_create_returnVal : public Hidden::Object
{
public:
//the constructor receives the instance used as constructor
//which may be different for each instance of this value
H_create_returnVal(H_Created_with_prototype &use_proto);
}
}
Run Code Online (Sandbox Code Playgroud)
忽略任何(可能的)语法奇怪(自从我编写一行C++以来已经超过一年),并忽略名称空间和古怪的名称,列出的类除了Hidden::Function实际上需要创建的所有隐藏类之外运行你的代码.然后,您的所有代码都会分配对这些类的实例的引用.这些类本身在内存中占用的空间不大.任何其他引擎都会创建尽可能多的对象,因为它们也需要符合ECMAScript规范.
所以我想,就像这样看待它,这类问题回答了你所有的问题:并非所有的引擎都是这样的,但是这种方法不会导致大量的内存被使用,是的,这确实意味着很多信息保留/ data /对所有对象的引用,但这只是不可避免的,在某些情况下这种方法的副作用很快.
更新:我做了一些挖掘,并找到了一个如何使用模板将JS函数添加到V8的示例,它说明了V8如何将JS对象/函数转换为C++类,请参阅此处的示例
这只是我的推测,但我一点都不会惊讶于了解V8的工作方式,而且这种保留业务一般用于垃圾收集和内存管理(EG:删除属性更改隐藏类和比如)
例如:
var foo = {};//foo points to hidden class Object instance (call id C0)
foo.bar = 123;//foo points to child of Object, which has a property bar (C1)
foo.zar = 'new';//foo points to child of C1, with property zar (C2)
delete foo.zar;//C2 level is no longer required, foo points to C1 again
Run Code Online (Sandbox Code Playgroud)
最后一点只是我的猜测,但它可能是可能的GC做到这一点.
这个保留用的是什么
正如我所说,在V8中,JS对象实际上是一个指向C++类的指针.访问属性(这也包括数组的神奇属性!)很快.真的,真的很快.理论上,访问属性是 O(1)操作.
这就是IE上的原因:
var i,j;
for(i=0,j=arr.length;i<j;++i) arr[i] += j;
Run Code Online (Sandbox Code Playgroud)
快于:
for (i=0;i<arr.length;++i) arr[i] += arr.length;
Run Code Online (Sandbox Code Playgroud)
在镀铬时,如她所示,arr.length速度更快.我也回答了这个问题,它也包含了一些你可能要检查的V8的细节.可能是我的答案不再(完全)适用,因为浏览器及其引擎变化很快......
内存怎么样
不是一个大问题.是的,有时候Chrome可能会占用一些资源,但JS并不总是应该受到责备.编写干净的代码,大多数浏览器的内存占用量都不会太大.
如果你创建了一个巨大的构造函数,那么V8将创建一个更大的隐藏类,但是如果该类已经指定了很多属性,那么它们需要额外隐藏类的机会就会更小.
当然,每个函数都是Function类的一个实例.无论如何,这是函数式语言中的本机(并且非常重要)类型很可能是高度优化的类.
无论如何:就内存使用而言:V8在管理内存方面做得非常好.例如,远远超过IE的旧版本.以至于V8引擎用于服务器端JS(如在node.js中),如果内存确实存在问题,那么你就不会梦想在必须启动和运行的服务器上运行V8.可能,现在好吗?
在某种程度上,这只是Chrome是的吗?V8确实特别关注它如何使用和运行JS.它不是将代码编译为字节码并运行它,而是将AST直接编译成机器代码.同样,就像隐藏类技巧一样,这是为了提高性能.
我知道我在CR的答案中包含了这个图表,但仅仅是为了完整性:这是一个显示chrome(底部)和其他JS引擎(顶部)之间差异的图表

请注意,在字节码指令和CPU下面,有一个(橙色)解释器层.这是V8不需要的,因为JS直接被翻译成机器代码.
缺点是这使得某些优化变得更难,特别是关于在代码中使用DOM数据和用户输入的那些(例如someObject[document.getElementById('inputField').value]:),并且在CPU上初始处理代码更加困难.
好处是:一旦将代码编译成机器代码,它就是您将获得的最快速度,并且运行代码可能会减少开销.大多数情况下,字节码解释器在CPU上较重,这就是为什么FF和IE上的繁忙循环可以使浏览器提醒用户"正在运行的脚本",询问他们是否要停止它.
Ale*_*vig -6
您从 create 返回一个新实例到名为 Created 的对象。
create()()
> TypeError: object is not a function
Run Code Online (Sandbox Code Playgroud)
如果您要删除“new”关键字,那么您会将 Created 函数暴露给调用者的范围。
| 归档时间: |
|
| 查看次数: |
1949 次 |
| 最近记录: |