JavaScript 디자인 모델 (2) - 생 성 형 디자인 모델

16506 단어
단순 공장 모드
말 그대로 공장 대상 을 통 해 제품 대상 류 의 인 스 턴 스 를 만 드 는 것 은 같은 종류 나 기능 이 비슷 한 대상 을 만 드 는 것 이다.예 를 들 면:
  • 방법 1: 페이지 에서 자주 사용 하 는 팝 업 창 은 경고 상자, 알림 상자, 확인 상자 등 으로 나 뉘 는데 그 중에서 비슷 한 부분 을 추출 할 수 있 습 니 다. 예 를 들 어 문자, 탄 상자 전시 함수 등 코드 는 다음 과 같 습 니 다.
  • function createPop (type, text) {
      //       ,           
      var 0 = new Object();
      o.content = text;
      o.show = function () {
        //       
      }
      if (type === 'alert') {
        //        
      }
      if (type === 'prompt') {
        //        
      }
      if (type === 'confirm') {
        //        
      }
      
      //      
      return o;
    }
    
    //      
    var userNameAlert = createPop('alert', '      16           ');
    
  • 방법 2: 공 을 만 드 는 방법
  • //     
    var Basketball = function () {
      this.intro = '       ';
    }
    
    Basketball.prototype = {
      getMember: function () {
        console.log('      5   ');
      },
      getBallSize: function () {
        console.log('    ');
      }
    }
    
    //     
    var Football = function () {
      this.intro = '           ';
    }
    
    Football.prototype = {
      getMember: function () {
        console.log('      11   ');
      },
      getBallSize: function () {
        console.log('    ');
      }
    }
    
    //     
    var Tennis = function () {
      this.intro = '          ';
    }
    
    Tennis.prototype = {
      getMember: function () {
        console.log('      1   ');
      },
      getBallSize: function () {
        console.log('    ');
      }
    }
    
    //     
    var SportFactory = function (name) {
      switch (name) {
       case 'NBA': 
        return new Basketball();
       case 'wordCup': 
        return new Football();
       case 'FrenchOpen': 
        return new Tennis();
      }
    }
    

    결론: 첫 번 째 방법 은 새로운 대상 을 만 든 다음 에 포장 을 통 해 그의 속성 과 기능 을 강화 하 는 것 이다. 만 든 대상 은 새로운 개 체 를 잃 어 버 리 고 그들의 방법 은 공유 할 수 없다.두 번 째 방법 은 클래스 실례 화 대상 을 통 해 만 든 것 이다. 이런 방법 에서 만 든 대상 이 같은 부류 에서 계승 된다 면 그들의 원형 적 인 방법 은 공용 할 수 있다.실제 상황 에 따라 선택 할 수 있다.단점:
  • 비슷 한 수 요 를 받 으 면 공장 방법 도 수정 하고 기 류 를 만들어 야 하 며 두 가지 측면 에서 모두 변경 해 야 한다.
  • 여러 종류의 대상 을 만 드 는 데 적합 하지 않 습 니 다
  • 공장 모드
    특징: 제품 류 에 대한 추상 을 통 해 여러 가지 제품 의 인 스 턴 스 를 만 드 는 데 사용 합 니 다.실제 창설 대상 의 작업 을 하위 클래스 로 미 루 면 핵심 클래스 는 추상 클래스 가 된다.사용자 와 대상 류 간 의 결합 을 피 할 수 있 고 사용 자 는 이 대상 의 구체 적 인 유형 을 만 드 는 데 관심 을 가 질 필요 가 없 으 며 공장 방법 만 사용 하면 된다.우선, 우 리 는 안전 모델 을 알 아 보 자.
    var Demo = function () {
      if (!(this instanceof Demo)) {
        return new Demo ();
      }
    }
    
    Demo.prototype = {
      show: function () {
        console.log('    !');
      }
    }
    
    var d = new Demo();
    d.show(); //     !
    var d = Demo();
    d.show(); //     
    

    여기 서 우 리 는 위 에서 공장 방법 에 현재 대상 이 클래스 인지 아 닌 지 판단 하 는 것 을 보 았 습 니 다. 클래스 가 아니라면 이때 this 가 Window 를 가리 키 면 new 키 워드 를 통 해 새로운 대상 을 만 들 고 되 돌려 줍 니 다.그 대상 에 게 직접 돌아 가 는 것 이 라면.var d = Demo () 를 효과적으로 피 할 수 있 습 니 다.d. show () 호출 시의 오류 가 발생 했 습 니 다.
    //       
    var Factory = function (type, content) {
      if (this instanceof Factory) {
        var s = new this[type](content);
        return s;
      } else {
        return new Factory(type, content);
      }
    }
     
    //                     
    Factory.prototype = {
      Java: function (content) {
        // ......
      },
      JavaScript: function (content) {
        // ......
      },
      UI: function (content) {
        this.content = content;
        (function (content) {
          var div = document.createElement('div');
          div.innerHTML = content;
          div.style.border = '1px solid red';
          document.getElementById('container').appendChild(div);
        })(content);
      },
      php: function (content) {
        // ......
      }
    }  
    

    여기 서 우 리 는 나중에 비슷 한 수 요 를 받 으 면 방법 을 추가 하고 Factory 공장 방법 을 직접 수정 하면 된다 는 것 을 알 수 있다. 더 이상 기 류 를 다시 만 들 필요 가 없다.
    추상 공장 모드
    클래스 의 공장 추상 화 를 통 해 그 업 무 를 제품 클래스 의 창설 에 사용 하 는 것 이지, 특정한 제품 의 창설 을 책임 지 는 실례 가 아니다.추상 류: 추상 류 는 성명 이지 만 사용 할 수 없 는 종류 로 사용 할 때 잘못 보고 합 니 다.즉, 클래스 를 만 드 는 것 입 니 다. 생 성 할 때 속성 이 없고 원형 prototype 에서 인터넷 방법 을 사용 할 수 없습니다. 그렇지 않 으 면 오 류 를 보고 할 수 있 지만 계승 에 있어 서 유용 합 니 다. 클래스 를 정의 하고 필요 한 방법 을 정 의 했 기 때 문 입 니 다. 하위 클래스 에서 이 방법 을 다시 쓰 지 않 으 면 호출 할 때 부모 클래스 에서 이 방법 을 찾 아 오 류 를 보고 할 수 있 습 니 다.이 특징 은 우리 가 일부 대형 응용 에서 사용 할 수 있다. 즉, 자 류 는 일부 부 류 를 계승 하고 부 류 는 항상 필요 한 방법 을 정의 하지만 구체 적 으로 실현 되 지 않 는 다. 그러면 자 류 가 대상 을 만 들 면 이 대상 은 반드시 필요 한 방법 을 가 져 야 한다. 그러나 필요 한 방법 이 부 류 에서 계승 되 어 재 작성 되 지 않 고 실현 되 지 않 는 다.원형 체인 의 방법 을 찾 아 오 류 를 보고 할 수 있다.이 는 사실상 재 작성 자 류 를 잊 는 것 에 대한 힌트 다.즉, 추상 류 의 역할 은 하나의 제품 클 러 스 터 를 정의 하고 필수 적 인 방법 을 설명 하 는 것 이다. 만약 에 하위 클래스 에서 다시 쓰 지 않 으 면 잘못 보고 할 것 이다.주: 자 바스 크 립 트 에 보존 자 abstract 가 존재 하기 때문에 추상 클래스 를 직접 만 들 수 는 없 지만, 클래스 의 방법 에서 수 동 으로 오 류 를 던 져 추상 클래스 를 모 의 할 수 있 습 니 다.예 를 들 면:
    //      ,                 
    var Car =- function () {};
    Car.prototype = {
      getPrice: function () {
        return new Error('        ');
      },
      getSpeed: function () {
        return new Error('        ');
      }
    }
    

    추상 류 는 일부 기능 을 외 현적 으로 정의 할 뿐 구체 적 으로 실현 되 지 않 았 고 한 대상 은 완전한 기능 을 가 져 야 하기 때문에 우 리 는 이 를 통 해 진실 한 대상 을 만 들 수 없다.일반적으로 부모 클래스 로 하위 클래스 를 만 들 고 하위 클래스 를 통 해 실제 대상 을 만 들 수 있 습 니 다.구체 적 인 예 는 다음 과 같다.
    //       
    var VehicleFactory = function (subType, superType) {
      //                
      if (typeof VehicleFactory[superType] === 'function') {
         //    
         function F() {};
         //          
         F.prototype  = new VehicleFactory[superType]();
         //     constructor    
        subType.constructor = subType;
        //       “  ”
        subType.prototype = new F();
      } else {
        throw new Error('       ');
      }
    }
    
    //       
    VehicleFactory.Car = function () {
      this.type = 'car';
    }
    VehicleFactory.Car.prototype = {
      getPrice: function () {
        return new Error ('        ');
      },
      getSpeed: function () {
        return new Error ('        ');
      }
    }
    
    //       
    VehicleFactory.Bus = function () {
      this.type = 'bus';
    }
    VehicleFactory.Bus.prototype = {
      getPrice: function () {
        return new Error ('        ');
      },
      getPassengerNum: function () {
        return new Error ('        ');
      }
    }
    
    //      
    VehicleFactory.Truck = function () {
      this.type = 'truck';
    }
    VehicleFactory.Truck.prototype = {
      getPrice: function () {
        return new Error ('        ');
      },
      getTrainload: function () {
        return new Error ('        ');
      }
    }
    

    위의 예 에서 추상 류 의 존재 성에 대한 판단 을 추 가 했 습 니 다. 만약 에 존재 한다 면 자 류 는 아버지 류 를 계승 하 는 방법 을 보 았 습 니 다. 여기 서 우 리 는 아버지 류 를 계승 하 는 과정 에서 과도 류 의 원형 을 계승 할 때 여 기 는 아버지 류 의 원형 을 계승 하 는 것 이 아니 라 new 키 워드 를 통 해 아버지 류 의 인 스 턴 스 를 복제 하 는 것 입 니 다. 이렇게 하 는 것 은 과도 류 가 아버지 류 의 원형 방법 만 계승 하 는 것 이 아니 라부류 의 대상 속성 도 계승 합 니 다.추상 공장 에 추상 류 를 첨가 하 는 것 은 매우 특수 하 다.추상 적 인 공장 방법 은 실례 화 할 필요 가 없 기 때문에 한 부 만 필요 하기 때문에 추상 적 인 공장 에 유형의 속성 을 직접 추가 하면 된다. 위의 예 에서 점 문법 을 통 해 추상 적 인 공장 에 세 개의 자동차 클 러 스 터 추상 적 인 카, 버스, 트럭 을 추가 했다.사용 방법:
    //       
    var BMW = function (price, speed) {
      this.price = price;
      this.speed = speed;
    }
    
    //        Car      
    VehicleFactory (BMW, 'Car');
    BMW.prototype.getPrice = function () {
      return this.price;
    }
    BMW.prototype.getSpeed = function () {
      return this.speed;
    }
    
    //       
    var YUTONG = function (price, passenger) {
      this.price = price;
      this.passenger = passenger;
    }
    //        Bus      
    VehicleFatory(YUTONG, 'Bus');
    YUTONG.prototype.getPrice = function () {
      return this.price;
    }
    YUTONG.prototype.getPassengerNum = function () {
      return this.passenger;
    }
    
    //       
    var BenzTruck = function (price, trainLoad) {
      this.price = price;
      this.trainLoad= trainLoad;
    }
    //        Truck      
    VehicleFatory(BenzTruck, 'Truck');
    BenzTruck.prototype.getPrice = function () {
      return this.price;
    }
    BenzTruck.prototype.gettrainLoad = function () {
      return this.trainLoad;
    }
    

    하위 클래스 는 모두 실례 화 될 수 있다. 예 를 들 어:
    var truck = new BenzTruck(1000000, 1000);
    console.log(truck.getPrice()); // 1000000
    console.log(truck.type); // truck
    

    단점:
  • JavaScript 에 서 는 추상 화 된 생 성과 가상 방법 을 지원 하지 않 기 때문에 이런 모델 은 다른 대상 언어 에서 응용 되 는 것 처럼 광범 위 하지 못 합 니 다.
  • getPrice 와 같은 일반적인 방법 에 대해 서 는 재 활용 할 수 없습니다.

  • 건설 자 모드
    복잡 한 대상 의 구축 층 과 표현 층 을 서로 분리 시 키 고 똑 같은 구축 과정 은 서로 다른 표현 을 사용 할 수 있다.공장 모델 을 비교 하 는 것 은 주로 대상 의 인 스 턴 스 나 클 러 스 터 를 만 들 기 위해 서 입 니 다. 최종 적 으로 만 든 것 이 무엇 인지 에 관심 이 있 고 전체적인 생 성 과정 에 관심 이 없 으 며 결과 에 만 관심 이 있 습 니 다.건설 자 모델 의 최종 목적 도 생 성 대상 이지 만 그 는 생 성 과정 에 도 참 여 했 고 생 성 된 구체 적 인 세부 사항 에 도 참 여 했 으 며 생 성 된 대상 이 더욱 복잡 하여 보통 복합 대상 을 만 드 는 데 사용 된다.
    //      " "
    var Human = function (param) {
      //   
      this.skill = param && param.skill || '  ';
      //     
      this.hobby = param && param.hobby || '  ';
    }
    
    // " "     
    Human.prototype = {
      getSkill: function () {
        return this.skill;
      },
      getHobby: function () {
        return this.hobby;
      }
    }
    
    //       
    var Named = function (name) {
      var that = this;
      //    
      //               
      (function (name, that) {
        that.wholeName = name;
        if (name.indexOf(' ') > -1) {
          that.FirstName = name.slice(0, name.indexOf(' '));
          that.secondName = name.slice(name.indexOf(' '));
        }
      })(name, that);
    }
    
    //       
    var Work = function (work) {
      var that = this;
      //    
      //                          
      (function (work, that) {
        switch (work) {
          case 'code': 
            that.work = '   ';
            that.workDescript = '       ';
            break;
          case 'UI':
          case 'UE':
            that.work = '   ';
            that.workDescript = '        ';
            break;
          case 'teach': 
            that.work = '  ';
            that.workDescript = '        ';
            break;
          default: 
            that.work = work;
            that.workDescript = '   ,                 ';
            break;
        }
      })(work, that);
    }
    
    //        
    Work.prototype.changeWork = function (work) {
      this.work = work;
    }
    
    //         
    Work.prototype.changeDescript = function (sentence) {
      this.workDescript = sentence;
    }
    

    새로운 클래스 만 들 기:
    /****
    *       
    *    name:   (  )
    *   work:     
    **/
    var Person = function (name, work) {
      //          
      var _person = new Human();
       //            
      _person.name = new Named(name);
       //          
      _person.work= new Work(work);
      //           
      return _person;
    }
    

    계승 & 실례 화
    var person = new Person ('xiao ming', code);
    

    위의 예 에서 우 리 는 부분 적 인 대상 을 만 든 다음 에 이 를 전체적인 대상 으로 합성 할 수 있다 는 것 을 알 수 있다.알림: 이러한 생 성 모드 에서 우 리 는 생 성 대상 의 클래스 를 모듈 화 합 니 다. 이렇게 하면 생 성 된 클래스 의 모든 모듈 을 유연 하고 질 좋 은 운용 을 할 수 있 습 니 다.단점: 이런 방식 은 전체 대상 류 의 분할 에 대해 모 르 는 사이 에 구조의 복잡성 을 증가 시 켰 기 때문에 대상 의 입도 가 작 거나 모듈 간 의 재 활용 율 이 낮 고 변동 이 크 지 않 으 면 대상 전 체 를 만 드 는 것 이 좋다.
    원형 모드
    프로 토 타 입 대상 을 생 성 대상 의 클래스 로 가리 키 며 프로 토 타 입 대상 을 공유 하 는 방법 과 속성 입 니 다.이러한 계승 은 속성 과 방법 에 대한 공 유 를 바탕 으로 하 는 것 이지 속성 과 방법 에 대한 복사 가 아니다.다음은 라운드 맵 플러그 인 입 니 다. 원형 모드 로 작 성 된 예 입 니 다.
    var LoopImages = function (imgArr, container) {
      this.imagesArray = imgArr; //       
      this.container = container; //       
      this.createImage = function () {} //       
      this.changeImage = function () {} //        
    }
    

    계승 윤 방도 함수
    //       
    var SlideLoopImg = function (imgArr, container) {
      //            
      LoopImgages.call(this, imgArr, container);
      //              
      this.changeImage = function () {}
    }
    
    //      
    var FadeLoopImg = function (imgArr, container, arrow) {
      LoopImages.call(this, imgArr, container);
      //         
      this.arrow = arrow;
      this.changeImage = function () {}
    }
    

    구체 적 인 상황 에서 의 사용 은 실제 와 같이 점진 적 인 전환 그림 류 를 예 로 들 면
    var fadeImg = new FadeLoopImg([
        '01.jpg',
        '02.jpg',
        '03.jpg',
        '04.jpg'
      ], 'slide', [
        'left.jpg',
        'right.jpg'
      ]);
    
    FadeImg.changeImage(); // FadeLoopImg changeImage function
    

    단점: 위 에서 말 한 예 에서 우 리 는 기본 적 인 Loop Images 를 보 았 습 니 다. 기본 적 인 클래스 로 서 하위 클래스 에 계승 되 어야 합 니 다. 그러면 이때 속성 과 방법 을 기본 적 인 구조 함수 에 쓰 면 부모 류 의 구조 함수 에서 만 들 때 시간 이 오래 걸 리 는 논리 가 많 거나 초기 화 할 때마다 반복 적 인 것 을 해 야 하기 때문에 소모 가 많 을 수 있 습 니 다.하위 클래스 계승 때마다 부모 클래스 를 만 들 면 성능 이 떨어진다.
    여기 서 위의 문 제 를 해결 하기 위해 우 리 는 이러한 중복 되 는 부분 을 원형 체인 에 놓 을 수 있다. 이렇게 하면 우리 가 기 류 를 만 들 때 매번 에 만 든 간단 하면 서도 차별 화 된 내용 에 대해 우 리 는 구조 함수 에 두 고 자원 을 소모 하 는 비교적 큰 방법 을 기본 적 인 원형 에 두 어 불필요 한 소 모 를 피 할 수 있다.이것 도 원형 모델 의 모형 이다.
    //      
    var LoopImage = function (imgArr, container) {
      this.imageArray = imgArr; //       
      this.container = container; //       
    }
    
    LoopImages.prototype = {
      //       
      createImage: function () {},
      changeImage: function () {}
    }
    

    계승 이미지 윤 방 류 함수
    //        
    var SlideLoopImg = function (imgArr, container) {
      //            
      LoopImage.call(this, imgArr, container);
    }
    
    SlideLoopImg.prototype = new LoopImage();
    
    //             
    SlideLoopImg.prototype.changeImage = function () {}
    
    //      
    var FadeLoopImg = function (imgArr, conyainer, arrow) {
      //         
      this.arrow = arrow;
    }
    FadeLoopImg.prototype = new LoopImage();
    FadeLoopImg.prototype.changeImage = function () {}
    

    여기 서 우리 가 주의해 야 할 것 은 원형 대상 이 공유 하 는 대상 이 라면 부계 의 실례 대상 이 든 부계 의 상속 이 든 모두 그 에 대한 지향 적 인용 이 고 원형 식 이 공유 되 는 것 이다. 즉, 원형 대상 에 대한 확장 은 부계 든 부계 의 실례 대상 이 든 모두 계 승 될 것 이다.특징: 언제든지 기본 클래스 나 하위 클래스 에 대해 방법 을 확대 할 수 있 고 모든 실례 화 된 대상 이나 클래스 는 이런 방법 을 얻 을 수 있 기 때문에 우 리 는 기능 확장 에 대한 자유 성 을 가지 고 원형 상의 방법 을 수정 할 때 이 방법 을 사용 하 는 다른 하위 클래스 에 영향 을 주 고 부작용 이 발생 하기 쉽다.
    따라서 프로 토 타 입 모델 은 대상 을 만 드 는 데 더 많이 활용 된다. 예 를 들 어 인 스 턴 스 대상 을 만 드 는 구조 함수 가 복잡 하거나 시간 이 오래 걸 리 거나 여러 대상 을 만 드 는 것 을 통 해 이 루어 진다. 이때 new 키워드 로 기 류 를 복사 하지 않 는 것 이 좋 지만 이러한 대상 의 속성 이나 방법 을 복사 하여 만 들 수 있다.따라서 원형 모델 의 구체 적 인 방법 은 다음 과 같다.
    /******
      *                     
      * arguments[0], arguments[1],arguments[2]:  1,  2,  3      
      *   :                     (        ),               
      ******/
    function prototypeExtend () {
      var F = function () {}, //    ,            
      args = arguments,
      i = 0,
      len = args.length;
      for (; i

    우리 가 상속 대상 에 게 독립 된 원형 대상 이 필요 할 때 우 리 는 원형 대상 을 복제 해 야 한다. 따라서 원형 대상 은 복잡 한 대상 을 만 드 는 데 더욱 적합 하고 수요 가 끊임없이 변화 하여 대상 구조 가 끊임없이 변화 하 는 상황 에 대해 비교적 안정 적 인 속성 과 방법 을 공유 하여 상속 의 실현 을 추출 해 야 한다.
    단일 모드
    한 번 만 실례 화 할 수 있 는 대상 클래스 입 니 다. 장점 은 우리 도 하나의 대상 으로 하나의 명칭 공간, 질서정연 한 관리 대상 의 속성 과 방법 을 계획 합 니 다.단일 모드 에 서 는 이러한 문 제 를 해결 하 는 데 사용 되 는 네 임 스페이스 를 제공 합 니 다. 코드 를 쉽게 이해 하기 위해 사람들 은 단어 나 병 음 으로 변수 나 방법 을 정의 하지만 사람들 이 사용 할 수 있 는 단어 나 한자 병 음 이 제한 되 어 있 기 때문에 서로 다른 사람들 이 정 한 변 수 를 사용 하 는 단어 이름 이 중복 될 수 있 습 니 다.이 때 우 리 는 이렇게 이름 을 지 을 수 있 습 니 다. 작은 장 이 쓴 코드 는 xiaozhang 의 네 임 스페이스 로 정의 할 수 있 습 니 다. 작은 장 이 정의 한 변 수 는 xiaozhang. xx 를 통 해 사용 할 수 있 습 니 다.또한, 우 리 는 자주 단일 모드 를 통 해 코드 라 이브 러 리 의 각 모듈 을 관리한다.예 를 들 어 jQuery 에서 jQuery. animate ().구체 적 으로 아래 코드 를 참고 할 수 있다
    var Ming = {
      g: function (id) {
        return document.getElementById(id)
      },
      css: function (id, key, value) {
        //       this   g  
        this.g(id).style[key] = value;
      }
    }
    

    마지막 으로 단일 모드 는 정적 변 수 를 관리 하 는 데 도 도움 을 줄 수 있다.JavaScript 에 static 키워드 가 없 기 때문에 모든 변 수 는 이론 적 으로 수정 할 수 있 기 때문에 JavaScript 에서 정적 변 수 를 실현 하 는 것 이 필요 합 니 다.정적 변수의 특징: 접근 만 할 수 있 고 수정 할 수 없습니다.생 성 후 바로 javaScript 에 사용 할 수 있 습 니 다. 정적 변 수 를 만 드 는 사 고 는 수정 되 지 않도록 함수 역할 영역 에 넣 고 특권 적 인 방법 으로 접근 해 야 합 니 다.생 성 된 후에 바로 사용 할 수 있 도록 만 든 함수 가 한 번 실 행 될 수 있 도록 해 야 합 니 다. 이때 우리 가 만 든 대상 에 정적 변 수 를 저장 하고 수치 기 를 통 해 접근 합 니 다. 마지막 으로 이 대상 을 하나의 사례 로 전체 공간 에 두 고 정적 변수 로 다른 사람 에 게 사용 할 수 있 습 니 다.
    var Conf = (function () {
      //     
      var conf = {
        MAX_NUM: 100,
        MIN_NUM: 1,
        COUNT: 1000
      }
      //        
      return {
        //      
        get: function (name) {
          return conf[name] ? conf[name] : null;
        }
      }
    })();
    
    //     
    var count = Conf.get('COUNT');
    

    게 으 름 생 성: 어떤 때 는 단일 대상 에 대한 생 성 지연 이 필요 합 니 다. 우 리 는 생 성 지연 형식 을 사용 합 니 다.
    //       
    var LazySingle = (function () {
      //       
      var instance = null;
      //   
      function Single () {
        //            
        return {
          publicMethod: function () {},
          publicProperty: '1.0'
        }
      }
    
      //         
      return function () {
        //             
        if (!_instance) {
          _instance = Single();
        }
        //     
        return _instance
      }
    })()
    
    LazySingle().publicProperty
    

    참고 문헌
    장 용 명 《 자 바스 크 립 트 디자인 모델 》

    좋은 웹페이지 즐겨찾기