javascript中继承的实现

基础实现

function Range(from,to){
this.from =from;
this.to =to;
}
Range.prototype = {
includes:function(x){
return this.from<=x &&this.to >=x;
},
foreach:function(f){
for(var x = Math.ceil(this.from);x<=this.to;x++){
f(x);
}
},
toString:function(){
return "("+this.from+"..."+this.to+")";
}
}
var r= new Range(1,3);
console.log(r.includes(2));
// 可以借鉴下面这种用法
r.foreach(alert); // 弹出三次
console.log(r.toString());

线上链接

寄生组合继承

function SuperType() {
this.property = true;
}
SuperType.prototype.superMethod = function () {
} function LowType() {
SuperType.call(this);
this.lowProperty = true;
}
function inheritPrototype(subType, superType) {
var prototype1 = object(superType.prototype);
prototype1.constructor = subType;
subType.prototype = prototype1;
}
inheritPrototype(LowType, SuperType);
LowType.prototype.lowMethod = function () {
};
var lowType = new LowType();

线上链接。线上链接主要讲最后一个例子,寄生组合继承,其实这里命名,在我后来都没遇到

还有,如果实例对应的类原型的引用变化了,这个实例指向的原型会变吗?不会的。

最优雅的写法

/* @article https://johnresig.com/blog/simple-javascript-inheritance/
* Simple JavaScript Inheritance
* By John Resig http://ejohn.org/
* MIT Licensed.
*/ // Inspired by base2 and Prototype
(function () {
var initializing = false, fnTest = /xyz/.test(function () {
xyz;
}) ? /\b_super\b/ : /.*/;
// The base Class implementation (does nothing)
this.Class = function () {
}; // Create a new Class that inherits from this class
Class.extend = function (prop) {
var _super = this.prototype;
// Instantiate a base class (but only create the instance,
// don't run the init constructor)
initializing = true;
var prototype = new this();
initializing = false; // Copy the properties over onto the new prototype
for (var name in prop) {
// Check if we're overwriting an existing function
prototype[name] = typeof prop[name] == "function" &&
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
(function (name, fn) {
return function () {
var tmp = this._super;
// Add a new ._super() method that is the same method
// but on the super-class
this._super = _super[name]; // The method only need to be bound temporarily, so we
// remove it when we're done executing
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]) :
prop[name];
} // The dummy class constructor
function Class() {
// All construction is actually done in the init method
if (!initializing && this.init)
this.init.apply(this, arguments);
} // Populate our constructed prototype object
Class.prototype = prototype;
// Enforce the constructor to be what we expect
Class.prototype.constructor = Class;
// And make this class extendable
Class.extend = arguments.callee;
return Class;
};
})();

线上链接。这个继承是大牛写的,是我见过最简洁写法了,但是在工作中,一直没看到有人在子类方法内,要用到父类方法,直接使用this._super方法,还有init方法construction,也只会调用一次。继承中构造函数保持在原型链中。这个实现不支持继承静态方法,但以后如果有需求,我会尽量用这个方法。

最繁重的写法

var Class = (function () {
// Class
// -----------------
// Thanks to:
// - http://mootools.net/docs/core/Class/Class
// - http://ejohn.org/blog/simple-javascript-inheritance/
// - https://github.com/ded/klass
// - http://documentcloud.github.com/backbone/#Model-extend
// - https://github.com/joyent/node/blob/master/lib/util.js
// - https://github.com/kissyteam/kissy/blob/master/src/seed/src/kissy.js // The base Class implementation.
function Class(o) {
// Convert existed function to Class.
if (!(this instanceof Class) && isFunction(o)) {
return classify(o)
}
} // Create a new Class.
//
// var SuperPig = Class.create({
// Extends: Animal,
// Implements: Flyable,
// initialize: function() {
// SuperPig.superclass.initialize.apply(this, arguments)
// },
// Statics: {
// COLOR: 'red'
// }
// })
//
Class.create = function (parent, properties) {
if (!isFunction(parent)) {
properties = parent
parent = null
} properties || (properties = {})
parent || (parent = properties.Extends || Class)
properties.Extends = parent // The created class constructor
function SubClass() {
// Call the parent constructor.
parent.apply(this, arguments) // Only call initialize in self constructor.
if (this.constructor === SubClass && this.initialize) {
this.initialize.apply(this, arguments)
}
} // Inherit class (static) properties from parent.
if (parent !== Class) {
mix(SubClass, parent, parent.StaticsWhiteList)
} // Add instance properties to the subclass.
implement.call(SubClass, properties) // Make subclass extendable.
return classify(SubClass)
} 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
}
}
} // Create a sub Class based on `Class`.
Class.extend = function (properties) {
properties || (properties = {})
properties.Extends = this return Class.create(properties)
} function classify(cls) {
cls.extend = Class.extend
cls.implement = implement
return cls
} // Mutators define special properties.
Class.Mutators = { 'Extends': function (parent) {
var existed = this.prototype
var proto = createProto(parent.prototype) // Keep existed properties.
mix(proto, existed) // Enforce the constructor to be what we expect.
proto.constructor = this // Set the prototype chain to inherit from `parent`.
this.prototype = proto // Set a convenience property in case the parent's prototype is
// needed later.
this.superclass = parent.prototype
}, 'Implements': function (items) {
isArray(items) || (items = [items])
var proto = this.prototype, item while (item = items.shift()) {
mix(proto, item.prototype || item)
}
}, 'Statics': function (staticProperties) {
mix(this, staticProperties)
}
} // Shared empty constructor function to aid in prototype-chain creation.
function Ctor() {
} // See: http://jsperf.com/object-create-vs-new-ctor
var createProto = Object.__proto__ ?
function (proto) {
return {__proto__: proto}
} :
function (proto) {
Ctor.prototype = proto
return new Ctor()
} // Helpers
// ------------ function mix(r, s, wl) {
// Copy "all" properties including inherited ones.
for (var p in s) {
if (s.hasOwnProperty(p)) {
if (wl && indexOf(wl, p) === -1) continue // 在 iPhone 1 代等设备的 Safari 中,prototype 也会被枚举出来,需排除
if (p !== 'prototype') {
r[p] = s[p]
}
}
}
} 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]'
} 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
} return Class
})()

线上链接。这个继承是功能最全面的,包括类实现里,怎么实现静态方法,但是它的语法糖太多了,ExtendsImplementsStaticsinitialize,继承中的构造函数提出来,放在当前类的构造函数中执行,new初始化时的钩子,是initialize,只会调用一次,继承中initialize也可以调用。

brix里的实现

// http://g.alicdn.com/thx/brix-release/1.0.0-beta.9/brix-base/dist/base-debug.js

var _ = {}
var toString = Object.prototype.toString _.each = function (obj, iterator, context) {
if (obj === null || obj === undefined) return obj
if (obj.forEach) {
obj.forEach(iterator, context);
} else if (obj.length === +obj.length) {
for (var i = 0, length = obj.length; i < length; i++) {
iterator.call(context, obj[i], i, obj)
}
} else {
for (var prop in obj) {
iterator.call(context, obj[prop], prop, obj)
}
}
return obj
} _.each('Boolean Number String Function Array Date RegExp Object Error'.split(' '), function (name) {
_['is' + name] = function (obj) {
return toString.call(obj) == '[object ' + name + ']'
}
})
_.extend = function () {
var target = arguments[0]
var index = 1
var length = arguments.length
var deep = false
var options, name, src, copy, clone if (typeof target === "boolean") {
deep = target
target = arguments[index] || {}
index++
} if (typeof target !== "object" && typeof target !== "function") {
target = {}
} if (length === 1) {
target = this
index = 0
} for (; index < length; index++) {
options = arguments[index]
if (!options) continue for (name in options) {
src = target[name]
copy = options[name] if (target === copy) continue
if (copy === undefined) continue if (deep && (_.isArray(copy) || _.isObject(copy))) {
if (_.isArray(copy)) clone = src && _.isArray(src) ? src : []
if (_.isObject(copy)) clone = src && _.isObject(src) ? src : {} target[name] = _.extend(deep, clone, copy)
} else {
target[name] = copy
}
}
} return target
} /*
This function is loosely inspired by Backbone.js.
http://backbonejs.org
*/
function extend(protoProps, staticProps) {
var parent = this // 构造函数 Initialize constructor
var constructor = protoProps && protoProps.hasOwnProperty('constructor') ?
protoProps.constructor : // 自定义构造函数 Custom constructor
parent // 父类构造函数 Base constructor // 子类 Subclass
var child = function () {
parent.__x_created_with = child.__x_created_with
var instance = constructor.apply(this, arguments) || this // instance.options vs parameter options
var options = arguments[0]
if (options && !instance.hasOwnProperty('options')) {
instance.options = _.extend(true, {}, this.options, options)
} // 如果模块带有 __x_created_with,则一切初始化行为都交给第三方;否则调用 .create() 方法。
// If the child module has a property named as __x_created_with, the third-library will be response for the rest of initialization actions.
// If not, the child module will call the `.create()`.
if (!child.__x_created_with && instance.created && instance.constructor === child) {
instance.created.apply(instance, instance.created.length ? [instance.options] : []) // @edit
} return instance
} // 静态属性 Static properties
_.extend(child, parent, staticProps) // 原型链 Build prototype chain.
var Surrogate = function () {
// this.constructor = constructor // @remove
}
Surrogate.prototype = parent.prototype
child.prototype = new Surrogate()
child.prototype.constructor = child // @add // 原型属性 Copy prototype properties from the parameter protoProps to the prototype of child
if (protoProps) _.extend(child.prototype, protoProps) // Add keyword __super__
child.__super__ = parent.prototype child.extend = extend return child
} function Brix() {
}
Brix.prototype = {
created: function () {
if (this.init) this.init()
if (this.render) this.render()
},
constructor: Brix // @add
}
Brix.extend = extend

Brix Base里关于继承的实现,因为为了支持框架Brix Loader,所以继承里做了特殊处理,比如options参数,比如__x_created_with,继承中的构造函数提出来,放在当前类的构造函数中执行,new时初始化钩子,是created方法调用init、render,所以初始化语句可以放在init、render,按正常结果只调用一次,但这里调用了两次,重复了。

magix里实现

magix中View如何实现继承?也是类似brix,但它不包含任何语法糖,new初始化钩子,是

View.extend = function(props, statics, ctor) {

第三个参数ctor是初始化钩子,这里会多次调用

总结

为了实现类似Person.extend能生成新类,extend要向封装传入原型上方法,父类,静态方法,每次执行extend时要定义新类的构造函数。返回的新类也能extend再生成子类。

封装内,将父类的构造函数和原型拆分。新类的构造函数执行父类的构造函数,新类的原型指向Object.create(父类的原型)。构造函数在实例化时才执行,原型链实现,在实现继承时就执行。

新类的构造函数中执行父类的构造函数,父类的构造函数再执行祖先的构造函数...,直到最顶层。最顶层构造函数可自由实现。在simple-inherit中是封装外面的Class,在brix中是首次直接赋值了extend方法的类。complex-inherit中,是由Class.create传入的。

实例化类的时候,要自动执行初始化的操作,正常是在构造函数中实现的,extend封装里的构造函数是统一写死的,无法自由做一些初始化,要在构造函数里提供个钩子,构造函数执行这个钩子函数,钩子函数,在定义新类的时候传入,也就是Person.extend时传入。

钩子一般是定义在原型上的某方法,比如initialize方法,因为构造函数里是调父类的构造函数,一直到最顶层基类,也要多次执行构造函数内钩子吗?钩子是这样执行的this.initialize.apply(this, arguments),this在每次执行时都不变的,也没必要多次执行,只要执行一次即可。并且只执行原型链中,第一个有钩子方法的原型,剩下的都不执行了。

实现原型链继承时,要注意原型的constructor属性,为了子类方法能调父类方法,给每个类加个superclass属性,指向父类的原型对象。

mix

	// https://g.alicdn.com/mm/pubplus/0.4.12/app_debug/exts/arale/class.js
function mix(r, s, wl) {
// Copy "all" properties including inherited ones.
for (var p in s) {
if (s.hasOwnProperty(p)) {
if (wl && indexOf(wl, p) === -1) continue // 在 iPhone 1 代等设备的 Safari 中,prototype 也会被枚举出来,需排除
if (p !== 'prototype') {
r[p] = s[p]
}
}
}
}

extend在库里实现

underscore

将后面对象的自有可枚举属性,拷贝到第一个对象,并返回第一个对象

  // http://g.alicdn.com/thx/brix-release/1.0.0-beta.9/underscore/underscore.js
// Extend a given object with all the properties in passed-in object(s).
_.extend = function(obj) {
if (!_.isObject(obj)) return obj;
var source, prop;
for (var i = 1, length = arguments.length; i < length; i++) {
source = arguments[i];
for (prop in source) {
if (hasOwnProperty.call(source, prop)) {
obj[prop] = source[prop];
}
}
}
return obj;
};

jquery

支持深度拷贝,深拷贝除了支持对象还包括数组,改变第一个对象,并返回第一个对象

// http://g.alicdn.com/thx/brix-release/1.0.0-beta.9/jquery/dist/jquery.js
jQuery.extend = jQuery.fn.extend = function() {
var src, copyIsArray, copy, name, options, clone,
target = arguments[ 0 ] || {},
i = 1,
length = arguments.length,
deep = false; // Handle a deep copy situation
if ( typeof target === "boolean" ) {
deep = target; // skip the boolean and the target
target = arguments[ i ] || {};
i++;
} // Handle case when target is a string or something (possible in deep copy)
if ( typeof target !== "object" && !jQuery.isFunction( target ) ) {
target = {};
} // extend jQuery itself if only one argument is passed
if ( i === length ) {
target = this;
i--;
} for ( ; i < length; i++ ) { // Only deal with non-null/undefined values
if ( ( options = arguments[ i ] ) != null ) { // Extend the base object
for ( name in options ) {
src = target[ name ];
copy = options[ name ]; // Prevent never-ending loop
if ( target === copy ) {
continue;
} // Recurse if we're merging plain objects or arrays
if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
( copyIsArray = jQuery.isArray( copy ) ) ) ) { if ( copyIsArray ) {
copyIsArray = false;
clone = src && jQuery.isArray( src ) ? src : []; } else {
clone = src && jQuery.isPlainObject( src ) ? src : {};
} // Never move original objects, clone them
target[ name ] = jQuery.extend( deep, clone, copy ); // Don't bring in undefined values
} else if ( copy !== undefined ) {
target[ name ] = copy;
}
}
}
} // Return the modified object
return target;
};

kissy1.3中有关对象的方法

mix

改变并返回第一个参数,下面实现方法中MIX_CIRCULAR_DETECTION标记,感觉是为了调试,看deep了几次

/*
* for example:
* @example
* var t = {};
* S.mix({x: {y: 2, z: 4}}, {x: {y: 3, a: t}}, {deep: TRUE}) => {x: {y: 3, z: 4, a: {}}}, a !== t
* S.mix({x: {y: 2, z: 4}}, {x: {y: 3, a: t}}, {deep: TRUE, overwrite: false}) => {x: {y: 2, z: 4, a: {}}}, a !== t
* S.mix({x: {y: 2, z: 4}}, {x: {y: 3, a: t}}, 1) => {x: {y: 3, a: t}}
*/
mix: function (r, s, ov, wl, deep) {
if (typeof ov === 'object') {
wl = ov['whitelist'];
deep = ov['deep'];
ov = ov['overwrite'];
}
var cache = [],
c,
i = 0;
mixInternal(r, s, ov, wl, deep, cache);
while (c = cache[i++]) {
delete c[MIX_CIRCULAR_DETECTION];
}
return r;
} function mixInternal(r, s, ov, wl, deep, cache) {
if (!s || !r) {
return r;
} if (ov === undefined) {
ov = TRUE;
} var i = 0, p, keys, len; // 记录循环标志
s[MIX_CIRCULAR_DETECTION] = r; // 记录被记录了循环标志的对像
cache.push(s); if (wl) {
len = wl.length;
for (i = 0; i < len; i++) {
p = wl[i];
if (p in s) {
_mix(p, r, s, ov, wl, deep, cache);
}
}
} else {
// mix all properties
keys = S.keys(s);
len = keys.length;
for (i = 0; i < len; i++) {
p = keys[i];
if (p != MIX_CIRCULAR_DETECTION) {
// no hasOwnProperty judge!
_mix(p, r, s, ov, wl, deep, cache);
}
}
} return r;
} function _mix(p, r, s, ov, wl, deep, cache) {
// 要求覆盖
// 或者目的不存在
// 或者深度mix
if (ov || !(p in r) || deep) {
var target = r[p],
src = s[p];
// prevent never-end loop
if (target === src) {
// S.mix({},{x:undefined})
if (target === undefined) {
r[p] = target;
}
return;
}
// 来源是数组和对象,并且要求深度 mix
if (deep && src && (S.isArray(src) || S.isPlainObject(src))) {
if (src[MIX_CIRCULAR_DETECTION]) {
r[p] = src[MIX_CIRCULAR_DETECTION];
} else {
// 目标值为对象或数组,直接 mix
// 否则 新建一个和源值类型一样的空数组/对象,递归 mix
var clone = target && (S.isArray(target) || S.isPlainObject(target)) ?
target :
(S.isArray(src) ? [] : {});
r[p] = clone;
mixInternal(clone, src, ov, wl, TRUE, cache);
}
} else if (src !== undefined && (ov || !(p in r))) {
r[p] = src;
}
}
}

merge

合并所有obj到返回新

merge: function (var_args) {
var_args = S.makeArray(arguments);
var o = {},
i,
l = var_args.length;
for (i = 0; i < l; i++) {
S.mix(o, var_args[i]);
}
return o;
},

augment

只对原型对象进行复制,改变并返回第一个参数

augment: function (r, var_args) {
var args = S.makeArray(arguments),
len = args.length - 2,
i = 1,
ov = args[len],
wl = args[len + 1]; if (!S.isArray(wl)) {
ov = wl;
wl = undefined;
len++;
}
if (!S.isBoolean(ov)) {
ov = undefined;
len++;
} for (; i < len; i++) {
S.mix(r.prototype, args[i].prototype || args[i], ov, wl);
} return r;
},

extend

继承的实现,跟上面封装的继承相比,还是有缺点的,首先新类的构造函数执行时候,父类的构造函数不会自动执行,总觉得superclass的指向的原型,中间多了一层

extend: function (r, s, px, sx) {
if (!s || !r) {
return r;
} var sp = s.prototype,
rp; // add prototype chain
rp = createObject(sp, r);
r.prototype = S.mix(rp, r.prototype);
r.superclass = createObject(sp, s); // add prototype overrides
if (px) {
S.mix(rp, px);
} // add object overrides
if (sx) {
S.mix(r, sx);
} return r;
}, function Empty() {
}
function createObject(proto, constructor) {
var newProto;
if (ObjectCreate) {
newProto = ObjectCreate(proto);
} else {
Empty.prototype = proto;
newProto = new Empty();
}
newProto.constructor = constructor;
return newProto;
}

更多例子以及在例子中,我对继承的摸索http://sprying.github.io/webtest/class/index.html

最新文章

  1. Linux From Scratch(从零开始构建Linux系统,简称LFS)- Version 7.7(三)
  2. 基于redis分布式锁实现“秒杀”
  3. 比较详细PHP生成静态页面教程
  4. C++ Primer:第八章:IO库(续)
  5. python 小试牛刀之信息管理
  6. Ubuntu--服务器版本系统安装图解教程
  7. Android:使用ViewPager实现左右滑动切换图片 (简单版)
  8. (转载)mysql_query( )返回值
  9. Mac下配置node.js环境(Mac 10.12)
  10. TRY
  11. Dynamics 365 解决方案导出报错
  12. DOCTYPE声明作用?标准模式与兼容模式?
  13. 细说Django的中间件
  14. hadoop 集群中数据块的副本存放策略
  15. python UI自动化测试
  16. WPF布局控件常用属性介绍
  17. Spring Cloud Ribbon负载均衡配置类放在Spring boot主类同级增加Exclude过滤后报Field config in com.cloud.web.controller.RibbonConfiguration required a bean of type &#39;com.netflix.client.config.IClientConfig&#39; that could not b
  18. 014---Django的中间件
  19. EasyNVR摄像机网页无插件直播使用过程中问题的自我排查-设备不在线问题的自我排查
  20. ROS Learning-013 beginner_Tutorials (编程) 编写ROS服务版的Hello World程序(Python版)

热门文章

  1. Android-ContentProvider流程
  2. 理解ASP.NET MVC引擎处理模型字符串的默认行为,涉及Html.Raw()和HttpUtility.HtmlDecode()
  3. 基于ZKEACMS的.Net Core多租户CMS建站系统
  4. 极大似然估计MLE 极大后验概率估计MAP
  5. 关于C#低版本升级高版本时,项目中引用Microsoft.Office.Interop.Word,程序提示不存在类型或命名空间名office.
  6. bzoj 2780: [Spoj]8093 Sevenk Love Oimaster(广义SAM)
  7. 【redis基础】
  8. IOS面试题(二)
  9. leetcode-824-Goat Latin(字符串的处理)
  10. css 实现文字图片垂直对齐