我尝试不使用ES6关键字(例如,不使用class
,super
或extends
来模仿此javascript):
class Foo {
constructor() {
if (!new.target)
console.log('Foo() must be called with new');
}
}
class Bar extends Foo {
constructor() {
super(...arguments);
}
}
var bar = new Bar();
var barIsFoo = bar instanceof Foo;
console.log(barIsFoo); // true
我已经走了这么远,但它们并不相等。以下抛出(我登录),而后者则不抛出:
function Foo() {
if (!new.target)
console.log('Foo() must be called with new');
}
function Bar() {
Foo.apply(this, arguments)
}
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.constructor = Bar;
var bar = new Bar();
var barIsFoo = bar instanceof Foo;
console.log(barIsFoo);
那么,当我从new.target
调用Foo
时如何模拟为Bar
提供值?
因此,似乎没有apply
或call
允许传递new.target
。我想这会违背new.target
的目的(尽管JS中的所有内容都是公开的这一事实确实吸引了我)。
因此,要在ES5中进行仿真,我们需要添加一些内容。
以下答案中的一个解决方案分配了一个新对象。
此解决方案添加了新功能construct
,这些功能可以在ES5中照常链接,并使该功能本身可以做任何事情,仅需检查其是否用作构造函数即可。
function Foo() {
if (!new.target)
throw 'Foo() must be called with new';
console.log('Foo new check');
Foo.prototype.construct.apply(this, arguments);
}
Foo.prototype.construct = function() {
console.log('Foo construction logic');
}
function Bar() {
if (!new.target)
throw 'Bar() must be called with new';
console.log('Bar new check');
Bar.prototype.construct.apply(this, arguments);
}
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.constructor = Bar;
Bar.prototype.construct = function() {
// super()
Foo.prototype.construct.apply(this, arguments);
console.log('Bar construction logic');
}
var bar = new Bar();
var barIsFoo = bar instanceof Foo;
console.log(barIsFoo);
最重要的是,似乎ES6功能不是不是,只是ES5上的语法糖。当然,他们可以只添加一个Function.prototype.super(target, arguments, newTarget)
,然后我们就可以使用它。我希望他们能做到!
只有super
可以使用Javascript调用函数,并且无法立即使this
可用。因此super
是唯一的。 super
只能在constructor
的上下文中调用,而class
只能在super
的上下文中使用。因此,所有这些关键字对于使this
都是必需的。因此,Javascript引入了非常具体的面向对象功能。看起来像在“原型”概念之上构建语言是有局限性的。
这是可耻的...
我想知道为什么javascript突然决定强制执行这个不变式。 super
在调用super
之前不可用。为什么不把BaseType.prototype.constructor.call(this, ...)
简化为super
呢?为什么不让它被多次调用?我们可以用Javascript的许多其他方式来解决问题,为什么现在就开始执行呢?
反正...
因此,双重底线是,存在一个早期绑定的Javascript调用foo.bar()
,它没有等效的后期绑定(例如,bar.call('foo')
可以通过{{ 1}})。
答案 0 :(得分:4)
使用Object.assign
将父构造函数的new Foo(...arguments)
分配给实例:
function Foo(arg) {
if (!new.target)
throw 'Foo() must be called with new';
this.arg = arg;
this.fooProp = 'fooProp';
}
function Bar() {
Object.assign(
this,
new Foo(...arguments)
);
this.barProp = 'barProp';
}
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.constructor = Bar;
var bar = new Bar('abc');
var barIsFoo = bar instanceof Foo;
console.log(barIsFoo);
console.log(bar);
但是new Foo(...arguments)
是ES6语法。要将其翻译为ES5,请使用
new (Function.prototype.bind.apply(Foo, [null, ...arguments]))()
(负责new
部分),然后再次转换为
new (Function.prototype.bind.apply(Foo, [null].concat(Array.prototype.slice.call(arguments))))()
function Foo(arg) {
if (!new.target)
throw 'Foo() must be called with new';
this.arg = arg;
this.fooProp = 'fooProp';
}
function Bar() {
Object.assign(
this,
new (Function.prototype.bind.apply(Foo, [null].concat(Array.prototype.slice.call(arguments))))()
);
this.barProp = 'barProp';
}
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.constructor = Bar;
var bar = new Bar('abc');
var barIsFoo = bar instanceof Foo;
console.log(barIsFoo);
console.log(bar);
答案 1 :(得分:0)
ES6类语法不是ES5的语法糖,但 却是其他ES6功能的语法糖。
class Bar extends Foo {
constructor() {
super(...arguments);
}
}
非常类似于
let Bar = function(...args) {
const _this = Reflect.construct(Object.getPrototypeOf(Bar), args, new.target);
return _this;
};
Object.setPrototypeOf(Bar, Foo);
Object.setPrototypeOf(Bar.prototype, Foo.prototype);
其中Reflect.construct
使用给定的new.target
值构造和对象,并使用一组参数调用给定的构造函数。
答案 2 :(得分:0)
要编写抽象类,您需要确保在被调用类的原型的上下文中调用父类的构造函数。 为此,ES6引入了“ Reflect.construct”。
对于ES5,您可以实现自己的“拐杖”。
// Reflect.construct simulation for ES5
class Reflect2 {
static construct(TargetClass,args,ProtoClass){
let oldProto= TargetClass.prototype;
let desc=Object.getOwnPropertyDescriptor(TargetClass,'prototype');
if(desc.hasOwnProperty('value') && desc.writable===false){
if(desc.configurable===true){
desc.writable=true;
Object.defineProperty(TargetClass,'prototype',desc);
}
}
TargetClass.prototype=ProtoClass.prototype;
let self=new (Function.prototype.bind.apply(TargetClass,args));
TargetClass.prototype=oldProto;
return self;
}
}
function ParentClass(){
}
ParentClass.prototype.constructor=ParentClass;
function MyClass(){
if(new.target===undefined){throw Error();}
let self=Reflect2.construct (ParentClass,[],new.target);
// your code
return self;
}
MyClass.prototype=Object.create(ParentClass.prototype,{
constructor:{
value:MyClass
}
});
let a=new MyClass();
console.log(Object.getPrototypeOf(a)===MyClass.prototype);
ES6中的“ super”与“ this”相同,只是在父原型的上下文中。例如,当我们通过“ bind()”将对象绑定到函数时,此处仅将对象“ this”绑定到原型对象。 “ super”可以通过两种方式实现:
ES6中的仿真“超级” 通过代理对象
/**
* @param {object} self - this object
* @param {function} ProtoClass - the class to observe.
Usually the current class for which methods are being written.
* @param {function} [new_target] used in the constructor.
If not passed, then the default will be the prototype constructor for "self"
(Object.getPrototyprOf(self).constructor).
* @param {boolean} [bindSelf] - if true, then the methods and reactive properties
of the parent will work in the context of self, if false, then the methods and
reactive properties will work within the framework of the object in which they
are called (i.e. within the Proxy object).
*/
function Super(self,ProtoClass,new_target,bindSelf=false){
let parentProto=Object.getPrototypeOf(ProtoClass.prototype);
let descs={};
// collect descriptors of all prototypes
do {
let buf=Object.getOwnPropertyDescriptors(parentProto);
for(let prop of Object.keys(buf)){
if(!Object.prototype.hasOwnProperty.call(descs,prop)){
descs[prop]=buf[prop];
}
}
parentProto=Object.getPrototypeOf(parentProto);
}while (parentProto!==null);
// we define the control object
let answer={};
let new_obj=function (...args){
let ParentClass=Object.getPrototypeOf(ProtoClass.prototype).constructor;
if(ParentClass===Object || ProtoClass===ParentClass){
return self;
}
new_target=new_target??Object.getPrototypeOf(self).constructor;
return Reflect.construct(ParentClass,args,new_target);
}
let parent=new Proxy(self,{
get(target,prop){
let desc=descs[prop];
if(desc===undefined){
return;
}
if(desc.hasOwnProperty('value')){
if(bindSelf && typeof desc.value==='function'){
return desc.value.bind(target);
}
return desc.value;
} else
if(desc.hasOwnProperty('get')){
return desc.get.call(target);
}
},
set(target,prop,value){
let desc=descs[prop];
if(desc===undefined || desc.hasOwnProperty('value')){
target[prop]=value;
return true;
}
if(desc.hasOwnProperty('set')){
desc.set.call(target,value);
return true;
}
}
});
Object.defineProperties(answer,{
new:{
value:new_obj
},
parent:{
value:parent
}
});
return answer;
}
使用方法
function A(...args){
}
A.prototype.method=function(){
console.log('hello',this);
return this;
}
A.prototype.constructor=A;
function B(...args){
if(new.target===undefined){throw Error();}
let _super=Super(this,B,new.target);
let self= _super.new(...args);
let b=_super.parent.method(); // return proxy object
/*
in this case, all execution inside the method will occur as if this has a parent prototype.
*/
console.log(b===this);// false
return self;
}
B.prototype=Object.create(A.prototype);
B.prototype.constructor=B;
B.prototype.method=function(){
console.log('bay');
return this;
}
let b=new B();
或
function A(...args){
}
A.prototype.method=function(){
console.log('hello',this);
return this;
}
A.prototype.constructor=A;
function B(...args){
if(new.target===undefined){throw Error();}
/*
we want the methods to execute against the "this" context
and not in the "new Proxy ('this', {})" context
*/
let _super=Super(this,B,new.target,true);// arg[3] true
let self= _super.new(...args);
let b=_super.parent.method(); // return this object
console.log(b===this);// true
return self;
}
B.prototype=Object.create(A.prototype);
B.prototype.constructor=B;
B.prototype.method=function(){
console.log('bay');
return this;
}
let b=new B();
对于ES5代理,可以用常规对象替换,其中“ this”到“ bind”绑定到方法。
function Super(self,ProtoClass,new_target){
let parentProto=Object.getPrototypeOf(ProtoClass.prototype);
let parent={};
// properties all prototypes
do {
let buf=Object.getOwnPropertyDescriptors(parentProto);
for(let prop of Object.keys(buf)){
if(!Object.prototype.hasOwnProperty.call(parent,prop)){
let desc=buf[prop];
if(buf[prop].hasOwnProperty('get') ){
desc.get=buf[prop].get.bind(self);
}
if(buf[prop].hasOwnProperty('set') ){
desc.set=buf[prop].set.bind(self);
}
if(buf[prop].hasOwnProperty('value') ){
if (typeof buf[prop].value === 'function') {
desc.value=buf[prop].value.bind(self);
} else{
delete desc.value;
delete desc.writable;
desc.get=function (){
return buf[prop];
};
desc.set=function(v){
self[prop]=v;
};
}
}
Object.defineProperty(parent,prop,desc);
}
}
parentProto=Object.getPrototypeOf(parentProto);
} while (parentProto!==null);
// we define the control object
let answer={};
let new_obj=function (){
let ParentClass=Object.getPrototypeOf(ProtoClass.prototype).constructor;
if(ParentClass===Object || ProtoClass===ParentClass){
return self;
}
new_target=new_target??Object.getPrototypeOf(self).constructor;
// We described the "Reflect.construct method" above. see Reflect2.construct
return Reflect.construct(ParentClass,Array.prototype.slice.call(arguments),new_target);
}
Object.defineProperties(answer,{
new:{
value:new_obj
},
parent:{
value:parent
}
});
return answer;
}
示例
function A(...args){
}
A.prototype.method=function(){
console.log('hello',this);
return this;
}
A.prototype.constructor=A;
function B(...args){
if(new.target===undefined){throw Error();}
let _super=Super(this,B,new.target);
let self= _super.new(...args);
let b=_super.parent.method();
console.log(b===this);
return self;
}
B.prototype=Object.create(A.prototype);
B.prototype.constructor=B;
B.prototype.method=function(){
console.log('bay');
return this;
}
let b=new B();
答案 3 :(得分:0)
使用 if (!new.target)
的目的是检查 Foo
是否使用 new
(作为构造函数)调用,在这种情况下,我们可以使用 if(this instanceof Foo)
代替。
this
可以是 Foo
或 Bar
的实例,无论您调用 new Foo()
还是 Foo.apply(this)
,this instanceof Foo
始终可以返回true
而如果您只调用 false
(没有 Foo()
或 new
/apply
),它将返回 call
。
试试这个:
function Foo() {
if (!this instanceof Foo)
console.log('Foo() must be called with new');
}
function Bar() {
Foo.apply(this, arguments)
}
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.constructor = Bar;
var bar = new Bar();
var barIsFoo = bar instanceof Foo;
console.log(barIsFoo);