资讯专栏INFORMATION COLUMN

读 arale 源码之 class 篇

firim / 1034人阅读

摘要:拥有了和方法的三个变种属性这三个属性会做特殊处理继承的方法,只支持单继承建立原型链来实现继承强制改变构造函数提供语法糖,来调用父类属性混入属性,可以混入多个类的属性将参数变成数组无论参数是类,还是对象,都混入。

更新:读 arale 源码之 attribute 篇

arale 是阿里、开源社区明星人物--玉伯,开发的一套组件,代码相当优美,大赞玉伯的开源精神,我是您的粉丝。

这里分享下我对这段源代码的感悟,若有错误的地方,烦请指正。= ̄ω ̄=

先谈谈基于原型的继承。

先看看 segementfault 上讨论的一道题。

function F() {}
Object.prototype.a = function() {}
Function.prototype.b = function() {}
var f = new F()
// F.a F.b f.a

F 可以调用 a 和 b,因为 F 的原型链是这样的。(直观解释:F 是 Function 实例,F 继承自 Object)

F ----> Function.prototype ----> Object.prototype ----> null

//既 F.__proto__ === Function.prototype
// Function.prototype.__proto__ === Object.prototype
// Object.prototype.__proto__ === null

而 f 只能调用 a , f 的原型链是这样的。(直观解释:f 是 F 的实例,一切皆对象,f 继承自 Object)

f ----> F.prototype ----> Object.prototype ----> null

//既 f.__proto__ === F.prototype
// F.prototype.__proto__ === Object.prototype
// Object.prototype.__proto__ === null

在 f 的原型链上并没有 Function.prototype . 因此访问不到 b 。

注意,访问原型对象 __proto__ 是非标准方法,ES5 标准方法是 Object.getPrototypeOf();

到这里,基于原型链的继承已经很明显了,只需要

function Animal() {}

function Dog() {}

Dog.prototype.__proto__ = Animal.prototype;

var dog = new Dog();

// dog.__proto__ --> Dog.prototype;
// dog.__proto__.__proto__ --> Animal.prototype

基于 ES5 标准写法是

Dog.prototype = Object.create(Animal.prototype);
来看看 arale 的封装
// 创建原型链
function Ctor() {};

var createProto = Object.__proto__ ? function(proto) {
  return {
    __proto__: proto
  }
} : function(proto) {
  Ctor.prototype = proto;
  return new Ctor();
}

有三种写法可以实现原型链继承,但是我测试 new Ctor 是最慢的啊,Object.creaete 其次,__proto__ 是最快的。

function Ctor() {}

function getProto1(proto, c) {
  Ctor.prototype = proto;
  var o = new Ctor();
  o.constructor = c;
  return o;
}

function getProto2(proto, c) {
  return Object.create(proto, {
    constructor: {
      value: c
    }
  })
}

function getProto3(proto, c) {
  return {
    __proto__: proto,
    constructor: c
  }
}

接着往下看。。。

function Class(o) {
  if (!(this instanceof Class) && isFunction(o)) {
    return classify(o);
  }
}

function classify(cls) {
  cls.extend = Class.extend;
  cls.implement = implement;
  return cls;
}

这种写法是,当不使用 new 关键字调用时,将参数 类化,如:

修改:是不支持 new 的方式调用。

function Animal() {}
Animal.prototype.talk = function() {}

//Class(Animal); Animal 拥有了 extend 和 implement 方法
var Dog = Class(Animal).extend({
  swim: function() {}
})

Class 的三个变种属性 Extends Implements Statics

这三个属性会做特殊处理

Class.Mutators = {
  // 继承的方法,只支持单继承
  Extends: function(parent) {
    var existed = this.prototype;
    // 建立原型链来实现继承
    var proto = createProto(parent.prototype);
    mix(proto, existed);
    // 强制改变构造函数
    proto.constructor = this;
    this.prototype = proto;
    // 提供 superclass 语法糖,来调用父类属性
    this.superclass = parent.prototype;
  },
  // 混入属性,可以混入多个类的属性
  Implements: function(items) {
    // 将参数变成数组
    isArray(items) || (items = [ items ]);
    var proto = this.prototype, item;
    while (item = items.shift()) {
      // 无论参数是 类(Function),还是 对象(Object),都混入。
      mix(proto, item.prototype || item);
    }
  },
  Statics: function(staticProperties) {
    // 直接混入静态属性。
    mix(this, staticProperties);
  }
}

再来看看 implement 方法, 它是用来混入属性的。

三个变种属性将被执行

function implement(properties) {
  var key, value;
  for (key in properties) {
    value = properties[key];
    if (Class.Mutators.hasOwnProperty(key)) {
      Class.Mutators[key].call(this, value);
    } else {
      this.prototype[key] = value;
    }
  }
}

好了,最关键的方法 Class.create 来了,它是用来创建类的,可以指定父类。

Class.create = function(parent, properties) {
  // 如果没有指定父类,父类就是 Class
  if (!isFunction(parent)) {
    properties = parent;
    parent = null;
  }
  properties || (properties = {});
  // 如果指定了 Extends 属性, 父类就是它了
  parent || (parent = properties.Extends || Class);
  properties.Extends = parent;
  // 创建子类的构造函数
  function SubClass() {
    // 调用父类的构造函数
    parent.apply(this, arguments);
    // 仅调用自身的初始化方法,initialize
    if (this.constructor === SubClass && this.initialize) {
      this.initialize.apply(this, arguments);
    }
  }
  // 指定父类的情况下,继承父类的属性
  if (parent !== Class) {
    Mix(SubClass, parent, parent.StaticsWhiteList);
  }
  // 为子类添加实例属性,三个特殊属性,在这里被执行
  implement.call(SubClass, properties);
  // 返回可继续 继承 的子类
  return classify(SubClass);
}

最后来看看继承的方法 Class.extend ,被 classify 的类,都可以继续创建子类。

Class.extend = function(properties) {
  properties || (properties = {});
  // 指定父类为调用者
  properties.Extends = this;
  return Class.create(properties);
}

最后的最后,简单介绍它的工具类,Helpers

// 属性混合,增加白名单限制
function mix(r, s, wl) {
  for (var p in s) {
    // 最佳实践:任何 for in 循环都要带上 hasOwnProperty。除非你想遍历原型
    if (s.hasOwnProperty(p)) {
      if (wl && indexOf(wl, p) === -1) continue;
      if (p !== "prototype") {
        r[p] = s[p];
      }
    }
  }
}

// [].indexOf 是 ES5 加入的,并非所有浏览器都支持。
// 这里没有也不需要使用 polyfill 的方式。
var indexOf = Array.prototype.indexOf ? function(arr, item) {
  return arr.indexOf(item);
} : function(arr, item) {
  for (var i = 0, len = arr.length; i < len; i++) {
    if (arr[i] === item) {
      return i;
    }
  }
  return -1;
}

// 这个很简单,只有 Object.prototype.toString 才能知道它的 [[class]]
var toString = Object.prototype.toString;
var isArray = Array.isArray || function(val) {
  return toString.call(val) === "[object Array]";
}
var isFunction = function(val) {
  return toString.call(val) === "[object Function]";
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/85987.html

相关文章

  • arale 源码 attribute

    摘要:系列文章读源码之篇提供基本的属性添加获取移除等功能。判断是否为等对象特性检查闭包实现块作用域,不污染全局变量。找这个属性,若没有则返回空对象执行函数,返回被修改的值。 系列文章:读 arale 源码之 class 篇 attributes 提供基本的属性添加、获取、移除等功能。它是与实例相关的状态信息,可读可写,发生变化时,会自动触发相关事件 先来了解一下 Attribute 模块要实...

    Magicer 评论0 收藏0
  • Arale源码解析(3)——Base模块和Aspect模块

    摘要:本文同步自我的博客前言这个模块实际上才是模块系统中对外的模块,它包含了之前介绍的类和类,以及自己内部的模块和模块,因此模块是真正的基础类。这两个方法的作用就是针对类上的某个方法,给这个方法绑定先于其执行和后于其执行的回调函数。 本文同步自我的GitHub博客 前言 Base这个模块实际上才是Arale模块系统中对外的模块,它包含了之前介绍的Class类和Events类,以及自己内部...

    stdying 评论0 收藏0
  • Arale源码解析(1)——Class

    摘要:先来看源码中,首先是做的是参数的处理工作,针对某些参数未传的情况作了调整,最后达到的效果是的值为传入的父类构造函数,如果没有,设为。下一个语句其作用是处理父类构造函数没有修改的属性值并且有方法的时候,在上调用方法。 本文同步自我的GitHub 概述 Arale是支付宝开发的一套基础类库,提供了一整套前端模块架构,基于CMD规范,所有模块均是以sea.js的标准进行开发。其开发过程借...

    _ivan 评论0 收藏0
  • js 支持 Aspect 切面编程

    摘要:在方法执行后,再执行函数函数在执行时,接收的参数第一个是的返回值,之后的参数和传给相同。的返回值源码定义两个出口定义一个可柯里化的函数,柯里化成函数指向基于生成的类的实例,如上例的如果该函数是第一次切面化绑定,则包装该函数。 系列文章:读 arale 源码之 class 篇 使用 Aspect,可以允许你在指定方法执行的前后插入特定函数 before object.before(me...

    zhaot 评论0 收藏0
  • 2015 Hackathon 杭州站回顾BugLife

    摘要:今天我偷偷的去上喵了几眼,发现我捉出来的,还没被消灭掉,看来有必要发个邮件给高阳了。还得到了高阳的饭约,因为我们捉了好几个的,高阳你别忘啊下面就上几张我们的的靓照吧的获奖自之后,我们还是很有自信能得个奖的。 BugLife的诞生记 先自我介绍下我们团队,Wellming、Bell orchid、Retamia,三枚程序员,主要技能点PHP,目前在一家棒棒的做开源网校系统(EduSoho...

    SmallBoyO 评论0 收藏0

发表评论

0条评论

firim

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<