JavaScript 中的方法继承
Method Inheritance in JavaScript
JavaScript 使用原型系统,这与类系统有着根本的不同。这是我第一次认真接触这种语言。我以前玩过它,但这是我第一次构建具有适当 OO、继承、多态等的系统。
从我读到的内容来看,似乎有一些常用的方法可以在 Javascript 中进行成员函数继承。假设您有一个父foo
foo = function(){ this.one = 1; }
foo.prototype.func1 = function(){return this.one;}
MDN JavaScript 继承简介提出了在子上下文中调用父方法的幼稚方法,如下所示。
bar = function(){ foo.call(this); }
bar.prototype = Object.create(foo.prototype);
bar.prototype.func1 = function(){ return this.one + foo.prototype.func1();}
这具有易于理解的优点,但正如此Salsify博客文章中指出的那样,可能会变得繁琐。此博客文章概述了一种替代方法,其中在子原型中定义super
属性,并将每个成员函数的名称作为属性附加到该方法。但是,此方法依赖于方法的caller
属性,本文指出该方法将很快被弃用。与其重复整篇文章,我认为重要观点的总结是这些
Object.defineProperty(bar.prototype, "super", {
get: function get() {
...
// methodName is set as a property on each method in an omitted code segment
methodName = get.caller.methodName;
...
Object.getPrototypeOf(this.prototype)[methodName]
}
}
也就是说,您可以在原型的原型中找到具有相同名称的方法。我想知道这是否可以以更简单的方式完成,而无需将方法名称附加为参数,也无需Function.caller
.
foo.prototype.super = function(method) {
superMethod = Object.getPrototypeOf(this.constructor.prototype)[method];
return superMethod.call(this, Array.prototype.slice.call(arguments, 1));
}
bar.prototype.func1 = function(){ return this.one + super('func1'); }
我在上面做了一些假设,我想验证一些假设。
- new bar().
- constructor.prototype === Object.getPrototypeOf(new bar())
- 如果上述情况总是正确的,那么一个比另一个更可取吗?
- Parent 的成员函数将始终存在于子原型的原型中(假设在创建对象后两个原型都没有发生突变)
- Object.getPrototypeOf()不是博客所指的ES6中添加的"访问超级方法的语言支持"。
- 如果 Object.getPrototypeOf() 不是该语言支持,那是什么?
在看到使用 this
的错误后,它在整个执行过程中不会改变并且始终引用子类的实例,我重新审视并认为我需要这样的东西
Grandfather = function(){};
Grandfather.prototype.function1 = function(){console.log("I am the Grandfather");};
Father = function(){Grandfather.apply(this);};
Father.prototype = Object.create(Grandfather.prototype);
Father.prototype.function1 = function f(){ f.super(); console.log("I am the Father");};
Father.prototype.function1.super = Grandfather.prototype.function1;
Child = function(){Father.apply(this);}
Child.prototype = Object.create(Father.prototype);
Child.prototype.function1 = function f(){ f.super(); console.log("I am the Child");};
Child.prototype.function1.super = Father.prototype.function1;
c = new Child();
c.function1();
// I am the Grandfather
// I am the Father
// I am the Child
所以问题就变成了,如何以某种自动方式为每个函数设置 super
属性?
下面显示了一种方法,它的好处是在对象实例化后添加到原型中的函数仍然能够调用superFunc
,而在类扩展时设置super
属性的方法如果稍后将函数添加到原型中,则不会设置这样的属性。
此方法的缺点是它只能在单线程环境中工作,并且需要从公共基类继承的功能。它不是线程安全的,因为某些状态保存在实际上是函数的静态变量中。这对我的目的来说很好,因为浏览器仍然有单线程的JavaScript。所有类都继承自包含此方法的某个基类的要求并不是一个巨大的障碍(特别是如果你做了一件"坏事"并将其插入到 Object 的原型中)。
Grandfather.prototype.superFunc = function f(funcName){
currentPrototype = Object.getPrototypeOf(f.startingPrototype || Object.getPrototypeOf(this));
f.startingPrototype = currentPrototype;
return currentPrototype[funcName]();
}
Child.prototype.function2 = function(){this.superFunc('function2'); console.log("Still in the Child");};
Father.prototype.function2 = function(){this.superFunc('function2'); console.log("Still in the Father");};
GrandFather.prototype.function2 = function(){console.log("Still in the Grandfather");};
c = new Child();
c.function2();
// Still in the Grandfather
// Still in the Father
// Still in the Child
问题 1
new Bar().constructor.prototype
应该等于Object.getPrototypeOf(new Bar())
,前提是您没有覆盖Bar.prototype.constructor
或Bar.prototype
,或者在Bar
构造函数中返回不同的对象。下面是一个示例:
function Bar() {}
var foo = new Bar();
foo.constructor.prototype === Object.getPrototypeOf(foo); // true
function Bar2() {}
var foo2 = new Bar2();
Bar2.prototype = {};
foo2.constructor.prototype === Object.getPrototypeOf(foo2); // false
function Bar3() {}
var foo3 = new Bar3();
Bar3.prototype.constructor = function NotBar3() {};
foo3.constructor.prototype === Object.getPrototypeOf(foo3); // false
问题2
如果要获取对象的实际原型,请使用 Object.getPrototypeOf
,因为它不受上面显示的任何更改的影响。
问题3
不,您将无法从new Bar()
访问Foo
。在您的示例中,new Bar()
不会从Foo.prototype
继承,因此,除非您使Foo
继承Foo.prototype
或将Foo
分配给 new Bar()
或 Bar.prototype
的属性,否则无法访问。
问题 4/5
不,这不是他们所指的。ES6 将引入一个单独的class
构造,其中super
具有特殊含义(类似于super
在其他语言中使用类的工作方式)。下面是类在 ES6 中如何工作的示例:
class Foo {
constructor() {
this.one = 1;
}
func1() {
return this.one;
}
}
class Bar extends Foo {
func1() {
return this.one + super();
}
}
当你以你的方式使用 super 时,当继承超过 2 级时它会中断。
假设您将按以下方式使用它:
//changed super to this.super since super is not shown to exist in global scope
bar.prototype.func1(){ return this.one + this.super('func1'); }
请参阅以下示例:
function GrandFather(){
this.i = 0;
};
GrandFather.prototype.test = function(){
console.log('test in GrandFather');
};
function Father(){
GrandFather.call(this);
};
Father.prototype = Object.create(GrandFather.prototype);
Father.prototype.constructor = Father;
Father.prototype.super = GrandFather.prototype;
Father.prototype.test = function(){
console.log('test in Father');
//prevent too many recursions
this.i++;
if(this.i>5){
return;
}
this.super.test.call(this);//because test in child was called
// with Child instance as invoking object this will be Child
// and this.super will be Father.prototype
};
function Child(){
Father.call(this);
}
Child.prototype = Object.create(Father.prototype);
Child.prototype.constructor = Child;
Child.prototype.super = Father.prototype;
Child.prototype.test = function(){
console.log('test in Child');
this.super.test.call(this);//because invoking object is Child
//this.super in Father is Child
};
var c = new Child();
c.test();
使用大写字母启动构造函数也是常见的做法,因此最好使用 Foo 和 Bar 作为构造函数名称。
如果你想经历在JavaScript中模拟super的所有麻烦,那么以下方式会稍微健壮一些: http://ejohn.org/blog/simple-javascript-inheritance/
- 如何防止在javascript中执行继承属性的getter-setter方法
- Vue.js继承调用父方法
- Javascript继承-使用.call方法
- 在Javascript中扩展继承的原型方法
- 在Javascript中,如何继承和扩展正在继承的方法
- 使变量继承现有原型的方法
- 如何使用来自PIXI.extras.TilingSprite的Pixi JS中的继承方法“生成纹理”
- 来自JavaScript继承方法的变量访问
- Javascript简单继承方法
- 如何在JavaScript中访问继承方法内部的私有属性
- 通过指令表达式继承方法
- 原型继承为什么子对象不从父对象继承方法
- 为什么所有这些Javascript原型继承方法看起来都是一样的?
- 继承方法(原型)和静态方法(表达式)的区别是什么?
- TypeScript类可以移除对继承方法的访问吗?
- 继承方法到除原语以外的所有对象
- 可以帮助澄清Javascript原型继承方法调用
- 这种Javascript继承方法有什么问题吗
- 继承方法调用触发Typescript编译器错误
- Javascript:如何在覆盖子类中的继承方法后运行父类的构造函数逻辑