资讯专栏INFORMATION COLUMN

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

newsning / 3311人阅读

摘要:同时,创建的子类有几个固定字段,分别是初始化函数原型初始化函数对象通过这个函数,把基类和子类的函数合并执行,这样解决了基类构造函数无法执行的问题。二是构造函数可能不止会操作,还可能会修改全局的某些状态比如计数器。

综述

在 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。

不过这里有个小坑,就如果静态变量是基本类型(比如字符串),那么显然在子类上修改对基类无效。于是基本类型的静态变量只能是常量,如果需要非常量的静态变量,必须使用对象。

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

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

相关文章

  • 谈谈我所理解面向对象

    摘要:众多面向对象的编程思想虽不尽一致,但是无论哪种面向对象编程语言都具有以下的共通功能。原型编程以类为中心的传统面向对象编程,是以类为基础生成新对象。而原型模式的面向对象编程语言没有类这样一个概念。 什么是面向对象?这个问题往往会问到刚毕业的新手or实习生上,也是往往作为一个技术面试的开头题。在这里我们不去谈如何答(fu)好(yan)问(guo)题(qu),仅谈谈我所理解的面向对象。 从历...

    avwu 评论0 收藏0
  • SegmentFault 技术周刊 Vol.32 - 七夕将至,你对象”还好吗?

    摘要:很多情况下,通常一个人类,即创建了一个具体的对象。对象就是数据,对象本身不包含方法。类是相似对象的描述,称为类的定义,是该类对象的蓝图或原型。在中,对象通过对类的实体化形成的对象。一类的对象抽取出来。注意中,对象一定是通过类的实例化来的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 马上就要到七夕了,离年底老妈老爸...

    李昌杰 评论0 收藏0
  • SegmentFault 技术周刊 Vol.32 - 七夕将至,你对象”还好吗?

    摘要:很多情况下,通常一个人类,即创建了一个具体的对象。对象就是数据,对象本身不包含方法。类是相似对象的描述,称为类的定义,是该类对象的蓝图或原型。在中,对象通过对类的实体化形成的对象。一类的对象抽取出来。注意中,对象一定是通过类的实例化来的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 马上就要到七夕了,离年底老妈老爸...

    Lyux 评论0 收藏0
  • SegmentFault 技术周刊 Vol.32 - 七夕将至,你对象”还好吗?

    摘要:很多情况下,通常一个人类,即创建了一个具体的对象。对象就是数据,对象本身不包含方法。类是相似对象的描述,称为类的定义,是该类对象的蓝图或原型。在中,对象通过对类的实体化形成的对象。一类的对象抽取出来。注意中,对象一定是通过类的实例化来的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 马上就要到七夕了,离年底老妈老爸...

    AaronYuan 评论0 收藏0
  • Java项目经验——程序员成长钥匙

    摘要:当你真正到公司里面从事了几年开发之后,你就会同意我的说法利用找工作,需要的就是项目经验,项目经验就是理解项目开发的基本过程,理解项目的分析方法,理解项目的设计思 Java就是用来做项目的!Java的主要应用领域就是企业级的项目开发!要想从事企业级的项目开发,你必须掌握如下要点: 1、掌握项目开发的基本步骤 2、具备极强的面向对象的分析与设计技巧 3、掌握用例驱动、以架构为核心的主流开发...

    zhangfaliang 评论0 收藏0

发表评论

0条评论

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