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

js中有关类、对象的增强函数的更多相关文章

  1. js中的类和对象以及自定义对象

    js中的类 1.类的声明 function Person(name,age){ this.name=name; this.age=age; this.test=function(a){ alert(a ...

  2. JS中定义类的方法

    JS中定义类的方式有很多种: 1.工厂方式    function Car(){     var ocar = new Object;     ocar.color = "blue" ...

  3. JS中定义类的方法<转>

    转载地址:http://blog.csdn.net/sdlfx/article/details/1842218 PS(个人理解): 1) 类通过prototype定义的成员(方法或属性),是每个类对象 ...

  4. js中escape对应的C#解码函数 UrlDecode

    js中escape对应的C#解码函数 System.Web.HttpUtility.UrlDecode(s),使用过程中有以下几点需要注意   js中escape对应的C#解码函数 System.We ...

  5. JS中的面相对象

    1.使用Object或对象字面量创建对象 JS中最基本创建对象的方式: var student = new Object(); student.name = "easy"; stu ...

  6. JS中的event 对象详解

    JS中的event 对象详解   JS的event对象 Event属性和方法:1. type:事件的类型,如onlick中的click:2. srcElement/target:事件源,就是发生事件的 ...

  7. js中两个对象的比较

    代码取自于underscore.js 1.8.3的isEqual函数. 做了一些小小的修改,主要是Function的比较修改. 自己也加了一些代码解读. <!DOCTYPE html> & ...

  8. js中的json对象详细介绍

    JSON一种简单的数据格式,比xml更轻巧,在JavaScript中处理JSON数据不需要任何特殊的API或工具包,下面为大家详细介绍下js中的json对象, 1.JSON(JavaScript Ob ...

  9. js中如何访问对象和数组

    js中如何访问对象和数组 一.总结 一句话总结:js访问对象点和中括号,访问数组的话就是中括号 对象 . [] 数组 [] 1.js访问对象的两种方式? . [] 可以使用下面两种方式访问对象的属性和 ...

  10. js动态创建类对象

    1.定义函数,函数中定义类对象 f1=function(){ //定义类 function Pannel(name){ this.name = name; this.print = function( ...

随机推荐

  1. cxgrid列高度行宽度定义

    行高度定义 TableView->OptionView->dataRowHieght  即可设置行高度 自动调整行宽 1.选中cxgridview,在属性中找OptionsView---& ...

  2. java学习(六)面向对象 final关键字 多态

    1.被fnial修饰的方法不能被重写,常见的为修饰类,方法,变量 /* final可以修饰类,方法,变量 特点: final可以修饰类,该类不能被继承. final可以修饰方法,该方法不能被重写.(覆 ...

  3. jquery.pagination参数释义

    参数名 参数说明 可选值 默认值callback 点击分页按钮的回调函数 函数 function(){return false;}current_page 初始化时选中的页码 数字 0items_pe ...

  4. C#反射(转载)

    转载原文出处忘了,一直保存在本地(勿怪) 前期准备 在VS2012中新建一个控制台应用程序(我的命名是ReflectionStudy),这个项目是基于.net 4.0.接着我们打开Program.cs ...

  5. .net core i上 K8S(七).netcore程序的服务发现

    上一章我们分享了k8s的网络代理模式,今天我们来分享一下k8s中的服务发现. 1.环境变量模式的服务发现 k8s默认为我们提供了通过环境变量来实现服务发现的功能,前提是 1.需要service在pod ...

  6. Android 用 res 中文件名获取资源 id 的方法

    res 中我们可能会放很多图片和音频视频等.它们放在 R.drawable, R.raw 下面. 有一种情况是,比如我有一个数据库保存项目中声音的一些信息.声音的 id 就很难保存.因为我们不能把 R ...

  7. 模拟实现STL库

    最近在复习STL,感觉再看的时候比刚开始学的时候通透很多.以前模拟实现了一个STL库,最近复习完又重构了一遍.代码放出来以供后面学习.如果有写的不好的地方欢迎大家批评指正. STL_List.h #p ...

  8. 手把手教你在Mac中搭建iOS的 React Native环境

    准备工作 1.你需要一台Mac电脑..(这不是废话?我所用的是Air~~穷..) 2.我所操作的电脑系统环境是 3.xcode版本:8.0正式版 必需的软件 1.Homebrew Homebrew, ...

  9. HTML5 调用百度地图API地理定位

    <!DOCTYPE html> <html> <title>HTML5 HTML5 调用百度地图API地理定位实例</title> <head&g ...

  10. iOS 进阶---推送通知之本地通知

    1.推送通知的2种方式 1)本地推送通知(Local Notification) 2)远程推送通知(Remote Notification) 2.通知的作用 可以让不在前台运行的app,告知用户app ...