Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

thief系列之二:从获取DOM和增删类中看js如何构造一个类 #63

Open
youngwind opened this issue Apr 17, 2016 · 0 comments
Labels

Comments

@youngwind
Copy link
Owner

youngwind commented Apr 17, 2016

要解决的问题

上一节 #60 我们已经知道如何使用IIFE和闭包来处理模块化的问题。现在我们来尝试实现两个功能。

  1. 获取DOM元素
  2. 添加删除类

我们回想一下,jquery完成上面的功能大概是这样的。

$('selector');
$('selector').addClass('className');
$('selector').removeClass('className');

我们分析一下。

  1. $函数接受选择器作为参数,返回DOM元素。
  2. 返回的DOM元素拥有addClass和removeClass方法。

工厂模式

ok,上最基础的工厂模式。

var T = function (selector) {

    // 获取DOM元素
    var dom = document.querySelector(selector);

    // 添加类
    dom.addClass = function (className) {
      this.classList.add(className);
    };

    // 删除类
    dom.removeClass = function (className) {
      this.classList.remove(className)
    };
    return dom;
  };

缺点

第一,返回的DOM元素无法知道它是从T这个构造函数中来的。
证据:

typeof T('h1') === T;
// return false;

你可能会说,你这不废话吗!这里只用了工厂模式,压根没构造模式,哪儿来的构造函数!
没错,不过我们看看jquery

typeof $('h1') === jQuery
// return true;

看见没?我们现在姑且可以认为jQuery那样做的好处在于起码我知道实例化的对象是从哪儿来的。

第二,每个dom实例的addClass和removeClass方法都会在内存中存一份,并不能共享,造成资源上的浪费。证明依据。

T('h2').addClass === T('h1').addClass
// return false

构造模式

我们先来尝试解决工厂模式的第一个缺点。

// 构造模式
  var T = function (selector) {

    // 获取DOM元素
    this.dom = document.querySelector(selector);

    // 添加类
    this.addClass = function (className) {
      this.dom.classList.add(className);
    };

    // 删除类
    this.removeClass = function (className) {
      this.dom.classList.remove(className);
    };
  };

验证

new T('h1') instanceof T
// return true;

发现第一个问题已经解决。我们来看第二个问题,函数复用问题。
我们把addClass和removeClass抽象成全局函数(注意,这里的全局是指自执行表达式里面的)

(function () {

  function addClass(className) {
    this.dom.classList.add(className);
  }

  function removeClass(className) {
    this.dom.classList.remove(className);
  }

  // 构造模式
  var T = function (selector) {

    // 获取DOM元素
    this.dom = document.querySelector(selector);

    // 添加类
    this.addClass = addClass;

    // 删除类
    this.removeClass = removeClass;
  };

  window.T = T;
})(window);

验证:

new T('h1').addClass === new T('h1').addClass
// return true;

缺点

addClass和removeClass被声明为全局。虽然我们通过闭包将这种影响限制在模块内,但是这样子做也总感觉失去了对象的封装性。

原型模式

为了解决构造模式的全局函数声明问题,我们使用prototype来实现所谓的原型模式
shit.....忽然发现光用原型模式居然没法搞出一个demo来。比如一般的原型模式是这样的。

function T(selector) {
  }

  T.prototype.dom = document.querySelector(selector);
  T.prototype.addClass = function (className) {
    this.dom.classList.add(className)
  };
  T.prototype.removeClass = function (className) {
    this.dom.classList.remove(className)
  };

实际上这是会报错的

Uncaught ReferenceError: selector is not defined

由此我们可以看到原型模式的一个重大缺陷:无法传递参数,也就是说每次构造出来的对象只能是一样的,这能忍?
当然,它还有另外一个重大缺陷,那就是传递引用。由于这里还不涉及到,就不说了。

混合模式

原型模式走不通,原因是因为不能传递参数。但是前面的构造函数可以传参啊,所以结合两者就是混合模式。

// 混合模式 = 构造模式   原型模式

  // 构造模式定义属性
  function T(selector) {
    this.dom = document.querySelector(selector);
  }

  // 原型模式定义方法
  T.prototype.addClass = function (className) {
    this.dom.classList.add(className)
  };

  T.prototype.removeClass = function (className) {
    this.dom.classList.remove(className)
  };

如何把new去掉?

目前我们已经做了很多了,但是还不够。比如,看下面的对比。

$('selector')
new T('selector')

jquery实例化的时候是不需要new关键字的,而我们的T因为包含构造模式,所以得使用new。那么jquery是如何把new去掉的呢?(当然,new $('selector')也是可以执行的)
这里有一篇文章讲得很不错,http://www.cnblogs.com/aaronjs/p/3278578.html
下面我直接给出改造过后的代码

function T(selector) {
    return new T.prototype.init(selector);
  }

  T.prototype = {
    init: function (selector) {
      this.dom = document.querySelector(selector);
      return this;
    },
    addClass: function (className) {
      this.dom.classList.add(className);
    },
    removeClass: function (className) {
      this.dom.classList.remove(className);
    }
  };

  T.prototype.init.prototype = T.prototype;
@youngwind youngwind changed the title thief系列之二:从实现获取DOM和添加删除类中看js如何构造一个类 thief系列之二:从获取DOM和增删类中看js如何构造一个类 Apr 17, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant