谈谈 javascript 面向对象的一些细节题目

综述

在 ES6 之前,ES5 完成面向对象是人人常常议论的题目,趁着 ES6 还没进入浏览器,借我本身的一段剧本,跟人人议论一下 js 面向对象的一些细节题目,迎接留言指教。

例子代码

基础的观点比方“基类”,“子类”等就不诠释了。下面是我写的一段完成类继续的 js 剧本:

/**
 * @file Inheritable.js
 * @author Y3G
 * @fileoverview
 * 可继续对象
 */

var _ = require('lodash');
var assert = require('./assert');
var seq = require('./seq');

/**
 * 并联函数
 * @return 天生的函数
 */
var parallel = function () {
    var fns = _.filter(arguments, _.isFunction);

    if (fns.length === 0) return;

    return function () {
        _.forEach(fns, function (fn) {
            fn.apply(this, _.slice(arguments));
        });
    };
}

/**
 * 建立类
 * @param base 基类函数或原型对象
 * @param ex 实例扩大
 * @return 天生的类函数
 *         默许要领:
 *           __init__: 组织函数
 *           __initProto__: 原型组织函数
 *         默许类要领:
 *           makeClass: 建立类
 *           makeSubClass: 建立子类
 */
function makeClass(base, ex) {
    base = base || {};
    ex = ex || {};

    var proto = _.isFunction(base) ? new base(true) : base;

    ex.__init__ = parallel(proto.__init__, ex.__init__); // 实例初始化函数
    ex.__initProto__ = parallel(proto.__initProto__, ex.__initProto__); // 原型初始化函数
    proto = _.mixin(proto, ex); // 兼并原型和实例扩大

    function SubClass(isProto) {
        var initFunc = isProto ? this.__initProto__ : this.__init__;
        if (_.isFunction(initFunc)) {
            initFunc.apply(this, _.slice(arguments));
        }
        
        this.$id = seq();
    }

    if (_.isFunction(base)) {
        // 复制静态函数等属性到子类
        _.forOwn(base, function (val, key) {
            SubClass[key] = val;
        });
    }

    SubClass.prototype = proto;
    SubClass.prototype.constructor = SubClass;
    SubClass.makeSubClass = _.curry(makeClass, SubClass);
    SubClass.makeClass = makeClass;

    return SubClass;
}

/**
 * 基础类
 * @class Root
 */
var Root = makeClass();

module.exports = Root;

这段代码导出了一个基础类 Root,它有一个静态要领叫做 makeSubClass,挪用可天生一个 Root 的子类,建立出的子类一样带有 makeSubClass 这个静态要领。同时,建立的子类有几个牢固字段,分别是:

  • __init__ 初始化函数

  • __initProto__ 原型初始化函数

  • $id 对象 id

经由过程 parallel 这个函数,makeClass 把基类和子类的 __init__ 函数兼并实行,如许处理了基类组织函数没法实行的题目。

下面说说我对几个细节题目的思索。

几个题目和我的意见

:组织函数 __init__ 的实行递次是基类 -> 子类比较好照模样类 -> 基类比较好?

根据我贴的代码,是基类的组织函数先实行,当时我是想模仿 C++。然则我如今以为应当子类组织函数先实行。

缘由很简单,就是 ES6 运用的是类 java 体式格局, constructor 函数是子类先实行的,而且基类 constructor 是靠
super() 手工挪用的。基于一点 java 的运用履历,我也以为如许的组织递次,比基类 -> 子类天真不少。别的,采纳和 ES6 一样的组织递次,更有利于移植。

__initProto__ 是什么鬼?

这是我一直以来对峙的意见——用 js 模仿类,假如一个类的实例有能够作为 prototype 存在,就必需把实例组织和 prototype
组织离开,而 __initProto__ 就是特地用来初始化 prototype 的。

缘由有两方面:

  • 一是对象能够会很高贵,占许多资本。

  • 二是组织函数 __init__ 能够不止会操纵 this,还能够会修正全局的某些状况(比方计数器)。这类时刻多建立一个和少建立一个实例,显然是差别的。

:为何要把基类函数上的静态内容都拷贝到子类函数上?

由于原型链查找对静态内容无效。

比方如许:

var Foo = Root.makeSubClass({});

Foo.SOME_STATIC_THING = 0;

var Bar = Foo.makeSubClass({});

alert(Bar.SOME_STATIC_THING);

这时刻假定不拷贝 SOME_STATIC_THING 到子类上去,就不能经由过程 Bar.SOME_STATIC_THING 访问到该属性。这是异常违背基本知识的,没有言语是如许子的。同时,如今又有了另一个这么做的来由,就是 ES6 有 static
关键字,假如你运用其他体式格局完成静态属性,将不利于今后移植到 ES6。

不过这里有个小坑,就假如静态变量是基础范例(比方字符串),那末显然在子类上修正对基类无效。因而基础范例的静态变量只能是常量,假如须要异常量的静态变量,必需运用对象。

    原文作者:Y3G
    原文地址: https://segmentfault.com/a/1190000005824847
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞