必须有一些我不了解JS对象模型的东西。
从这些资源:
我已经收集了我的想法或想法,是对象模型的准确心理表征。这是:
所有对象都有一个属性,文档称为[[Prototype]]
。 [[Prototype]]
可以被视为对象父母的引用。更准确地说:
对[parent&#;;]原型对象的引用被复制到 新实例的内部
[[Prototype]]
属性。 (source 1)
您可以使用[[Prototype]]
访问子项的Object.getPrototypeOf(child)
属性。此处返回的值将是对父级原型(不是其内部[[Prototype]]
属性的引用,但它的实际原型)
obj.prototype
与对象的内部[[Prototype]]
属性不同。它的作用类似于用于创建此精确对象的实例的蓝图,而其[[Prototype]]
属性指向用于生成其父实例的蓝图。
Parent.prototype === Object.getPrototypeOf(child); //true
详细说明:
如果您向child.prototype
添加一项功能,child
及其任何一个孩子都可以使用此功能。
如果您向parent.prototype
添加一个功能,这相当于向Object.getPrototypeOf(child)
添加功能,那么该功能将可用于parent
及其所有功能&#39 ; s的孩子,包括child
及其所有siblings
。
您可以使用Object.create()
创建一个具有所需[[Protoype]]
属性的新对象。因此,您可以将其用作实现继承的方法。有关示例,请参阅source 2。
考虑到这一点,我希望得到一个自己的实例。我的计划是创建一个父母班级'然后做一个孩子的课程'继承自它。
我希望子类实现一个方法,该方法从父方法重载方法。需要注意的是,我希望孩子的方法版本能够调用该方法的父版本,然后做一些额外的工作。
以下是我提出的问题,请参阅下面的相关问题:
var Parent = function() {};
Parent.prototype.myF = function() {
console.log('derp');
};
function Child() {
Parent.call(this);
};
//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() {
Object.getPrototypeOf(this).myF();
console.log("did I derp??");
};
childInstance = new Child();
childInstance.myF();

似乎是当我尝试重载Parent.myF()
时,当我重载它时,我实际上是在同时修改原始函数。这似乎是因为记录的结果是:
'derp'
'did I derp??'
'did I derp??'
大概是'did I derp??'
的第一次出现来自父母的功能的修改版本,我不意味着做,然后是第二版来自孩子的功能。
任何人都可以详细说明为什么会这样吗?
答案 0 :(得分:6)
很好的问题,需要进行一些测试和研究才能找到它。
我改变了你的代码以找出在以下时调用的函数:
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??"
由于我向Child
构造函数添加了'name'属性,因此只有在我查看Child
的实例时才会出现,而不是.prototype
。
因此输出的第一行意味着当前this
上下文确实是childInstance
。 但第二个既不是childInstance
,也不是Parent.prototype
:
myF
childInstance
}:this
是指childInstance
。 Object.getPrototypeOf(this).myF();
然后查找[[Prototype]]
的{{1}}, childInstance
,而不是Child.prototype
。
输出:“我做了derp ??”致电(Parent.prototype
的{{1}}):myF
引用Child.prototype
,即this
[[原型]]财产。所以Child.prototype
的第二次调用最终返回childInstances
(有点)。输出:“我做了derp ??”
调用Object.getPrototypeOf(this).myF();
创建的Parent.prototype
实例myF
:最后,调用父项Parent.prototype
。输出:'derp'
由于您的Object.create
位于myF
函数调用之后,输出的顺序相反。下图说明了代码的遍历方式:
因此,您对console.log("did I derp??")
所指的内容的假设是错误的。
来自@LukeP: https://jsfiddle.net/Lpxq78bs/28/
为了避免这种混淆,并且由于您正在使用经典继承模式,您可以查看ES6 Classes。以下是您要做的事情的一个粗略示例:
myF
我希望这有助于理解会发生什么。
答案 1 :(得分:6)
您的代码按预期工作,您获得的输出是Object.getPrototypeOf
的结果,可以通过
第1步:当您调用childInstance.myF();
时,它会转到下面代码this
引用childInstance本身
Child.prototype.myF = function() {
Object.getPrototypeOf(this).myF();
console.log("did I derp??");
};
然后Object.getPrototypeOf
返回childInstance.[[Prototype]]
Child.prototype
并再次调用myF
方法(在执行方法后稍后打印第二行)但下次{{} 1}}将引用this
。
第2步:在此致电childInstance.[[Prototype]]
指向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]]
并再次调用myF方法,但这次myF方法将打印 derp 因为正在调用Parent.prototype
的myF方法。之后,第二行将打印我做了derp 。
第3步:然后该功能返回到第1步执行第二行,再次打印我做了derp
答案 2 :(得分:0)
如果你必须在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;
};