> 웹 프론트엔드 > JS 튜토리얼 > 자바스크립트 상속 학습 경험_기본지식 요약

자바스크립트 상속 학습 경험_기본지식 요약

WBOY
풀어 주다: 2016-05-16 15:10:22
원래의
1078명이 탐색했습니다.

js 상속에 대해 많은 내용을 읽었으니 이제 요약할 차례입니다.
먼저 일반적인 이해를 드리겠습니다. 제가 틀렸다면 정정해 주십시오. 그러면 저의 세 가지 견해를 바로잡을 수 있습니다. 또한, 다음 예제는 원본이 아니며 단지 변수 이름을 변경한 것일 뿐이며 일부는 직접 사용하기도 합니다.

js 상속의 용도:

우선 js에는 다른 객체지향 언어와 동일한 상속 개념이 없습니다. js에서 언급하는 상속은 시뮬레이션 상속을 의미합니다.
js 상속이 정확히 무슨 일을 하나요? 처음 프론트엔드 작업을 시작할 때 인터뷰용으로 사용했습니다(처음 작은 효과를 작성할 때는 기본적으로 사용하지 않았습니다. 왜 읽어야 할까요? 면접관들이 좋아하기 때문이죠. 이 질문을 하기 위해) 그래서 그냥 인터뷰에 사용했어요. 대략적으로 살펴보시고, 인터뷰할 때 대략적인 개요를 말씀해 주신다면 이 문제에 대한 좋은 면접관이라고 생각합니다. 나중에는 개념이 그다지 명확하지 않았음에도 불구하고 점차적으로 사용하기 시작했습니다.
실제로는 어떤 용도로 사용되나요? 주로 이전에 작성한 코드를 재사용하는 데 사용됩니다. 예를 들어, 우리가 함수나 객체를 작성했거나 다른 사람이 작성한 것을 사용했는데 우리가 직접 뭔가를 추가하고 싶다면 다른 사람의 것을 변경할 수는 없습니다. 상속의 실제 사용.

js 상속 구현 방법:
먼저 코드에 들어가지는 않고 제 생각을 먼저 말씀드리겠습니다. 실제로 상속은 다른 객체(js의 모든 것은 객체입니다)의 속성이나 메서드를 우리 객체로 전송하여 우리 객체를 사용할 수 있도록 하는 방법을 찾는 것입니다. 이는 또한 재사용 목적을 달성합니다.
이제 목적이 분명해졌으니 이를 달성하기 위한 수단은 다음과 같습니다.
js의 특성에 따라 구현은 다음 방법 중 하나 또는 조합에 지나지 않습니다.

1. 생성자, js는 생성자에 대한 엄격한 정의가 없는 것 같지만 new를 사용하여 새 개체를 만들 수 있습니다. 생성자 역시 엄격한 객체지향 언어에서 상속을 구현하기 위한 방법이라고 하기 때문에 당연히 js에서도 시뮬레이션이 가능하므로 oop 언어를 배워본 분들이라면 이런 생각이 먼저 드실 겁니다.

2. 함수 프로토타입과 프로토타입 체인을 사용하여 두 객체를 연결합니다. js 프로토타입 체인은 상대적으로 독특하기 때문에 이렇게 생각하기 쉽습니다.
프로토타입에는 여러 가지 유형이 있습니다. 즉, 상속된 객체의 프로토타입, 상속된 객체의 프로토타입 또는 상속된 객체의 인스턴스 또는 직접 상속자로 사용되는 것입니다. 이러한 프로토타입을 상속 개체로 사용하여 얻는 상속 효과는 다릅니다.

3. 속성 및 메소드 복사 상속된 객체의 모든 속성 또는 메소드를 복사하여 자체 객체의 속성 및 메소드로 변환하면 매우 원활하게 사용할 수 있습니다. 물론 이는 얕은 복사와 깊은 복사라는 두 가지 상황으로 나눌 수 있습니다.

4. 호출과 적용이라는 두 가지 메서드를 사용하세요. 이 두 메서드는 함수 실행 컨텍스트를 변경할 수 있습니다(this). 따라서 이 두 메서드를 사용하면 상속된 메서드의 상속과 재사용도 실현할 수 있습니다. 물체. .

일반적으로 js에서 상속을 달성하는 방법은 아마도 이러한 방법을 기반으로 모두 결합되고 업그레이드됩니다. 물론 사용의 효과 때문입니까? 단일 방법은 이상적이지 않습니다. 물론, 자신의 요구 사항을 충족하고 이를 달성하기 위해 어떤 방법을 사용해야 하는지 명시하지 않는 한 프로젝트의 실제 요구 사항에 따라 사용할 방법을 선택할 수 있습니다. 베이징에서 스자좡까지 가는 가장 빠른 방법은 비행기라고 말하는 것과 같습니다. 하지만 공항까지의 이동, 공항에서 시내로의 이동 등 공항과의 거리가 전반적으로 고속철도만큼 빠르지 않다면 고속철도를 이용할 수 있습니다. 예를 들어, 자동차가 있고 운전을 할 수 있지만, 도전하고 싶다면 자전거를 타는 것이 좋습니다.


코드 구현, 코드를 기반으로 위의 구현 방법에 대해 이야기해 보겠습니다. 그 중 일부는 다른 곳에서 가져온 것이며 몇 가지 설명을 추가합니다.

js 상속에 대해 많은 내용을 읽었으니 이제 요약할 차례입니다.
먼저 일반적인 이해를 드리겠습니다. 제가 틀렸다면 정정해 주십시오. 그러면 저의 세 가지 견해를 바로잡을 수 있습니다. 또한, 다음 예제는 원본이 아니며 단지 변수 이름을 변경한 것일 뿐이며 일부는 직접 사용하기도 합니다.

js 상속의 용도:

우선 js에는 다른 객체지향 언어와 동일한 상속 개념이 없습니다. js에서 언급하는 상속은 시뮬레이션 상속을 의미합니다.
js 상속이 정확히 무슨 일을 하나요? 처음 프론트엔드 작업을 시작할 때 인터뷰용으로 사용했습니다(처음 작은 효과를 작성할 때는 기본적으로 사용하지 않았습니다. 왜 읽어야 할까요? 면접관들이 좋아하기 때문이죠. 이 질문을 하기 위해) 그래서 그냥 인터뷰에 사용했어요. 대략적으로 살펴보시고, 인터뷰할 때 대략적인 개요를 말씀해 주신다면 이 문제에 대한 좋은 면접관이라고 생각합니다. 나중에는 개념이 그다지 명확하지 않았음에도 불구하고 점차적으로 사용하기 시작했습니다.
실제로는 어떤 용도로 사용되나요? 주로 이전에 작성한 코드를 재사용하는 데 사용됩니다. 예를 들어, 우리가 함수나 객체를 작성했거나 다른 사람이 작성한 것을 사용했는데 우리가 직접 뭔가를 추가하고 싶다면 다른 사람의 것을 변경할 수는 없습니다. 상속의 실제 사용.

js 상속을 구현하는 방법:
먼저 코드에 대해 이야기하지 말고 먼저 아이디어에 대해 이야기합시다. 실제로 상속은 다른 객체(js의 모든 것은 객체입니다)의 속성이나 메서드를 우리 객체로 전송하여 우리 객체를 사용할 수 있도록 하는 방법을 찾는 것입니다. 이는 또한 재사용 목적을 달성합니다.
이제 목적이 분명해졌으니 이를 달성하기 위한 수단은 다음과 같습니다.
js의 특성에 따라 구현은 다음 방법 중 하나 또는 조합에 지나지 않습니다.

1. 생성자, js는 생성자에 대한 엄격한 정의가 없는 것 같지만 new를 사용하여 새 개체를 만들 수 있습니다. 생성자 역시 엄격한 객체지향 언어에서 상속을 구현하기 위한 방법이라고 하기 때문에 당연히 js에서도 시뮬레이션이 가능하므로 oop 언어를 배워본 분들이라면 이런 생각이 먼저 드실 겁니다.

2. 함수 프로토타입과 프로토타입 체인을 사용하여 두 객체를 연결합니다. js 프로토타입 체인은 상대적으로 독특하기 때문에 이렇게 생각하기 쉽습니다.
프로토타입에는 여러 가지 유형이 있습니다. 즉, 상속된 객체의 프로토타입, 상속된 객체의 프로토타입 또는 상속된 객체의 인스턴스 또는 직접 상속자로 사용되는 것입니다. 이러한 프로토타입을 상속 개체로 사용하여 얻는 상속 효과는 다릅니다.

3. 속성 및 메소드 복사 상속된 객체의 모든 속성 또는 메소드를 복사하여 자체 객체의 속성 및 메소드로 변환하면 매우 원활하게 사용할 수 있습니다. 물론 이는 얕은 복사와 깊은 복사라는 두 가지 상황으로 나눌 수 있습니다.

4. 호출과 적용이라는 두 가지 메서드를 사용하세요. 이 두 메서드는 함수 실행 컨텍스트를 변경할 수 있습니다(this). 따라서 이 두 메서드를 사용하면 상속된 메서드의 상속과 재사용도 실현할 수 있습니다. 물체. .

일반적으로 js에서 상속을 달성하는 방법은 아마도 이러한 방법을 기반으로 모두 결합되고 업그레이드됩니다. 물론 사용의 효과 때문입니까? 단일 방법은 이상적이지 않습니다. 물론, 자신의 요구 사항을 충족하고 이를 달성하기 위해 어떤 방법을 사용해야 하는지 명시하지 않는 한 프로젝트의 실제 요구 사항에 따라 사용할 방법을 선택할 수 있습니다. 베이징에서 스자좡까지 가는 가장 빠른 방법은 비행기라고 말하는 것과 같습니다. 하지만 공항까지의 이동, 공항에서 시내로의 이동 등 공항과의 거리가 전반적으로 고속철도만큼 빠르지 않다면 고속철도를 이용할 수 있습니다. 예를 들어, 자동차가 있고 운전을 할 수 있지만, 도전하고 싶다면 자전거를 타는 것이 좋습니다.


코드 구현, 코드를 기반으로 위의 구현 방법에 대해 이야기해 보겠습니다. 그 중 일부는 다른 곳에서 가져온 것이며 몇 가지 설명을 추가합니다.

1. 생성자 구현(생성자 차용):

function Super(arg){
      this.arr1 = "I'm super "+arg;
      this.show = function(){
        alert(this.arr1);
      }
    }

    Super.prototype.say = function(){
      alert(this.arr1);
    }

    function suber(arg){
      Super.apply(this, arguments); //在suber的上下文中运行super
    }

    var sub =new suber("suber");
    var sub2 = new suber("suber1");

    console.log(sub.arr1); //I'm super suber
    console.log(sub.show); //function (){ alert(this.arr1);}
    console.log(sub.say); //undefined
    console.log(sub.show === sub2.show); //false
로그인 후 복사

앗, sub.say가 정의되지 않은 것을 발견했습니다. 이는 상속되지 않았음을 의미합니다. 아래 두 객체 sub와 sub2의 표시는 동일하지 않습니다. 이는 두 함수가 두 개의 다른 객체를 가리킨다는 의미입니다. 복사가 2개 나왔어요.

따라서 이 메서드가 상속을 구현하는 경우 프로토타입 개체의 속성과 메서드는 상속되지 않으며 Super의 속성과 메서드는 새 개체마다 별도로 복사됩니다.
따라서 프로토타입의 메서드 중 어느 것도 상속되지 않았기 때문에 상속을 구현하기 위해 이 메서드만 사용하는 것은 적절하지 않습니다. 그래서 마스터들은 프로토타입 상속이라는 아이디어를 생각해 냈습니다

2. 프로토타입 상속:

function Super(arg){
      this.arr1 = "I'm super "+arg;
      this.show = function(){
        alert(this.arr1);
      }
    }

    Super.prototype.say = function(){
      alert(this.arr1);
    }

    function suber(arg){}

    suber.prototype = new Super();

    var sub = new suber("suber1");
    var sub2 = new suber("suber2");

    console.log(sub.arr1); //I'm super undefined
    console.log(sub.show); //function (){ alert(this.arr1);}
    console.log(sub.say); //function (){ alert(this.arr1);}
    console.log(sub.show === sub2.show);  //true;
    console.log(sub.say === sub2.say);  //true;
로그인 후 복사


이번에는 arr1을 상속받았으나 매개변수가 추가되지 않고 정의되지 않았기 때문에 이 메소드의 서브클래스를 선언할 때 전달된 매개변수와 서브클래스에서 상속받은 속성을 받을 수 없었습니다. 다른 모든 것은 꽤 정상입니다. Show와 Say는 모두 상속됩니다. 다만 주목할 점은 say는 super의 프로토타입 객체를 통해 상속되고, show는 새로운 슈퍼 객체 인스턴스를 생성할 때 해당 인스턴스의 속성이라는 점입니다.

그럼 프로토타입에 어떻게 매개변수 전송을 구현하고 상속하는 걸까요? 물론 위의 두 가지 방법을 결합하면 되기 때문에 선배님들은 다음과 같은 방법을 고안해 냈습니다


3. 결합 상속(생성자 대여 및 프로토타입 설정):

function Super(arg){
      this.arr1 = "I'm super "+arg;
      this.show = function(){
        alert(this.arr1);
      }
    }

    Super.prototype.say = function(){
      alert(this.arr1);
    }

    function suber(arg){
      Super.apply(this, arguments);
    }

    suber.prototype = new Super();


    var sub = new suber("suber1");
    var sub2 = new suber("suber2");

    console.log(sub.arr1); //I'm super suber1
    console.log(sub.show); //function (){ alert(this.arr1);}
    console.log(sub.say); //function (){ alert(this.arr1);}
    console.log(sub.show === sub2.show);  //false;
    console.log(sub.say === sub2.say);  //true;
로그인 후 복사

이번에는 거의 완벽하지만 sub.show와 sub2.show가 동일하지 않다는 것을 알 수 있습니다. 적용 위치가 show를 suber 고유의 속성으로 만든 다음 suber 프로토타입에 넣으면 되기 때문입니다. 쇼(Super의 쇼는 하위 프로토타입 개체의 인스턴스 개체로 사용됨)가 덮어쓰기되므로 한 번에 하나씩 복사됩니다. 물론 이를 피할 수 있는 방법은 없습니다. 이러한 불필요한 오버헤드를 발생시키지 않기 위해 이러한 공유 가능한 기능 중 더 많은 기능을 프로토타입 객체에 배치할 수 있습니다.
suber 생성 시 호출과 하위 프로토타입 객체에 값을 할당할 때의 호출로 인해 Super가 두 번 호출되기 때문에 새로운 하위 객체가 생성될 때마다 Super가 두 번 호출되고, 다음과 같은 경우 두 개의 인스턴스 객체가 생성됩니다. 두 번 호출해야 합니다.

그래서 선배들은 이 문제를 해결하기 위해 마음을 열고 다음과 같은 방법을 개발했습니다.

4. 기생 조합 상속:
이 방법과 방법 3의 주요 차이점은 부모 클래스 예제가 아닌 자식 클래스 프로토타입에 부모 클래스 프로토타입이 할당된다는 점입니다

function Super(arg){
  this.arr1 = "I'm super "+arg;
  
}

Super.prototype.show = function(){ //这个方法放到了原型对象上。
    alert(this.arr1);
  }
Super.prototype.say = function(){
  alert(this.arr1);
}

function suber(arg){
  Super.apply(this, arguments);
}

/*inherit函数的作用,使用一个新的空函数,来切断父类对象的原型对象与子类原型对象的直接联系,而是通过这个空构造的实例对象实现继承,这样可以避免更改子类原型的属性或者方法而影响了父类原型对象的属性或者方法。*/

function inherit(obj){ 
  function F(){}
  F.prototype = obj;
  return new F();  
}

suber.prototype = inherit(Super.prototype);


var sub = new suber("suber1");
var sub2 = new suber("suber2");

console.log(sub.arr1); //I'm super suber1
console.log(sub.show); //function (){ alert(this.arr1);}
console.log(sub.say); //function (){ alert(this.arr1);}
console.log(sub.show === sub2.show);  //true;
console.log(sub.say === sub2.say);  //true;
로그인 후 복사

好了,这样就把三方法的弊端干掉了,这个可以完美的使用了吧。

五、复制属性实现

拷贝我们可以写一个拷贝函数来实现。

function extend(Super,suber){
  suber = suber || {};
  for(var i in Super){
    if(Super.hasOwnProperty(i)){
      suber[i] = Super[i];
    }
   }
  return suber;
}


var parent = {
  name:"dad",
  num:[1,2,3],
  say:function(){alert("dad");}
}

var child = {
  age:5,
  sex:"boy"
};

child = extend(parent, child);

//以下测试
console.log(child); /*{
             age:5,
             sex:"boy",
             name:"dad",
             num:[1,2,3],
             say:function(){alert("dad");}
            }*/
console.log(child.say === parent.say); //true
console.log(child.num === parent.num); //true
로그인 후 복사

复制成功,那么child成功继承了parent的一些属性,但是后面两个测试发现他们是相等的,就表明了他们在公用同一个数组,同一个函数,函数这个可以,但是数组这个就有问题了,如果一个chiild改变了数组,几个被继承对象的数组也跟着变了,这就不给力了啊。
为什么会发生这种情况呢,js里边对象存储的是指针,然后这个指针指向这个值,我们在这复制的实际是指向该对象的指针的值,所以继承对象和被继承对象都指向了同一个对象,接下来看看如何使用深度复制来解决这个问题。

深度复制对象属性:

function extenddeep(Super, suber){
      var tostr = Object.prototype.toString, astr = "[object Array]";
      suber = suber || {};

      for(var i in Super){
        if(Super.hasOwnProperty(i)){
          if(typeof Super[i] === "object"){
            suber[i] = (tostr.call(Super[i]) == astr) ? [] : {};
            extenddeep(Super[i],suber[i]);
          }else {
            suber[i] = Super[i];
          }
        }
      }

      return suber;
    }


    var parent = {
      name:"papa",
      num:[1,2,3],
      say:function(){alert("I'm father of my son!");}
    }

    var child = {
      name:"jone",
      sex:"boy",
    }

    var kid = extenddeep(parent, child);

    console.log(kid);  // {name: "papa"
                num: Array[3]
                say: ()
                sex: "boy"
              // }

    console.log(kid.say === parent.say); //true
    console.log(kid.num === parent.num);  //false
    console.log(kid.name); //papa
로그인 후 복사

好了,深度复制完毕,但似有木有发现问题,name是parent的,也就是说如果继承对象有和被继承对象一样的属性名的属性如果不做处理就会被替换掉。那么我们可以做一下处理,先声明一个属性,保存parent里的东西,剩下的的当然就是child自己的东西了,最后再把属性给child对象就可以了。

六、利用call和apply这两个方法(借用方法)。
这个就是通过call和apply来复用其他对象的方法,达到复用的目的。

var one = {
      name:"object",
      say: function(greet){
        return greet + ', ' + this.name;
      }
    }

    var tow = {
      name:"two"
    }

    one.say.call(tow, "hi");  //hi, two
로그인 후 복사

这个就是借用了,好了,下课。

好吧,好吧,其实这里边还有其他东西要看。可以借用并不“带表”可以随便把某个方法赋值给谁然后跟没发生什么似的继续用。所以我们平时使用借用时要注意一下上下文,下面看下那些容易出错的地方。

//赋值给一个变量时候上下文会变化
    var say = one.say;
    console.log(say('hoho')); // "hoho, undefined"

    //作为回调函数时也会发生变化
    var yetother = {
      name:"yetother obj",
      method:function(callback){
        return callback("Hola");
      }
    }

    console.log(yetother.method(one.say)); //"Hola, "
로그인 후 복사

神马意思呢,就是this.name是undefined,当one.say赋值给say是,实际上是say存储了指向函数对象的指针,say这个变量明显又是全局变量的一个属性,那么它运行的时候实际的上下文就变成了windows了,当然这个时候name就变成undefined了。回调这个也是一样,return 的是函数运行的结果。如果我们事先设置 windows.name="windows" 那么得到的结果就变成了 "hoho, windows" 和"Hola, windows" 了。

function bind(o, m){
      return function(){
        return m.apply(o, [].slice.call(arguments));
      }
    }

    var othersay = bind(yetother, one.say);

    othersay("Hola"); //"Hola, yetother obj"
로그인 후 복사

通过apply可以改变方法执行的上下文,那么我们构建一个函数来实现这样一个功能,通过使用方法调用实现上下文的改变,这样就不会出现上下文不是我们期望的上下文的情况了。

//这段是直接复制过来的。
    // ECMAScript 5给Function.prototype添加了一个bind()方法,以便很容易使用apply()和call()。

      if (typeof Function.prototype.bind === 'undefined') {
        Function.prototype.bind = function (thisArg) {
          var fn = this,
      slice = Array.prototype.slice,
      args = slice.call(arguments, 1);
          return function () {
            return fn.apply(thisArg, args.concat(slice.call(arguments)));
          };
        };
      }

      var twosay2 = one.say.bind(two);
      console.log(twosay2('Bonjour')); // "Bonjour, another object"

      var twosay3 = one.say.bind(two, 'Enchanté');
      console.log(twosay3()); // "Enchanté, another object"
로그인 후 복사

介绍完了,该说说自己的疑惑了,当复制属性方法遇到的被继承对象里边存在方法,如何单独复制出来呢,现在的是直接共用了,因为毕竟方法一般不会经常改动。求解答?

下面是转载过来的jQuery的extend方法,好像也没有特殊处理函数这块,继承完了两个函数也是共用的。

$.extend源码

jQuery.extend = jQuery.fn.extend = function() {
          var options, name, src, copy, copyIsArray, clone,
             target = arguments[0] || {},
             i = 1,
             length = arguments.length,
             deep = false ;

          // Handle a deep copy situation
          //如果第一个参数是boolean类型
          //修正参数,将第二个参数作为target
          if ( typeof target === "boolean" ) {
             deep = target;

              // skip the boolean and the target
             target = arguments[ i ] || {};
              //i++是为了后续 i === length的判断
             i++;
          }

          // Handle case when target is a string or something (possible in deep copy)
          //如果目标既不是对象也不是方法(例如给基本类型扩展属性方法和属性不会报错但是是无用的),修正target为 js对象
          if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
             target = {};
          }

          // extend jQuery itself if only one argument is passed
          //如果只有一个参数,修正对象为JQuery函数或JQuery对象
          if ( i === length ) {
             target = this ;
              //修正target所在位置,后面的都是要添加给target的对象
             i--;
          }

          for ( ; i < length; i++ ) {
              // Only deal with non-null/undefined values
              if ( (options = arguments[ i ]) != null ) {
                 // Extend the base object
                 for ( name in options ) {
                    src = target[ name ];
                    copy = options[ name ];

                    // Prevent never-ending loop
                    //如果target和copy是同一个对象,略过,防止自己的属性引用了本身对象导致的循环引用,以致GC无法回收
                    if ( target === copy ) {
                        continue ;
                    }

                    // Recurse if we're merging plain objects or arrays
                    //如果是deep为true,并且要添加给target的copy为对象获数组
                    if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
                        if ( copyIsArray ) {
                           copyIsArray = false ;
                           clone = src && jQuery.isArray(src) &#63; src : [];

                       } else {
                           clone = src && jQuery.isPlainObject(src) &#63; src : {};
                       }
                 
                        // Never move original objects, clone them
                        //很巧妙 ,用一个递归,实现引用对象的深克隆,递归的返回条件是属性石基本类型,基本类型都是深克隆
                       target[ name ] = jQuery.extend( deep, clone, copy );

                    // Don't bring in undefined values
                    } else if ( copy !== undefined ) {
                        //浅克隆
                       target[ name ] = copy;
                    }
                 }
             }
          }

          // Return the modified object
          return target;
      };
로그인 후 복사

以上这篇javascript 继承学习心得总结就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿