개체의 [[prototype]]을 변경하는 것이 성능에 나쁜 이유는 무엇입니까?
표준 setPrototypeOf
기능 및 비표준 속성에 대한 MDN 문서에서 : __proto__
객체의 [[Prototype]]을 변경하는 것은 이것이 어떻게 이루어 졌는지에 관계없이 매우 느리고 피할 수없이 현대 JavaScript 구현에서 후속 실행을 느리게하기 때문에 강력히 권장되지 않습니다.
Function.prototype
속성을 추가하는 데 사용 하는 것은 javascript 클래스에 멤버 함수를 추가 하는 방법입니다. 그런 다음 다음과 같이 표시됩니다.
function Foo(){}
function bar(){}
var foo = new Foo();
// This is bad:
//foo.__proto__.bar = bar;
// But this is okay
Foo.prototype.bar = bar;
// Both cause this to be true:
console.log(foo.__proto__.bar == bar); // true
왜 foo.__proto__.bar = bar;
나쁜가요? 그 나쁘지 않은 것이 그렇게 나쁘지 않다면 Foo.prototype.bar = bar;
?
그렇다면 왜이 경고가 발생하는지 : 최신 자바 스크립트 구현에서 매우 느리고 불가피하게 후속 실행 속도가 느려집니다 . 확실히 Foo.prototype.bar = bar;
그렇게 나쁘지는 않습니다.
업데이트 아마도 돌연변이는 재 할당을 의미했을 것입니다. 수락 된 답변을 참조하십시오.
// This is bad: //foo.__proto__.bar = bar; // But this is okay Foo.prototype.bar = bar;
아니요. 둘 다 같은 일을하고 있고 ( foo.__proto__ === Foo.prototype
) 둘 다 괜찮습니다. 그들은 단지 객체에 bar
속성을 생성하고 있습니다 Object.getPrototypeOf(foo)
.
문이 나타내는 것은 __proto__
속성 자체에 할당하는 것입니다.
function Employee() {}
var fred = new Employee();
// Assign a new object to __proto__
fred.__proto__ = Object.prototype;
// Or equally:
Object.setPrototypeOf(fred, Object.prototype);
Object.prototype
페이지 의 경고는 더 자세히 설명됩니다.
객체의 [[Prototype]]을 변경하는 것은 최신 JavaScript 엔진이 속성 액세스를 최적화 하는 방식의 특성상 매우 느린 작업입니다.
그들은 단순히 이미 존재하는 객체 의 프로토 타입 체인 을 변경하면 최적화가 중단 된다고 말합니다 . 대신 .NET을 통해 다른 프로토 타입 체인으로 새 개체를 만들어야합니다 Object.create()
.
명시적인 참조는 찾을 수 없었지만 V8의 히든 클래스 가 어떻게 구현 되는지 고려하면 여기서 무슨 일이 벌어 질지 알 수 있습니다. 객체의 프로토 타입 체인을 변경하면 내부 유형이 변경됩니다. 속성을 추가 할 때처럼 단순히 하위 클래스가되는 것이 아니라 완전히 스왑됩니다. 이는 모든 속성 조회 최적화가 플러시되고 미리 컴파일 된 코드를 폐기해야 함을 의미합니다. 또는 단순히 최적화되지 않은 코드로 대체됩니다.
몇 가지 주목할만한 인용문 :
-
쓰기 가능한 __proto__는 구현하기가 매우 어렵고 (주기 확인을 위해 직렬화해야 함) 모든 종류의 유형 혼동 위험을 생성합니다.
-
스크립트가 거의 모든 객체의 프로토 타입을 변경하도록 허용하면 스크립트의 동작에 대해 추론하기가 더 어려워지고 VM, JIT 및 분석 구현이 더 복잡하고 버그가 많아집니다. 유형 추론은 변경 가능한 __proto__로 인해 몇 가지 버그가 있었으며이 기능으로 인해 여러 가지 바람직한 불변성을 유지할 수 없습니다 (예 : '유형 집합에는 var / property에 대해 실현할 수있는 모든 가능한 유형 개체를 포함합니다'및 'JSFunctions에는 함수이기도 한 유형이 있습니다' ).
Jeff Walden은 다음과 같이 말했습니다 .
생성 후 프로토 타입 돌연변이, 불규칙한 성능 불안정화, 프록시 및 [[SetInheritance]]에 미치는 영향
Erik Corry (Google)는 다음과 같이 말했습니다 .
나는 proto를 덮어 쓸 수 없도록 만들어서 큰 성능 향상을 기대하지 않습니다. 최적화되지 않은 코드에서는 프로토 타입 객체 (아이덴티티 아님)가 변경된 경우 프로토 타입 체인을 확인해야합니다. 최적화 된 코드의 경우 누군가 proto에 작성하면 최적화되지 않은 코드로 대체 할 수 있습니다. 따라서 적어도 V8-Crankshaft에서는 그다지 큰 차이가 없습니다.
-
__proto__를 설정하면 해당 객체에 대해 Ion의 향후 최적화를 위해 가질 수있는 기회를 망칠뿐만 아니라 엔진이 다른 모든 유형 추론 (함수 반환 값에 대한 정보, 또는 속성 값, 아마도)이 객체에 대해 알고 있다고 생각하고 많은 가정을하지 말라고 지시하는 것입니다. 이는 추가 최적화 해제 및 기존 jitcode의 무효화를 포함합니다.
실행 중에 객체의 프로토 타입을 변경하는 것은 정말 끔찍한 망치이며, 우리가 잘못되지 않도록해야하는 유일한 방법은 안전하게 플레이하는 것이지만 안전은 느립니다.
__proto__
/ setPrototypeOf
객체 프로토 타입에 할당하는 것과 동일하지 않습니다. 예를 들어 멤버가 할당 된 함수 / 객체가있는 경우 :
function Constructor(){
if (!(this instanceof Constructor)){
return new Constructor();
}
}
Constructor.data = 1;
Constructor.staticMember = function(){
return this.data;
}
Constructor.prototype.instanceMember = function(){
return this.constructor.data;
}
Constructor.prototype.constructor = Constructor;
// By doing the following, you are almost doing the same as assigning to
// __proto__, but actually not the same :P
var newObj = Object.create(Constructor);// BUT newObj is now an object and not a
// function like !!!Constructor!!!
// (typeof newObj === 'object' !== typeof Constructor === 'function'), and you
// lost the ability to instantiate it, "new newObj" returns not a constructor,
// you have .prototype but can't use it.
newObj = Object.create(Constructor.prototype);
// now you have access to newObj.instanceMember
// but staticMember is not available. newObj instanceof Constructor is true
// we can use a function like the original constructor to retain
// functionality, like self invoking it newObj(), accessing static
// members, etc, which isn't possible with Object.create
var newObj = function(){
if (!(this instanceof newObj)){
return new newObj();
}
};
newObj.__proto__ = Constructor;
newObj.prototype.__proto__ = Constructor.prototype;
newObj.data = 2;
(new newObj()).instanceMember(); //2
newObj().instanceMember(); // 2
newObj.staticMember(); // 2
newObj() instanceof Constructor; // is true
Constructor.staticMember(); // 1
Everybody seem to be focusing only on the prototype, and forget that functions can have members assigned to it and instantiated after mutation. There's currently no other way of doing this without using __proto__
/setPrototypeOf
. Barely anyone use a constructor without the ability to inherit from a parent constructor function, and Object.create
fails to serve.
And plus, that's two Object.create
calls, which at the present moment, is ungodly slow in V8 (both browser and Node), which makes __proto__
a more viable choice
Yes .prototype= is just as bad, hence the wording "no matter how it is accomplished". prototype is a pseudo object for extending the functionality at the class level. Its dynamic nature slows down script execution. Adding a function on the instance level, on the other hand, incurs far less overhead.
Here is a benchmark using node v6.11.1
NormalClass: A normal class, with the prototype non edited
PrototypeEdited: A class with the prototype edited (the test()
function is added)
PrototypeReference: A class with the added prototype function test()
who referer to an external variable
Results :
NormalClass x 71,743,432 ops/sec ±2.28% (75 runs sampled)
PrototypeEdited x 73,433,637 ops/sec ±1.44% (75 runs sampled)
PrototypeReference x 71,337,583 ops/sec ±1.91% (74 runs sampled)
As you can see, the prototype edited class is a way faster than the normal class. The prototype who has a variable which refer to an external one is the slowest, but that's an interesting way to edit prototypes with already instantied variable
Source :
const Benchmark = require('benchmark')
class NormalClass {
constructor () {
this.cat = 0
}
test () {
this.cat = 1
}
}
class PrototypeEdited {
constructor () {
this.cat = 0
}
}
PrototypeEdited.prototype.test = function () {
this.cat = 0
}
class PrototypeReference {
constructor () {
this.cat = 0
}
}
var catRef = 5
PrototypeReference.prototype.test = function () {
this.cat = catRef
}
function normalClass () {
var tmp = new NormalClass()
tmp.test()
}
function prototypeEdited () {
var tmp = new PrototypeEdited()
tmp.test()
}
function prototypeReference () {
var tmp = new PrototypeReference()
tmp.test()
}
var suite = new Benchmark.Suite()
suite.add('NormalClass', normalClass)
.add('PrototypeEdited', prototypeEdited)
.add('PrototypeReference', prototypeReference)
.on('cycle', function (event) {
console.log(String(event.target))
})
.run()
ReferenceURL : https://stackoverflow.com/questions/23807805/why-is-mutating-the-prototype-of-an-object-bad-for-performance
'programing' 카테고리의 다른 글
Android NumberPicker : XML에서 최소, 최대, 기본값 설정 (0) | 2021.01.15 |
---|---|
Play 스토어를 열지 않고 프로그래밍 방식으로 애플리케이션을 설치하는 방법 (Google 드라이브처럼) (0) | 2021.01.15 |
Java 8의 parallelStream에는 몇 개의 스레드가 생성됩니까? (0) | 2021.01.15 |
POSTMAN과 CORS (0) | 2021.01.15 |
백엔드 용 Webpack? (0) | 2021.01.15 |