development

자바 스크립트 컬렉션

big-blog 2020. 12. 27. 20:40
반응형

자바 스크립트 컬렉션


noobie 질문 죄송합니다. 다음의 차이점이 무엇인지 설명해 주시겠습니까?

1. var a = [];
   a['b'] = 1;

2. var a = {};
   a['b'] = 1;

인터넷에서 기사를 찾을 수 없어서 여기에 썼습니다.


리터럴

[]{}어레이와 물체라고 각각 리터럴.

var x = [] 약자 var x = new Array();

그리고 var y = {}줄여서var y = new Object();

배열

배열은 길이 속성이있는 구조입니다. 숫자 인덱스를 통해 값에 액세스 할 수 있습니다.

var x = [] or var x = new Array();
x[0] = 'b';
x[1] = 'c';

모든 속성을 나열하려면 다음을 수행하십시오.

for(var i = 0; i < x.length; i++)
console.log(x[i]);// numeric index based access.

성능 트릭과 문제

1. 길이 속성 내부 캐싱

표준 배열 반복 :

for (var i = 0; i < arr.length; i++) {
    // do stuff
};

거의 알려지지 않은 사실 : 위의 시나리오에서 arr.length속성은 for 루프의 모든 단계에서 읽 힙니다. 거기에서 호출하는 모든 함수와 마찬가지로 :

for (var i = 0; i < getStopIndex(); i++) {
     // do stuff
};

아무 이유없이 성능이 저하됩니다. 구조를위한 내부 캐싱 :

for (var i = 0, len = arr.length; i < len; i++) {
     // enter code here
};

증거가 있습니다.

2. 생성자에 배열 길이를 지정하지 마십시오.

// doing this:
var a = new Array(100);
// is very pointless in JS. It will result in an array with 100 undefined values.

// not even this:
var a = new Array();
// is the best way.

var a = [];
// using the array literal is the fastest and easiest way to do things.

어레이 정의에 대한 테스트 사례는 여기에서 확인할 수 있습니다 .

3. Array.prototype.push (arr.push) 사용을 피하십시오.

대규모 컬렉션을 처리하는 경우 직접 할당이 Array.prototype.push();방법을 사용하는 것보다 빠릅니다 .

myArray[i] = 0;myArray.push(0);jsPerf.com 테스트 케이스 에 따르면 보다 빠릅니다 .

4. 연관 할당에 배열을 사용하는 것은 잘못되었습니다.

작동하는 유일한 이유 는 JS 언어의 핵심 내에서 클래스를 Array확장 하기 때문 Object입니다. 예를 들어 Date();또는 RegEx();객체를 사용할 수도 있습니다 . 그것은 차이를 만들지 않을 것입니다. 항상 Objects 와 함께 x['property'] = someValue 사용해야합니다 .

배열에는 숫자 인덱스 만 있어야합니다. SEE THIS , 구글 JS 개발 가이드 라인! for (x in arr)루프 또는 arr['key'] = 5;.

이것은 쉽게 백업 할 수 있습니다 . 여기 에서 예를보십시오.

var x = [];
console.log(x.prototype.toString.call);

 다음을 출력합니다. [object Array]

이것은 핵심 언어의 '클래스'상속 패턴을 보여줍니다.  

var x = new String();
console.log(x.prototype.toString.call);

출력 [object String]됩니다.

5. 어레이에서 최소 및 최대 가져 오기.

약간 알려졌지만 정말 강력한 트릭 :

function arrayMax(arr) {
    return Math.max.apply(Math, arr);
};

, 각각 :

function arrayMin(arr) {
    return Math.min.apply(Math, arr);
};

사물

개체를 사용하면 다음 작업 만 수행 할 수 있습니다.

var y = {} 또는 var y = new Object();

y['first'] = 'firstValue'y.first = 'firstValue'배열로 할 수없는과 동일합니다. 객체는 String와의 연관 액세스를 위해 설계되었습니다 .

그리고 반복은 다음과 같습니다.

for (var property in y) {
    if (y.hasOwnProperty(property)) {
        console.log(y.property);
    };
};

성능 트릭과 문제

1. 개체에 속성이 있는지 확인합니다.

대부분의 사람들은 Object.prototype.hasOwnProperty. 불행히도 이는 종종 잘못된 결과를 제공하여 예기치 않은 버그로 이어집니다.

여기에 좋은 방법이 있습니다.

function containsKey(obj, key) {
    return typeof obj[key] !== 'undefined';
};

2. 스위치 문 바꾸기.

간단하지만 효율적인 JS 트릭 중 하나는 switch교체입니다.

switch (someVar) {
    case 'a':
        doSomething();
        break;
    case 'b':
        doSomethingElse();
        break;
    default:
        doMagic();
        break;
};

대부분의 JS 엔진에서 위의 내용은 매우 느립니다. 세 가지 가능한 결과를 볼 때 차이가 없지만 수십 또는 수백이 있다면 어떨까요?

위의 내용은 쉽게 개체로 대체 할 수 있습니다. 후행을 추가하지 마십시오 (). 이것은 함수를 실행하는 것이 아니라 단순히 참조를 저장하는 것입니다.

var cases = {
    'a': doSomething,
    'b': doSomethingElse,
    'c': doMagic
};

대신 switch:

var x = ???;
if (containsKey(cases, x)) {
    cases[x]();
} else {
    console.log("I don't know what to do!");
};

3. 딥 클로닝이 쉬워졌습니다.

function cloneObject(obj) {
   var tmp = {};
   for (var key in obj) {
       tmp[key] = fastDeepClone(obj[key];
   };
   return tmp;
}

function cloneArr(arr) {
   var tmp = [];
   for (var i = 0, len = arr.length; i < len; i++) {
     tmp[i] = fastDeepClone(arr[i]);
   }
   return tmp;
}


function deepClone(obj) {
   return JSON.parse(JSON.stringify(obj));
};

function isArray(obj) {
   return obj instanceof Array;
}

function isObject(obj) {
  var type = typeof obj;
  return type === 'object' && obj !== null || type === 'function';
}

function fastDeepClone(obj) {
  if (isArray(obj)) {
    return cloneArr(obj);
  } else if (isObject(obj)) {
    return cloneObject(obj);
  } else {
    return obj;
  };
};

여기 에 작동중인 딥 클론 기능이 있습니다.

자동 복싱

동적으로 유형이 지정되는 언어 인 JavaScript는 기본 객체 유형 측면에서 제한됩니다.

  • 목적
  • 정렬
  • 번호
  • 부울
  • 데이트
  • RegEx
  • 오류

Null은 유형이 아니라 typeof null객체입니다.

캐치는 무엇입니까? 원시 객체와 원시 객체가 아닌 객체 사이에는 강한 차이가 있습니다.

var s = "str";
var s2 = new String("str");

그들은 똑같은 일을하고, 당신은 s에서 모든 문자열 메소드를 호출 할 수 있습니다 s2. 아직:

type of s == "string"; // raw data type
type of s2  == "object" // auto-boxed to non-primitive wrapper type
s2.prototype.toString.call == "[object String]";

JS에서 모든 것이 object. 정말 쉬운 실수이지만 정확히 사실은 아닙니다.

실제로 두 가지 유형, 기본 및 객체 s.indexOf("c")가 있으며을 호출 하면 JS 엔진이 자동으로 s기본이 아닌 래퍼 유형 으로 변환 됩니다.이 경우 object String모든 메서드는 String.prototype.

이것을라고 auto-boxing합니다. Object.prototype.valueOf(obj)방법은 캐스트를 프리미티브에서 비 프리미티브로 강제하는 방법입니다. 이 같은 동작의 IT 자신의 프리미티브의 많은 자바 소개하고, 특히 쌍 같은 언어 : int- 정수 double- 더블, float- 플로트 등

왜 관심을 가져야합니까?

단순한:

function isString(obj) {
   return typeof obj === "string";
}
isString(s); // true
isString(s2); // false

따라서 당신 s2과 함께 생성 되었다면 var s2 = new String("test")상상할 수 없을 정도로 간단한 유형 검사에서도 거짓 부정이 발생합니다. 더 복잡한 개체는 또한 자체적으로 상당한 성능 저하를 가져옵니다.

일부 사람들이 말하듯이 마이크로 최적화이지만 문자열 초기화와 같은 매우 간단한 작업에서도 결과는 정말 놀랍습니다. 성능 측면에서 다음 두 가지를 비교해 보겠습니다.

var s1 = "this_is_a_test" 

var s2 = new String("this_is_a_test")

아마도 전반적으로 일치하는 성능을 기대할 것입니다. 그러나 놀랍게도 여기서new String 입증 된 바와 같이 사용하는 후자의 진술 은 첫 번째 진술 보다 92 % 더 느립니다 .

기능

1. 기본 매개 변수

||오퍼레이터는 디폴트의 가장 간단한 방법이다. 왜 작동합니까? 진실하고 거짓된 가치 때문에.

논리 상태를 평가할 때 undefinednull값에 오토 캐스트한다 false.

간단한 예 (코드 HERE ) :

function test(x) {
   var param = x || 5;
   // do stuff with x
};

2. OO JS

이해해야 할 가장 중요한 것은 JavaScript this객체가 불변이 아니라는 것입니다. 매우 쉽게 변경할 수있는 참조 일뿐입니다.

OO JS에서 우리 new는 JS 클래스의 모든 멤버에서 암시 적 범위를 보장하기 위해 키워드에 의존합니다 . 그럼에도 불구하고, 당신은 쉽게, 범위를 변경을 통해 수 Function.prototype.callFunction.prototype.apply.

또 다른 매우 중요한 것은 Object.prototype. 객체 프로토 타입에 중첩 된 비 기본 값은 공유되지만 기본 값은 공유되지 않습니다.

여기 에 예제 코드가 있습니다 .

간단한 클래스 정의 :

function Size(width, height) {
    this.width = width;
    this.height = height;
};

두 개의 멤버 this.widththis.height.

클래스 정의 this에서 앞에있는 것이 무엇이든 Size의 모든 인스턴스에 대한 새 참조를 만듭니다.

클래스에 메소드 추가 및 "클로저"패턴 및 기타 "팬시 이름 패턴"이 순수한 허구 인 이유

이것은 아마도 가장 악의적 인 JavaScript 안티 패턴이 발견되는 곳일 것입니다.

Size두 가지 방법으로 클래스에 메서드를 추가 할 수 있습니다 .

Size.prototype.area = function() {
   return this.width * this.height;
};

또는:

function Size2(width, height) {
   this.width = width;
   this.height = height;
   this.area = function() {
      return this.width * this.height;
   }
}

var s = new Size(5, 10);
var s2 = new Size2(5, 10);



var s3 = new Size2(5, 10);
var s4 = new Size(5, 10);
 // Looks identical, but lets use the reference equality operator to test things:
s2.area === s3.area // false
s.area === s4.area // true

area메서드는 Size2모든 인스턴스에 대해 생성됩니다. 이것은 완전히 쓸모없고 느리고 훨씬 느립니다. 정확히 89 %. 여기를 보세요 .

위의 설명은 알려진 모든 "멋진 이름 패턴"의 약 99 %에 대해 유효합니다. JS에서 가장 중요한 한 가지를 기억하세요.이 모든 것은 허구 일뿐입니다.

만들 수있는 강력한 아키텍처 주장이 있으며, 대부분 데이터 캡슐화 및 클로저 사용을 중심으로 진행됩니다.

안타깝게도 이러한 것들은 JavaScript에서 절대적으로 가치가 없으며 성능 손실은 그만한 가치가 없습니다. 우리는 90 % 이상에 대해 이야기하고 있으며 무시할 수없는 수준입니다.

3. 제한

prototype정의는 클래스의 모든 인스턴스간에 공유 되기 때문에 기본이 아닌 설정 개체를 거기에 둘 수 없습니다.

Size.prototype.settings = {};

왜? size.settings모든 단일 인스턴스에 대해 동일합니다. 그래서 프리미티브는 무엇입니까?

Size.prototype.x = 5; // won't be shared, because it's a primitive.
// see auto-boxing above for primitive vs non-primitive
// if you come from the Java world, it's the same as int and Integer.

요점:

일반적인 JS 사람은 다음과 같은 방식으로 JS를 작성합니다.

var x = {
    doStuff: function(x) {
    },
    doMoreStuff: 5,
    someConstant: 10
}

어느 벌금 (미세 = 불량, 코드를 유지하기 위해 하드)만큼 당신이 이해가가이다 Singleton오브젝트, 그 함수는 참조하지 않고 전역 범위에서 사용되어야 this그 내부.

그러나 절대적으로 끔찍한 코드가됩니다.

var x = {
   width: 10,
   height: 5
}
var y = {
   width: 15,
   height: 10
}

당신은 도망 쳤을 수도 있습니다 : var x = new Size(10, 5); var y = new Size(15, 5);.

입력하는 데 시간이 오래 걸리므로 매번 같은 내용을 입력해야합니다. 그리고 다시, 그것은 훨씬 느립니다. 여기를 보세요 .

전체적으로 열악한 표준

이것은 거의 모든 곳에서 볼 수 있습니다.

   function() {
      // some computation
      var x = 10 / 2;
      var y = 5;
      return {
         width: x,
         height: y
      }
    }

다시 대안으로 :

function() {
    var x = 10 / 2;
    var y = 5;
    return new Size(10, 5);
};

요점 : 적절한 곳에서 수업을 사용하세요 !!

왜? 예제 1은 93 % 더 느 립니다. 여기를 보세요 . 여기에있는 예제는 사소하지만 JS, OO에서 무시되는 것을 보여줍니다.

JS에 수업이 없다고 생각하는 사람을 고용하지 않고 "객체 지향"JS에 대해 말하는 채용 담당자로부터 일자리를 얻는 것은 확실한 경험 규칙입니다.

폐쇄

많은 사람들이 데이터 캡슐화 감각을 제공하기 때문에 위의 것보다 선호합니다. 90 %의 급격한 성능 저하 외에도 간과하기 쉬운 것이 있습니다. 메모리 누수.

function Thing(someParam) {
   this.someFn = function() {
     return someParam;
   }
}

에 대한 클로저를 생성했습니다 someParam. 왜 이것이 나쁜가요? 첫째, 클래스 메서드를 인스턴스 속성으로 정의해야하므로 성능이 크게 저하됩니다.

둘째, 클로저가 역 참조되지 않기 때문에 메모리를 소모합니다. 증거를 찾으 십시오 . 물론 가짜 데이터 캡슐화가 발생하지만 90 % 성능 저하로 메모리를 3 배 사용합니다.

또는 @private밑줄 함수 이름을 추가 하고 얻을 수 있습니다 .

클로저를 생성하는 다른 매우 일반적인 방법 :

function bindSomething(param) {
   someDomElement.addEventListener("click", function() {
     if (param) //do something
     else // do something else
   }, false);
}

param이제 마감입니다! 어떻게 제거합니까? 다양한 트릭이 있으며 일부는 여기 에서 찾을 수 있습니다 . 더 엄격 하긴하지만 가장 좋은 방법은 익명 함수를 함께 사용하지 않는 것이지만이를 위해서는 이벤트 콜백의 범위를 지정하는 방법이 필요합니다.

이러한 메커니즘은 내가 아는 한 Google Closure에서만 사용할 수 있습니다.

싱글 톤 패턴

좋아요, 싱글 톤은 어떻게하나요? 무작위 참조를 저장하고 싶지 않습니다. Google Closure의 base.js 에서 뻔뻔스럽게 도난당한 멋진 아이디어 가 있습니다.

/**
 * Adds a {@code getInstance} static method that always return the same instance
 * object.
 * @param {!Function} ctor The constructor for the class to add the static
 *     method to.
 */
function addSingletonGetter(ctor) {
  ctor.getInstance = function() {
    if (ctor.instance_) {
      return ctor.instance_;
    }
    return ctor.instance_ = new ctor;
  };
};

Java와 비슷하지만 간단하고 강력한 트릭입니다. 이제 다음을 수행 할 수 있습니다.

project.some.namespace.StateManager = function() {
   this.x_ = 5;
};
project.some.namespace.prototype.getX = function() { return x; }
addSingletonGetter(project.some.namespace.StateManager);

이것이 어떻게 유용합니까? 단순한. 다른 모든 파일에서는를 참조해야 할 때마다 다음 project.some.namespace.StateManager과 같이 작성할 수 있습니다 project.some.namespace.StateManager.getInstance().. 이것은보기보다 더 굉장합니다.

클래스 정의 (상속, 상태 저장 멤버 등)의 이점을 사용 하고 전역 네임 스페이스 오염시키지 않고 전역 상태를 가질 수 있습니다 .

단일 인스턴스 패턴

이제 이렇게하고 싶을 수 있습니다.

function Thing() {
   this.someMethod = function() {..}
}
// and then use it like this:
Thing.someMethod();

그것은 JavaScript에서 또 다른 큰 금지 사항입니다. this객체는 new키워드가 사용될 때만 변경 불가능하다는 것을 기억하십시오 . 위 코드의 마법은 흥미 롭습니다. this실제로 전역 범위이므로 의미없이 전역 개체에 메서드를 추가합니다. 그리고 당신은 그것을 짐작했습니다. 그런 것들은 결코 가비지 수집되지 않습니다.

JavaScript에 다른 것을 사용하도록 지시하는 것은 없습니다. function자체로는 범위가 없습니다. static속성으로 무엇을하는지 정말 조심하십시오 . 내가 읽은 인용구를 재현하기 위해 JavaScript 전역 객체는 공중 화장실과 같습니다. 때로는 거기에 갈 수밖에 없지만 가능한 한 표면과의 접촉을 최소화하려고 노력하십시오.

위의 Singleton패턴을 고수 하거나 네임 스페이스 아래에 중첩 된 설정 개체를 사용하십시오.

JavaScript의 가비지 수집

JavaScript는 가비지 수집 언어이지만 JavaScript GC는 종종 잘 이해되지 않습니다. 요점은 다시 속도입니다. 이것은 아마도 모두 너무 익숙 할 것입니다.

// This is the top of a JavaScript file.
var a = 5;
var b = 20;
var x = {};//blabla

// more code
function someFn() {..}

그것은 나쁘고 성능이 떨어지는 코드입니다. 그 이유는 간단합니다. JS는 변수를 가비지 수집하고 해당 변수의 범위가 해제 될 때만 보유하고있는 힙 메모리를 해제합니다. 예를 들어 메모리의 어느 곳에도 참조가 없습니다.

예를 들면 :

function test(someArgs) {
   var someMoreStuf = // a very big complex object;
}
test();

세개:

  • 함수 인수는 로컬 정의로 변환됩니다.
  • 내부 선언은 게양 됩니다.
  • 함수 실행이 완료되면 내부 변수에 할당 된 모든 힙 메모리가 해제됩니다.

Why? Because they no longer belong to the "current" scope. They are created, used, and destroyed. There are no closures either, so all the memory you've used is freed up through garbage collection.

For that reason, you should never, your JS files should never look like this, as global scope will just keep polluting memory.

var x = 5;
var y = {..}; //etc;

Alright, now what?

Namespaces.

JS doesn't have namespaces per say, so this isn't exactly a Java equivalent, yet from a codebase administration perspective you get what you want.

var myProject = {};
myProject.settings = {};
myProject.controllers = {};
myProject.controlls.MainController = function() {
    // some class definition here
}

Beautiful. One global variable. Proper project structure. With a build phase, you can split your project across files, and get a proper dev environment.

There's no limit to what you can achieve from here.

Count your libraries

Having had the pleasure of working on countless codebases, the last and most important argument is to be very mindful of your code dependencies. I've seen programmers casually adding jQuery into the mix of the stack for a simple animation effect and so forth.

Dependency and package management is something the JavaScript world hadn't addresses for the longest time, until the creation of tools like Bower. Browsers are still somewhat slow, and even when they're fast, internet connections are slow.

In the world of Google for instance, they go through the lengths of writing entire compilers just to save bytes, and that approach is in many ways the right mentality to have in web programming. And I uphold Google in very high regard as their JS library powers apps like Google Maps, which are not only insanely complex, but also work everywhere.

Arguably JavaScript has an immense variety of tools available, given its popularity, accessibility, and to some extent very low quality bar the ecosystem as a whole is willing to accept.

For Hacker News subscribers, a day doesn't go by without a new JS library out there, and they are certainly useful but one cannot ignore the fact that many of them re-implement the exact same concerns without any strong notion of novelty or any killer ideas and improvements.

It's a strong rule of thumb to resist the urge of mixing in all the new toys before they have the time to prove their novelty and usefulness to the entire ecosystem and to strongly distinguish between Sunday coding fun and production deployments.

If your <head></head> tag is longer than this post, you're doing it all wrong.

Testing your knowledge of JavaScript

A few "perfectionist" level tests:


A collection of objects? Use this notation (JavaScript arrays):

var collection = [ {name:"object 1"} , {name:"object 2"} , {name:"object 3"} ];

To put a new element into your collection:

collection.push( {name:"object 4"} );

In JavaScript all objects are associative arrays. In first case you create an array in the second case you created an empty object which is array too :).

So in JS you can work with any object as with array:

var a = {};
a["temp"] = "test";

And as object:

var a = {};
a.temp = "test";

I would use an array of objects:

collection = [ 
    { "key":"first key", "value":"first value" }, 
    { "key":"second key", "value":"second value" } 
];

etc


1) Is an Array 2) Is an Object

With Array all is usual as in other languages

With Object also. - You can get value a.b == 1 - But in JS you can also get value with such syntax a["b"] == 1

  • This could be usefull when key look like something this "some key", in this case you can't use "chaining"
  • also this usefull if key is the variable

you can write like this

    function some(f){
var Object = {name: "Boo", age: "foo"}, key;
if(f == true){
   key = "name";
}else{
   key = "age";
}
 return Object[key];
}

but I want to use it as collection, which I have to choose?

This depends of what data you want to store

ReferenceURL : https://stackoverflow.com/questions/12973706/javascript-collection

반응형