zoukankan      html  css  js  c++  java
  • 《JavaScript高级程序设计》扩展关于动态原型

      前文是基于《JavaScript高级程序设计》中关于对象创建的笔记和总结。但是作者Nicholas C. Zakas在【动态原型】方式创建对象的时候没有深究可能会存在的问题和解决方案。而仅仅在继承的时候对【动态原型】的瓶颈作了说明。即在作子类继承的时候,不能通过动态原型的方式来实现。

      原文大致如下:
    继承机制不能采用动态化的原因是:prototype对象的唯一性。实例代码:

    function A (i) {
    this.a = i;
    if (typeof A._init == 'undefined') {
    A.prototype.func
    = function () {
    return 0;
    }
    A._init
    = 1;
    }
    }
    function subA (j) {
    A.call(
    this, 1);
    this.j = j;
    if (typeof subA._init == 'undefined') {
    subA.prototype
    = new A();
    subA.prototype.func_sub
    = function () {
    return ++j;
    }
    subA._init
    = 1;
    }
    }
    var sub_a = new subA(1);
    alert(sub_a.func_sub());
    //error: sub_a.func_sub is not a function

    Nicholas解释说在代码运行前,对象已被实例,并与prototype联系,在当前对prototype对象替换不会对它产生任何影响,即当前的替换是访问不到的,只有未来对象的实例才会反映出这种改变。于是第一个实例对象就会不正确。但第二个及以后的子类实例都没问题。
    解决方法就是在构造函数外赋予新的prototype对象:

    代码
    function A (i) {
    this.a = i;
    if (typeof A._init == 'undefined') {
    A.prototype.func
    = function () {
    return 0;
    }
    A._init
    = 1;
    }
    }
    function subA (j) {
    A.call(
    this, 1);
    this.j = j;
    if (typeof subA._init == 'undefined') {

    subA.prototype.func_sub
    = function () {
    return ++j;
    }
    subA._init
    = 1;
    }
    }
    subA.prototype
    = new A();

    var sub_a = new subA(1);
    alert(sub_a.func_sub());
    //2

    可惜这违反了我们为什么使用动态原型的初衷。
    使用动态原型的初衷本来就是要让构造函数能“统一江山”,在视觉上让人觉得原型方法是类构造的一部分。

      以上是《JavaScript高级程序设计》中对动态原型继承小节的大概内容。

    <! -- ========== 分割线 ============ -->

      可是Nicholas在先前的章节讲对象构造的【动态原型】方式中,似乎忘了提这个同样的问题。我们看看上文中最后一个例子:

    var Obj = function (name) {
    this.name = name;
    this.flag = new Array('A', 'B');
    if (typeof Obj._init == 'undefined') {
    Obj.prototype
    = {
    showName :
    function () {
    alert(
    this.name);
    }
    };
    Obj._init
    = true;
    }
    }
    var obj1 = new Obj('aa');
    var obj2 = new Obj('bb');
    obj1.showName();
    //error: is not a function
    obj2.showName(); // bb;

    是的,这个问题其实和子类继承中出现的问题如出一辙,prototype在当前的替换是不会对该对象有任何影响的,只有在未来的实例中可见。如果按照Nicholas处理动态原型继承的方式中说的一样,那就意味着只能在构造函数外边重新赋予prototype对象。那么这不就成了【构造函数/原型混合】方式了吗?所谓的【动态原型】方式也就不存在了...

      其实我们可以想想,为什么在【构造函数/原型混合】这种已经基本没有副作用的构建对象方式后面还要在写一节【动态原型】方式。作者的意图无非就是想让构造函数在视觉上更为统一么。其实仅仅要视觉上的统一可以不用动态原型的。

    var Obj = function () {
    function __initialize (name) {
    this.name = name;
    this.flag = new Array('A', 'B');
    }
    __initialize.prototype
    = {
    showName :
    function () {
    alert(
    this.name);
    },
    showFlag :
    function () {
    alert(
    this.flag);
    }
    }
    return __initialize;
    }();

    var obj1 = new Obj('aa');
    var obj2 = new Obj('bb');
    obj1.showName();
    // aa
    obj2.showName(); // bb

    其实上面的方式就可以算是视觉的统一了,Obj的构造函数内通过__initialize来初始化属性,通过__initialize.prototype原型初始化方法。只不过稍微有点“小作弊”的感觉,__initialize代理了Obj的初始化...

      下面是来自tangoboy的“构造类”的封装,其实思路和上面基本一致,唯一不同的是他把属性也用原型方式创建了,同时把初始化属性和方法都扔到了构造函数参数对象里。方便自定义:

    /* == form tangoboy == */
    window[
    '$Class'] = {
    //创建一个类 混合构造函数/原型方式
    create: function(config) {
    var obj = function(){},config = config||{};
    //过滤构造方法和原型方法
    obj = obj.prototype.constructor = config["__"]||obj;
    delete config["__"];
    obj.prototype
    = config;
    return obj;
    }
    }
    /* -- eg -- */
    var man = $Class.create({
    __ :
    function (name) {
    this.name = name;
    },
    sex :
    'male',
    showName :
    function () {
    alert(
    this.name);
    }
    });
    var me = new man('ru');
    me.showName();
    //ru

    其实如果硬要追求视觉的统一也可以不用动态原型的方式。说到底看看上面的思路,已经回溯到了我们最常用的“类构造”方式:

    var Class = {
    create :
    function () {
    return function () {
    this.initialize.apply(this, arguments);
    }
    }
    }
    相信上面这段代码大家或许都不会陌生,如果细究下去,会发现其实和上面的代码都一致,用initialize函数作了初始化的代理,从而完成了视觉的统一。




     




     
  • 相关阅读:
    [译]rabbitmq 2.1 Consumers and producers (not an economics lesson)
    mysql存储过程执行权限问题
    修改linux端口范围 ip_local_port_range
    centos6 自启动任务
    randomize_va_space
    linux 捕获信号处理中遇到的死锁
    squid基础配置
    [转]你应该了解的 一些web缓存相关的概念
    libevent简介 构成
    Libevent windows/linux下编译
  • 原文地址:https://www.cnblogs.com/hongru/p/1871656.html
Copyright © 2011-2022 走看看