我真的很努力地制作一个可以继承数组所有属性的自定义对象,但其行为与普通实例相同,也就是说,instanceof
和constructor
会表现得像您想要的那样。我已经读过,类声明只是语法糖,所以我从来没有求助于它们(我对它们知之甚少)。
在我取得重大突破之前,我制造了可憎的东西:
function arrayLike() {
let al = [];
//make obj.constructor work
Object.defineProperty(al, 'constructor', {value: arrayLike});
//add methods
//make (obj instanceof arrayLike) == true
return new Proxy(al, {
getPrototypeOf() {
return arrayLike.prototype;
},
})
}
//make (obj instanceof Array) == true
Reflect.setPrototypeOf(arrayLike.prototype, Array.prototype);
碰巧的是,我看到一个非常接近我想做的课堂例子,然后发现它非常适合这项工作:
class arrayLike extends Array {
//add methods
}
在Chrome DevToos中查看它,我可以看到我创建的东西根本没有相同的结构。
如果类声明确实是语法糖,那么如果没有它,如何创建该对象呢?
答案 0 :(得分:2)
Javascript是一种具有继承形式的语言,称为原型继承。
其背后的想法是,给定一个对象,它具有一个称为 prototype 的隐藏属性,该属性是对另一个对象(称为原型对象)的引用。
当您要求javascript引擎为您提供对象属性的值时,这种关系很重要,我们将其称为 foo 只是为了解决这个问题。 javascript引擎将首先检查您的对象,以查看其是否具有名为 foo 的属性:如果在您的对象上定义了该属性,则返回其值并完成搜索。否则,如果您的对象没有名为 foo 的属性,则将搜索其原型对象,并再次重复相同的过程。
递归地重复此过程,直到研究了所有所谓的原型链。原型链的根是一个内置javascript对象,您可以使用表达式 Object.prototype 进行引用,并且是所有其他javascript对象都源自的对象。请注意,如果组成整个原型链的对象中的所有对象中缺少 foo 属性,则返回值 undefined 。
这是内置在javascript中的真正继承形式,而这实际上是ES6 class 键盘背后的业务,这是一种便利,可以隐藏这种混乱并给您以javascript具有形式的印象类继承(类继承已广为人知,大多数程序员发现它比原型继承更容易思考)。
为了获取一个对象并决定其行为类似于数组,您可以做的最基本的操作如下:
const myArray = [];
const myObject = { foo: "bar" }
Object.setPrototypeOf(myObject, myArray);
myObject.push("hello");
myObject.push("world");
console.log(myObject.length); // prints 2
This book是我所知道的关于javascript语言的最佳参考。 This is good too,但是如今有点过时了,而且跟以前的学习一样不容易。
通过使用函数作为构造函数,可以实现比上一个示例复杂得多的示例。实际上,这是实现类继承的ES5老式方法,这是您在ES5时为模仿类所做的事情:
function SpecialArray(name) {
this.name = name;
}
SpecialArray.prototype = [];
// fix the constructor property mess (see the book linked above)
Object.defineProperty(SpecialArray.prototype, "constructor", {
value: SpecialArray,
enumerable: false,
writable: true
});
SpecialArray.prototype.getSalutation = function() {
return "Hello my name is " + this.name;
};
const mySpecialArray = new SpecialArray("enrico");
// you can call the methods and properties defined on Array.prototype
mySpecialArray.push("hello");
mySpecialArray.push("world");
console.log(mySpecialArray.length); // prints 2
// you can use the methods and properties defined on SpecialArray.prototype
console.log(mySpecialArray.name); // prints enrico
console.log(mySpecialArray.getSalutation()); // prints Hello my name is enrico
// important sanity checks to be sure that everything works as expected
console.log(mySpecialArray instanceof Array); // prints true
console.log(mySpecialArray instanceof SpecialArray); // prints true
console.log(mySpecialArray.constructor === SpecialArray); // prints true
// you can iterate over the special array content
for (item of mySpecialArray){
console.log(item);
}
// you can read special array entries
console.log(mySpecialArray[1]); // prints world
答案 1 :(得分:0)
编辑:我研究了babel的转码,发现需要额外的触摸才能正确扩展Array之类的内置类,我们需要先将Array构造函数包装在普通的Wrapper函数中,否则原型链会在施工时断裂。
function _wrapNativeSuper(Class) {
_wrapNativeSuper = function _wrapNativeSuper(Class) {
function Wrapper() {
var instance = Class.apply(this, arguments)
instance.__proto__ = this.__proto__.constructor.prototype;
return instance;
}
Wrapper.prototype = Object.create(Class.prototype, {
constructor: {
value: Wrapper,
enumerable: false,
writable: true,
configurable: true
}
});
Wrapper.__proto__ = Class;
return Wrapper;
};
return _wrapNativeSuper(Class);
}
类声明语法可以做三件事。
因此,要重播class Foo extends Array {}
在老式js中的操作,您需要相应地做这三件事。
// 0. wrap the native Array constructor
// this step is only required when extending built-in objects like Array
var _Array = _wrapNativeSuper(Array)
// 1. setup the constructor
function Foo() { return _Array.apply(this, arguments) }
// 2. setup prototype chain
function __dummy__() { this.constructor = Foo }
__dummy__.prototype = _Array.prototype
Foo.prototype = new __dummy__()
// 3. inherit static properties
Foo.__proto__ = _Array
以下可运行示例:
function _wrapNativeSuper(Class) {
_wrapNativeSuper = function _wrapNativeSuper(Class) {
function Wrapper() {
var instance = Class.apply(this, arguments)
instance.__proto__ = this.__proto__.constructor.prototype;
return instance;
}
Wrapper.prototype = Object.create(Class.prototype, {
constructor: {
value: Wrapper,
enumerable: false,
writable: true,
configurable: true
}
});
Wrapper.__proto__ = Class;
return Wrapper;
};
return _wrapNativeSuper(Class);
}
// 0. wrap the native Array constructor
// this step is only required when extending built-in objects like Array
var _Array = _wrapNativeSuper(Array)
// 1. setup the constructor
function Foo() { return _Array.apply(this, arguments) }
// 2. setup prototype chain
function __dummy__() { this.constructor = Foo }
__dummy__.prototype = _Array.prototype
Foo.prototype = new __dummy__()
// 3. inherit static properties
Foo.__proto__ = _Array
// test
var foo = new Foo;
console.log('instanceof?', foo instanceof Foo);
Foo.prototype.hi = function() { return 'hello' }
console.log('method?', foo.hi());