Наследование JS: вызов родительской функции из дочерней функции
должно быть что-то, чего я не понимаю в объектной модели JS.
из этих ресурсов:
3 ответов
Отличный вопрос, потребовалось немного тестирования и исследований, чтобы узнать это.
выявление странного поведения
я немного изменил ваш код, чтобы узнать, какая функция вызывается, когда:
var Parent = function() {};
Parent.prototype.myF = function() {
console.log('derp');
};
function Child() {
Parent.call(this);
this.name = 'Test'; // this is a new test property
};
//make [[prototype]] of Child be a ref to Parent.prototype
Child.prototype = Object.create(Parent.prototype);
//need to explicitly set the constructor
Child.prototype.constructor = Child;
Child.prototype.myF = function() {
console.log(this); // here I want to find out the context, because you use it in the next line
Object.getPrototypeOf(this).myF();
console.log("did I derp??");
};
childInstance = new Child();
childInstance.myF();
вы можете проверить JSFiddle и попробовать его для себя:http://jsfiddle.net/Lpxq78bs/
решающая строка в вашем коде такова:
Child.prototype.myF = function() {
Object.getPrototypeOf(this).myF(); // this one
console.log("did I derp??");
};
после этого console.log(this);
чтобы выяснить, что this
относится к, я видел, что он изменяется между первым и вторым выходом did I derp??
.
я получил следующий вывод:
Object { name: "Test" }
Object { constructor: Child(), myF: window.onload/Child.prototype.myF() }
"derp"
"did I derp??"
"did I derp??"
интерпретация результатов
так как я добавил свойство "name" в Child
конструктор, это будет только вокруг, если я смотрю на экземпляр Child
, не .prototype
.
таким образом, первая строка вывода означает, что текущий this
контекст действительно childInstance
. но второе-ни childInstance
, ни Parent.prototype
:
- звонок (
myF
ofchildInstance
):this
относится кchildInstance
. ищет[[Prototype]]
наchildInstance
, что этоChild.prototype
, а неParent.prototype
. Вывод: "я что, сошел с ума??' вызов (
myF
ofChild.prototype
):this
относится кChild.prototype
, который являетсяchildInstances
[[Прототип]] Собственность. Итак, второй призывObject.getPrototypeOf(this).myF();
наконец возвращаетParent.prototype
(вроде). Вывод: "я что, сошел с ума??'звонок (
myF
ofParent.prototype
экземпляр созданObject.create
): наконец-тоmyF
на родительском называется. Вывод: 'derp'
поскольку console.log("did I derp??")
после myF
функции выход в обратном порядке. на следующем рисунке показано, как проходит код:
так что ваше предположение о что?!--18--> относится, был неправ.
решение в ES5
By @LukeP: https://jsfiddle.net/Lpxq78bs/28/
альтернативное решение в ES6
чтобы избежать этой путаницы, и поскольку вы работаете с классическим шаблоном наследования, вы можете взглянуть на классы ЕС6. Ниже приведен пример того, что вы пытаетесь сделать:
class Parent {
constructor() {
}
myF() {
console.log('derp');
}
}
class Child extends Parent {
constructor() {
super();
}
myF() {
super.myF();
console.log("did I derp??");
}
}
var childInstance = new Child();
childInstance.myF();
я надеюсь, что это помогает в понимании Что происходит.
ваш код работает так , как ожидалось, вывод, который вы получаете, из-за Object.getPrototypeOf
и может быть объяснено
Шаг 1: когда вы cal childInstance.myF();
затем он переходит к ниже код, где this
относится к самому childInstance
Child.prototype.myF = function() {
Object.getPrototypeOf(this).myF();
console.log("did I derp??");
};
затем Object.getPrototypeOf
возвращает childInstance.[[Prototype]]
что это Child.prototype
и снова вызов myF
метод (оставляя вторую строку для печати позже после выполнения метода), но в следующий раз this
будет ссылка на childInstance.[[Prototype]]
.
Шаг 2 : в этом вызове this
указывает на childInstance.[[Prototype]]
( или Child.prototype
сам объект)
Child.prototype.myF = function() {
Object.getPrototypeOf(this).myF();
console.log("did I derp??");
};
теперь Object.getPrototypeOf
возвращает childInstance.[[Prototype]].[[Prototype]]
( т. е. Child.prototype.[[Prototype]]
), которая Parent.prototype
и снова вызовите метод myF, но на этот раз метод myF будет печатать баттхерт потому что myF метод Parent.prototype
вызывается. После этого вторая строка будет печатать я баттхерт.
Шаг 3: затем функция возвращается Шаг 1, чтобы выполнить вторую строку, которая снова печатает я баттхерт
Если вам нужно работать с наследованием в ES5, я сделал оболочку, которая упрощает расширение и вызов родительских методов. Никаких рамок не требуется.
скрипка для рабочего кода здесь:https://jsfiddle.net/wcrwLmrk/13/
вот как работает код:
/* extend the base class to the new parent class and pass
an object with the new properties and methods */
var ParentClass = Class.extend(
{
// reset the constructor to the correct constructor
constructor: function()
{
},
callName: function(arg)
{
console.log('parent ' + arg);
}
});
/* extend the parent class to the new child class and pass
an object with the new properties and methods */
var ChildClass = ParentClass.extend(
{
// reset the constructor to the correct constructor
constructor: function()
{
ParentClass.call(this);
},
callName: function(arg)
{
// child method code
console.log('child ' + arg);
/* call parent method by passing the method name and any params */
this.super('callName', arg);
}
});
Теперь мы можем создать новый дочерний класс, который будет вызовите метод супер класса:
var child = new ChildClass();
child.callName('name');
вот базовый класс, который должен быть включен в проект:
var Class = function()
{
};
Class.prototype =
{
constructor: Class,
/* this is an alias for callParent. this will
allow child classes to call super methods.
@param (string) method name
@param [(mixed)] addasmany args as the super
method needs
@return (mixed) the super method return value */
super: function()
{
var parent = this.parent;
if(parent)
{
var args = [].slice.call(arguments),
// this will remove the first arg as the method
method = args.shift();
if(method)
{
var func = parent[method];
if(typeof func === 'function')
{
return func.apply(this, args);
}
}
}
return false;
}
};
/* this will return a new object and extend it if an object it supplied.
@param [(object)] object = the extending object
@return (object) this will return a new object with the
inherited object */
var createObject = function(object)
{
if(!Object.create)
{
var obj = function(){};
obj.prototype = object;
return new obj();
}
else
{
return Object.create(object);
}
};
/* this will extend an object and return the extedned
object or false.
@param (object) sourceObj = the original object
@param (object) targetObj = the target object */
var extendObject = function(sourceObj, targetObj)
{
if(typeof sourceObj !== 'undefined' && typeof targetObj !== 'undefined')
{
for(var property in sourceObj)
{
if(sourceObj.hasOwnProperty(property) && typeof targetObj[property] === 'undefined')
{
targetObj[property] = sourceObj[property];
}
}
return targetObj;
}
return false;
};
var extendClass = function(sourceClass, targetClass)
{
/* if we are using a class constructor function
we want to get the class prototype object */
var source = (typeof sourceClass === 'function')? sourceClass.prototype : sourceClass,
target = (typeof targetClass === 'function')? targetClass.prototype : targetClass;
if(typeof source === 'object' && typeof target === 'object')
{
/* we want to create a new object and add the source
prototype to the new object */
var obj = createObject(source);
/* we need to add any settings from the source that
are not on the prototype */
extendObject(source, obj);
/* we want to add any additional properties from the
target class to the new object */
for(var prop in target)
{
obj[prop] = target[prop];
}
return obj;
}
return false;
};
/* this will allow the classes to be extened.
@param (object) child = the child object to extend
@return (mixed) the new child prototype or false */
Class.extend = function(child)
{
if(!child)
{
return false;
}
var parent = this.prototype;
/* the child constructor must be set to set
the parent static methods on the child */
var constructor = child && child.constructor? child.constructor : false;
if(child.hasOwnProperty('constructor') === false)
{
constructor = function()
{
var args = arguments;
parent.constructor.apply(this, args);
};
}
constructor.prototype = extendClass(parent, child);
constructor.prototype.parent = parent;
/* this will add the static methods from the parent to
the child constructor. */
extendObject(this, constructor);
return constructor;
};