javascript 프레임워크 디자인 및 기타 Factory_javascript 기술

WBOY
풀어 주다: 2016-05-16 15:53:27
원래의
1244명이 탐색했습니다.

JavaScript에서 클래스와 상속의 출현은 JavaScript가 대규모 개발의 문턱에 도달했음을 보여줍니다. ECMAScript 4 이전에는 클래스, 모듈 등을 도입하려고 시도했지만 너무 많은 도입으로 인해. 기능 때문에 JavaScript가 엉망이 되어 거부당했습니다. 하지만 ES6으로의 클래스가 지연될 뿐입니다. 지금까지 JavaScript에는 실제 클래스가 없습니다. 그러나 최근에는 클래스 팩토리가 프레임워크의 표준 기능이 되었습니다. 이 장에서는 모든 사람이 자신의 프레임워크에서 원하는 스타일을 쉽게 선택할 수 있도록 다양한 클래스 구현을 소개합니다.

1.수업에 대한 자바스크립트 지원

다른 언어에서는 클래스의 인스턴스가 new 생성자를 통해 생성되어야 합니다. 의도적으로 Java를 모방한 언어입니다. JavaScript에는 새로운 연산자가 있으며 모든 함수를 생성자로 사용할 수 있습니다. 생성자는 일반적인 방법과 다르지 않습니다. Node, Element, HTMLElement 및 HTMLParagraphElement와 같은 번성하는 생태계를 구축하기 위해 브라우저는 분명히 상속 관계를 사용하여 일부 메서드나 속성의 공유를 용이하게 하므로 JavaScript는 다른 언어에서 프로토타입 메커니즘을 차용합니다. 프로토타입은 모든 기능에 대한 특별한 객체 속성으로 존재합니다. 함수가 new 연산자를 사용하여 "자식" - "인스턴스"를 생성하면 인스턴스라는 이름의 이 객체는 이 함수의 프로토타입 객체의 모든 멤버를 가지므로 모든 인스턴스 객체가 메서드 또는 속성 집합을 공유한다는 것을 인식합니다. JavaScript에서 소위 "클래스"는 이 프로토타입 개체를 수정하여 기본 개체를 정의된 다른 "클래스"와 구별하는 것입니다. 브라우저에서는 Node 클래스는 Object를 기반으로 수정되고 Element는 Node를 기반으로 하며 HTMLElement는 Element를 기반으로 수정됩니다.... 업무에 비해 자체 클래스를 생성하여 재사용 및 공유가 가능합니다.

  function A(){

  }
  A.prototype = {
    aa:"aa",
    method:function(){
    }
  };
  var a = new A;
  var b = new A;
  console.log(a.aa === b.aa);
  console.log(a.method === b.method)

로그인 후 복사

일반적으로 프로토타입에 정의된 메소드를 모든 인스턴스가 공유하는 프로토타입 메소드라고 부릅니다. 이는 좋은 점이기도 하고 나쁜 점이기도 합니다. JavaScript에서는 생성자에서 해당 메소드를 직접 지정할 수 있습니다. 권한 있는 방법이라고 합니다. 속성인 경우 권한 있는 속성이라고 합니다. 각 인스턴스에는 복사본이 있으며 서로 영향을 받지 않습니다. 따라서 우리는 일반적으로 프로토타입에 데이터를 운용하기 위한 공유 메소드를 넣고, 특권 속성에 프라이빗 속성을 넣습니다. 하지만 이렇게 올려놓으면 여전히 마음대로 접근이 가능하므로 함수 본문의 범위에 넣습니다. 이 시점에서 그것은 진정한 사유 재산이 됩니다.

  function A() {
    var count = 0;
    this.aa = "aa";
    this.method = function() {
      return count;
    }
    this.obj = {}
  }
  A.prototype = {
    aa:"aa",
    method:function(){

    }
  };
  var a = new A;
  var b = new A;
  console.log(a.aa === b.aa);//true 由于aa的值为基本类型,比较值
  console.log(a.obj === b.obj) //false 引用类型,每次进入函数体都要重新创建,因此都不一样。
  console.log(a.method === b.method); //false

로그인 후 복사

권한 있는 메서드 또는 속성은 프로토타입 메서드 또는 속성에만 적용되므로 권한 있는 메서드를 삭제하는 한 동일한 이름을 가진 프로토타입 메서드 또는 속성에 액세스할 수 있습니다.

  delete a.method;
  delete b.method;
  console.log(a.method === A.prototype.method);//true
  console.log(a.method === b.method); //true

로그인 후 복사

Java 언어에서는 프로토타입 메소드와 특권 메소드 모두 인스턴스 메소드의 속성입니다. Java에는 클래스 메소드와 클래스 속성이라는 것이 있습니다. JavaScript를 사용하여 시뮬레이션하는 것은 매우 간단합니다. 함수에서 직접 정의하기만 하면 됩니다.

  A.method2 = function(){} //类方法
  var c = new A;
  console.log(c.method2); //undefined
로그인 후 복사

다음으로 상속 구현을 살펴보겠습니다. 위에서 언급한 것처럼 프로토타입에 있는 것이 무엇이든 인스턴스에는 이 속성이 나중에 추가되거나 전체 프로토타입이 교체되는 등의 무언가가 있습니다. 이 프로토타입 객체를 다른 클래스의 프로토타입으로 바꾸면 해당 클래스의 모든 프로토타입 멤버를 쉽게 얻을 수 있습니다.

  function A() {};
  A.prototype = {
    aaa : 1
  }
  function B() {};
  B.prototype = A.prototype;
  var b = new B;
  console.log(b.aaa); //=> 1;
  A.prototype.bb = 2;
  console.log(b.bb) //=> 2;

로그인 후 복사

동일한 객체를 참조하므로 A 클래스의 프로토타입을 수정하면 B 클래스의 프로토타입을 수정하는 것과 같습니다. 따라서 하나의 객체를 두 클래스에 할당할 수 없습니다. 두 가지 방법이 있습니다.

방법 1:
에서 for를 통해 상위 클래스의 프로토타입 멤버를 하위 클래스의 프로토타입에 하나씩 할당합니다. 방법 2는: 하위 클래스의 프로토타입을 상위 클래스에서 직접 가져오지 않는 것입니다. 먼저 상위 클래스의 프로토타입을 함수에 할당한 다음 이 함수의 인스턴스를 하위 클래스의 프로토타입으로 사용합니다.

첫 번째 방법은 일반적으로 일부 책에서 복사 상속이라고 부르는 mixin과 같은 방법을 구현해야 합니다. 장점은 간단하고 직접적이지만 단점은 검증 인스턴스를 통과할 수 없다는 것입니다. 이를 위해 Prototype.js의 확장 메소드가 사용됩니다.

  function extend (des, source) { //des = destination
    for (var property in source)
      des[property] = source[property];
    return des;
  }
로그인 후 복사

두 번째 방법은 프로토타입에 두뇌를 사용하는 것이므로 프로토타입 상속이라고 합니다. 아래는 템플릿입니다

  function A() {};
  A.prototype = {
    aa:function(){
      alert(1)
    }
  }
  function bridge() {

  };
  bridge.prototype = A.prototype;

  function B() {}
  B.prototype = new bridge();

  var a = new A;
  var b = new B;

  console.log(a == b) //false 证明成功分开原型
  console.log(A.prototype == B.prototype) //true 子类共享父类的原型方法
  console.log(a.aa === b.aa); //为父类动态添加新的方法
  A.prototype.bb = function () {
    alert(2)
  }
  //true,继承父类的方法
  B.prototype.cc = function (){
    alert(3)
  }
  //false 父类未必有子类的new实例
  console.log(a.cc === b.cc)
  //并且它能够正常通过javascript自带的验证机制instanceof
  console.log(b instanceof A) ;//true
  console.log(b instanceof B) ; //true

로그인 후 복사

방법 2는 인스턴스 검증을 통과할 수 있습니다. es5에는 프로토타입 상속을 구현하기 위해 이 방법이 내장되어 있습니다. 두 번째 매개변수를 고려하지 않으면 대략 다음 코드와 같습니다.

  Object.create = function (o) {
    function F() {}
    F.prototype = o;
    return new F();
  }

로그인 후 복사

위 메소드에서는 상위 클래스의 프로토타입을 매개변수로 전달한 다음 하위 클래스의 프로토타입을 반환해야 합니다.

그러나 우리는 여전히 뭔가를 놓치고 있습니다. 하위 클래스는 상위 클래스의 상속을 상속받을 뿐만 아니라 자체적인 것도 가지고 있습니다. 게다가 프로토타입 상속은 하위 클래스가 상위 클래스의 멤버 및 권한 있는 멤버를 상속하는 것을 허용하지 않습니다. . 클래스 멤버와 같은 이들을 수동으로 추가해야 하며 위의 확장 메서드를 사용할 수 있으며 권한이 있는 멤버는 적용을 통해 하위 클래스 생성자에서 구현할 수 있습니다.

  function inherit(init, Parent, proto){
    function Son(){
      Parent.apply(this, argument); //先继承父类的特权成员
      init.apply(this, argument); //在执行自己的构造器
    }
  }
  //由于Object.create是我们伪造的,因此避免使用第二个参数
  Son.prototype = Object.create(Parent.prototype,{});
  Son.prototype.toString = Parent.prototype.toString; //处理IEbug
  Son.prototype.valueOf = Parent.prototype.valueOf; //处理IEbug
  Son.prototype.constructor = Son; //确保构造器正常指向,而不是Object
  extend(Son, proto) ;//添加子类的特有的原型成员
  return Son;

로그인 후 복사

下面,做一组实验,测试下实例的回溯机制。当我们访问对象的一个属性,那么他先寻找其特权成员,如果有同名就返回,没有就找原型,再没有,就找父类的原型...我们尝试将它的原型临时修改下,看它的属性会变成那个。

  function A(){

  }
  A.prototype = {
    aa:1
  }
  var a = new A;
  console.log(a.aa) ; //=>1

  //将它的所有原型都替换掉
  A.prototype = {
    aa:2
  }
  console.log(a.aa); //=>1

  //于是我们想到每个实例都有一个constructor方法,指向其构造器
  //而构造器上面正好有我们的原型,javascript引擎是不是通过该路线回溯属性呢
  function B(){

  }
  B.prototype = {
    aa:3
  }
  a.constructor = B;
  console.log(a.aa) //1 表示不受影响

로그인 후 복사

因此类的实例肯定通过另一条通道进行回溯,翻看ecma规范可知每一个对象都有一个内部属性[[prototype]],它保存这我们new它时的构造器所引用的Prototype对象。在标准浏览器与IE11里,它暴露了一个叫__proto__属性来访问它。因此,只要不动__proto__上面的代码怎么动,a.aa始终坚定不毅的返回1.

再看一下,new时操作发生了什么。

1.创建了一个空对象 instance
2.instance.__proto__ = intanceClass.prototype
3.将构造函数里面的this = instance
4.执行构造函数里的代码
5.判定有没有返回值,没有返回值就返回默认值为undefined,如果返回值为复合数据类型,则直接返回,否则返回this
于是有了下面的结果。

  function A(){
    console.log(this.__proto__.aa); //1
    this.aa = 2
  }
  A.prototype = {aa:1}
  var a = new A;
  console.log(a.aa)
  a.__proto__ = {
    aa:3
  }
  console.log(a.aa) //=>2
  delete a. aa; //删除特权属性,暴露原型链上的同名属性
  console.log(a.aa) //=>3

로그인 후 복사

有了__proto__,我们可以将原型设计继承设计得更简单,我们还是拿上面的例子改一改,进行试验

  function A() {}
  A.prototype = {
    aa:1
  }
  function bridge() {}
  bridge.prototype = A.prototype;

  function B(){}
  B.prototype = new bridge();
  B.prototype.constructor = B;
  var b = new B;
  B.prototype.cc = function(){
    alert(3)
  }
  //String.prototype === new String().__proto__ => true
  console.log(B.prototype.__proto__ === A.prototype) //true
  console.log(b.__proto__ == B.prototype); //true 
  console.log(b.__proto__.__proto__ === A.prototype); //true 得到父类的原型对象

로그인 후 복사

因为b.__proto__.constructor为B,而B的原型是从bridge中得来的,而bride.prototype = A.prototype,反过来,我们在定义时,B.prototype.__proto__ = A.prototype,就能轻松实现两个类的继承.

__proto__属性已经加入es6,因此可以通过防止大胆的使用

2.各种类工厂的实现。

上节我们演示了各种继承方式的实现,但都很凌乱。我们希望提供一个专门的方法,只要用户传入相应的参数,或按照一定简单格式就能创建一个类。特别是子类。

由于主流框架的类工厂太依赖他们庞杂的工具函数,而一个精巧的类工厂也不过百行左右

相当精巧的库,P.js

https://github.com/jiayi2/pjs

使用版:https://github.com/jiayi2/factoryjs

这是一个相当精巧的库,尤其调用父类的同名方法时,它直接将父类的原型抛在你面前,连_super也省了。

  var P = (function(prototype, ownProperty, undefined) {
 return function P(_superclass /* = Object */, definition) {
  // handle the case where no superclass is given
  if (definition === undefined) {
   definition = _superclass;
   _superclass = Object;
  }

  // C is the class to be returned.
  //
  // When called, creates and initializes an instance of C, unless
  // `this` is already an instance of C, then just initializes `this`;
  // either way, returns the instance of C that was initialized.
  //
  // TODO: the Chrome inspector shows all created objects as `C`
  //    rather than `Object`. Setting the .name property seems to
  //    have no effect. Is there a way to override this behavior?
  function C() {
   var self = this instanceof C ? this : new Bare;
   self.init.apply(self, arguments);
   return self;
  }

  // C.Bare is a class with a noop constructor. Its prototype will be
  // the same as C, so that instances of C.Bare are instances of C.
  // `new MyClass.Bare` then creates new instances of C without
  // calling .init().
  function Bare() {}
  C.Bare = Bare;

  // Extend the prototype chain: first use Bare to create an
  // uninitialized instance of the superclass, then set up Bare
  // to create instances of this class.
  var _super = Bare[prototype] = _superclass[prototype];
  var proto = Bare[prototype] = C[prototype] = C.p = new Bare;

  // pre-declaring the iteration variable for the loop below to save
  // a `var` keyword after minification
  var key;

  // set the constructor property on the prototype, for convenience
  proto.constructor = C;

  C.extend = function(def) { return P(C, def); }

  return (C.open = function(def) {
   if (typeof def === 'function') {
    // call the defining function with all the arguments you need
    // extensions captures the return value.
    def = def.call(C, proto, _super, C, _superclass);
   }

   // ...and extend it
   if (typeof def === 'object') {
    for (key in def) {
     if (ownProperty.call(def, key)) {
      proto[key] = def[key];
     }
    }
   }

   // if no init, assume we're inheriting from a non-Pjs class, so
   // default to using the superclass constructor.
   if (!('init' in proto)) proto.init = _superclass;

   return C;
  })(definition);
 }

 // as a minifier optimization, we've closured in a few helper functions
 // and the string 'prototype' (C[p] is much shorter than C.prototype)
})('prototype', ({}).hasOwnProperty);

로그인 후 복사

我们尝试创建一个类:

  var Dog = P (function(proto, superProto){
    proto.init = function(name) { //构造函数
      this.name = name;
    }
    proto.move = function(meters){ //原型方法
      console.log(this.name + " moved " + meters + " m.")
    }
  });
  var a = new Dog("aaa")
  var b = new Dog("bbb"); //无实例变化
  a.move(1);
  b.move(2);

로그인 후 복사

我们在现在的情况下,可以尝试创建更简洁的定义方式

  var Animal = P (function(proto, superProto){
    proto.init = function(name) { //构造函数
      this.name = name;
    }
    proto.move = function(meters){ //原型方法
      console.log(this.name + " moved " + meters + " m.")
    }
  });
  var a = new Animal("aaa")
  var b = new Animal("bbb"); //无实例变化
  a.move(1);
  b.move(2);
  //...............
  var Snake = P (Animal, function(snake, animal){
    snake.init = function(name, eyes){
      animal.init.call(this, arguments); //调运父类构造器
      this.eyes = 2;
    }
    snake.move = function() {
      console.log('slithering...');
      animal.move.call(this, 5); //调运父类同名方法
    }
  });
  var s = new Snake("snake", 1);
  s.move();
  console.log(s.name);
  console.log(s.eyes);

로그인 후 복사

私有属性演示,由于放在函数体内集中定义,因此安全可靠!

  var Cobra = P (Snake, function(cobra){
    var age = 1;//私有属性
    //这里还可以编写私有方法
    cobra.glow = function(){ //长大
      return age++;
    }
  });
  var c = new Cobra("cobra");
  console.log(c.glow()); //1
  console.log(c.glow()); //2
  console.log(c.glow()); //3
로그인 후 복사

以上所述就是本文的全部内容了,希望大家能够喜欢。

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