Javascript继承:如果属性名称相同,则自动调用父方法

时间:2017-04-15 10:15:32

标签: javascript inheritance

如果属性名称相同,是否有办法自动调用父方法。

// library part
var parent = {
    foo: function() { console.log('foo from parent'); },
    bar: function() { console.log('bar from parent'); }
}

// User part
var child = Object.create(parent);

child.foo = function() { console.log('foo from child'); };
child.bar = function() { console.log('bar from child'); };

如果我调用child.foo(),则输出为foo from child,我的目标输出为foo from childfoo from parent是控制台注销的。现在,当我需要这样做时,我使用这段代码:

child.foo = function() {
     console.log('foo from child');
     this.__proto__.foo();
}

我有理由这样做,它就像提供可以由用户扩展的父对象的库,而我不想提醒用户每次调用this.__proto__.foo()他们分配属性名称与父对象属性相同的方法的时间。

解决方案当然必须修改库部分(父对象或可能在对象创建中),而不是用户部分。

2 个答案:

答案 0 :(得分:3)

重新编辑

  

解决方案当然必须修改库部分(父对象或可能在对象创建中),而不是用户部分。

如果这意味着你真的希望用户能够写

child.foo = function() { ... };

...然后这会改变一些事情,但是仍然可以不使用代理(真的慢)。首先,我的原始答案,因为你接受了它,然后更新说明:

原始答案

  

我不想提醒用户每次分配属性名称与父对象属性相同的方法时调用this.__proto__.foo()

在这种情况下,您可能希望为它们提供一个工作函数,用于设置这些函数属性,并自动检测情况并对其进行处理。你必须决定是否在用户的新代码之前或之后调用原型的版本,如果你不打算让它们控制调用的位置,并决定是否返回它们的函数或原型的结果。 / p>

当然,没有什么能阻止他们选择忽略这一点,但只有你能做的事情。

例如,此示例假设您要在之后调用原始(如示例所示)并返回其覆盖的结果。

var mainProto = {
    addMethod: function(name, method) {
        var p = Object.getPrototypeOf(this); // See note later
        if (typeof p[name] === "function") {
            this[name] = function() {
                var result = method.apply(this, arguments);
                p[name].apply(this, arguments);
                return result;
            };
        } else {
            this[name] = method;
        }
        return this;
    }
};
var parent = Object.create(mainProto);
parent.addMethod("foo", function foo() { console.log('foo from parent'); });
parent.addMethod("bar", function bar() { console.log('bar from parent'); });
var child = Object.create(parent);
child.addMethod("foo", function foo() { console.log('foo from child'); });
child.addMethod("bar", function bar() { console.log('bar from child'); });
var grandChild = Object.create(child);
grandChild.addMethod("foo", function foo() { console.log('foo from grandChild'); });
grandChild.addMethod("bar", function bar() { console.log('bar from grandChild'); });

console.log("---");
parent.foo();
parent.bar();
console.log("---");
child.foo();
child.bar();
console.log("---");
grandChild.foo();
grandChild.bar();
.as-console-wrapper {
  max-height: 100% !important;
}

关于__proto__ vs getPrototypeOf__proto__官方仅在浏览器上支持,而不是在其他JavaScript环境中支持,并在Object.prototype上定义,因此完全可以拥有没有它的对象(通过Object.create(null))。相反,getPrototypeOf是跨环境定义的,无论对象是否继承自Object.prototype,都可以正常工作。

更新编辑

我们可以在相关属性上使用setter来插入包装器而不是让用户调用addMethod

// Library code
function wrapMethod(newMethod, parentMethod) {
    if (typeof parentMethod === "function") {
        return function() {
            var result = newMethod.apply(this, arguments);
            parentMethod.apply(this, arguments);
            return result;
        };
    }
    return newMethod;
}
function defineMethod(obj, name, method) {
    Object.defineProperty(obj, name, {
        set: function(newValue) {
            if (this === obj) {
                return;
            }
            defineMethod(this, name,wrapMethod(newValue, Object.getPrototypeOf(this)[name]));
        },
        get: function() {
            if (this === obj) {
                return method;
            }
            return this[name];
        }
    });
}
var parent = (function() {
    var _foo = function foo() { console.log("foo from parent"); };
    var _bar = function bar() { console.log("bar from parent"); };
    var parent = {};
    defineMethod(parent, "foo", _foo);
    defineMethod(parent, "bar", _bar);
    return parent;
})();

// User code
var child = Object.create(parent);
child.foo = function foo() { console.log('foo from child'); };
child.bar = function bar() { console.log('bar from child'); };
var grandChild = Object.create(child);
grandChild.foo = function foo() { console.log('foo from grandChild'); };
grandChild.bar = function bar() { console.log('bar from grandChild'); };

console.log("---");
parent.foo();
parent.bar();
console.log("---");
child.foo();
child.bar();
console.log("---");
grandChild.foo();
grandChild.bar();
.as-console-wrapper {
  max-height: 100% !important;
}

答案 1 :(得分:0)

我想另一种方法是在没有明确调用 proto 的情况下使用工厂函数:

const createChild = parent => Object.create(parent);
const enchanceChild = parent => ({
  foo() {
     console.log('from child');
     parent.foo();
  }
});

const parent = {
   foo(){ console.log('from parent') },
}

const child = createChild(parent);
const enchancedChild = enchanceChild(child);

enchancedChild.foo(); // from child form parent

这只是做你已经做过的事情的另一种方法。

您怎么看?

修改

使用Proxy Objects更清洁的方法:

var createObject = parent => {
   var handler = {
      get(target, name) {
         if (typeof target[name] === 'function' && typeof parent[name] === 'function') {
            return function() {
                const res = target[name]();
                parent[name]();
                return res;
            }
         }
         return target[name];
      }
   }

   return new Proxy({}, handler);
}

const parent = { foo() { console.log('parent'); }, };
const child = createObject(parent);

child.foo = () => console.log('child');

child.foo();
// returns:
// child
// parent