underscore.js源碼解析之繼承

1. 引言

  underscore.js是一個1500行左右的Javascript函數式工具庫,裏面提供了很多實用的、耦合度極低的函數,用來方便的操作Javascript中的數組、對象和函數,它支持函數式和麪向對象鏈式的編程風格,還提供了一個精巧的模板引擎。理解underscore.js的源碼和思想,不管是新手,還是工作了一段時間的人,都會上升一個巨大的臺階。雖然我不搞前端,但是在一個星期的閱讀分析過程中,仍然受益匪淺,決定把一些自認爲很有意義的部分記錄下來。

2.繼承方式

  Javascript中實現繼承有很多種方式,比如原型鏈、構造函數鏈、屬性深淺拷貝等,underscore中主要使用了兩種繼承方式,分別是淺拷貝繼承原型鏈繼承

1.原型鏈繼承

var Ctor = function() {};
var baseCreate = function(prototype) {
  //_.isObject判斷typeof 爲object的非null、NaN和undefined類型
  if (!_.isObject(prototype)) return {};
  if (nativeCreate) return nativeCreate(prototype);//如果有原生的Object.create()就用原生的

  Ctor.prototype = prototype;
  var result = new Ctor;
  Ctor.prototype = null;
  return result;
};

看後面那部分,underscore用一個空構造函數Ctor做中間對象,將其原型指向被繼承對象,new Ctor()將產生一個繼承後的實例,這和原生的Object.create()是一樣的,但是完事後,將Ctor的原型變成null,不僅釋放空間,下次繼承時還能夠複用,這是很不錯的技巧。

2.淺拷貝繼承

//生成一個函數的函數,keysFunc指的是_.keys和_.allKeys,都是返回一個對象的屬性數組,只不過後者會包含原型鏈上的屬性。
var createAssigner = function(keysFunc, undefinedOnly) {
  return function(obj) { //這個函數可以傳入多個對象,將它們所有的屬性全部拷貝到一個第一個參數obj上
    var length = arguments.length;
    if (length < 2 || obj == null) return obj;
    for (var index = 1; index < length; index++) {//下標從第2個開始
      var source = arguments[index],
          keys = keysFunc(source),//獲取屬性數組
          l = keys.length;
      for (var i = 0; i < l; i++) {
        var key = keys[i];
        //keysFunc和undefinedOnly可以控制拷貝的具體方式,
        //1、只拷貝obj中沒有的屬性  2、不管obj中有沒有這個屬性,都將屬性值拷貝覆蓋過去。
        if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key];
      }
    }
    return obj;
  };
};

這種產生函數的函數在js中非常常見,外圍函數傳參給內部閉包用來配置或者佔位,是典型的函數式風格。
在返回的函數中,可以實現將多個對象的所有屬性拷貝到第一個對象中,實現 多重繼承,由於是淺拷貝,父對象中primitive type的屬性值發生變化,子對象不會受到影響(如果是深拷貝,則任何類型的屬性值都不會影響),繼承在Java,C++等語言中讓人詬病的因素之一就是父類一旦發生變化,子類也會隨之改變。深拷貝繼承沒有這個問題,但是如果屬性非常多,開銷會較大。

//三種不同的繼承實現函數

//原型鏈上的屬性也會繼承,如果屬性名相同則會覆蓋
_.extend = createAssigner(_.allKeys);

//原型鏈上的屬性不會繼承,只繼承對象本身的屬性,如果屬性名相同會覆蓋
_.extendOwn = _.assign = createAssigner(_.keys);

//原型鏈上的屬性也會繼承,只繼承屬性名不相同的屬性
_.defaults = createAssigner(_.allKeys, true);

//這相當於Object.create,不過可以添加額外的屬性和值
_.create = function(prototype, props) {
  var result = baseCreate(prototype);
  if (props) _.extendOwn(result, props);
  return result;
};

//利用淺拷貝繼承實現的淺克隆
_.clone = function(obj) {
  if (!_.isObject(obj)) return obj;
  //slice是數組淺拷貝的好辦法
  return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
};

3.總結

  1. 如果可能發生多次繼承,可以預先定義一個空構造函數,每次用完將其原型指向null。
  2. Javascript中繼承方式非常多,不過也只能分成三大類,原型鏈、構造函數鏈和屬性深淺拷貝。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章