1. 程式人生 > >underscore.js原始碼解析之繼承

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中繼承方式非常多,不過也只能分成三大類,原型鏈、建構函式鏈和屬性深淺拷貝。