我目前正在学习JavaScript / ECMAScript的编程,我正在阅读这本书"专业JavaScript for Web Developers,3rd Edition"。我很享受这本书,同时我在互联网上寻找一些支持这些主题的材料。就像所有OO程序员从一种语言中学习另一种语言一样,我遇到了困难。我目前在第6章,研究遗产(PAG.201,202 - 如果有人拿到这本书,并希望看到这部分内容),我很困惑,没有专注。
我完全理解这个主题背后的机制......但我不明白为什么。例如,本书的作者显示了以下案例(某些行略有调整,注释和分离):
// SUPERTYPE
function SuperType()
{
this.property = true;
}
SuperType.prototype.getSuperValue = function()
{
return this.property;
};
// SUBTYPE
function SubType()
{
this.subproperty = false;
}
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function ()
{
return this.subproperty;
};
// TEST
var instance = new SubType();
alert(instance.getSuperValue()); //true
我理解在ECMAScript中,类型的实例继承了在其原型上配置的属性和方法,并且通过prototype属性可以继承。因此,将一种类型的原型更改为另一种类型将导致行为和属性被重用。
什么驱使我无头,是事实上将A类型的新实例分配为B类原型,其中可以理解B可能有实例,也有A(不是这个)应该发生,但允许发生。)
我的意思是什么?好吧......想象一下,我有一个名为" Vehicle"的超类型。和一个名为" Car"的子类型。如果我使用" Vehicle"的实例作为" Car"的超类型。 (毕竟,一个子类型不知道他的超类型,但只有它的原型 - 如本书中的PAG.185所说的那样),我创造了一些完全抽象的东西作为具体的东西。此外,我使用的只是抽象超类型(?)的许多可能实例中的一个,因为在将来没有什么能阻止超类型在这种类型的代码中拥有更多自身实例,添加更多代码/方法/属性/等。
我真正的问题是......无论如何这是它?种类......开发人员应该担心关联正确的实例,只关注其中一个?
另外,我对原型链有一些疑问。例如,我知道当请求/使用属性/方法时,首先在对象中查找它,然后在其原型中查找(如果未找到)。在这个故事的背景下会发生什么?我的意思是,只在对象的原型链中进行搜索而不运行任何上下文?我之前读过,当使用变量时,它会在一堆上下文中寻找。 上下文堆栈与原型链之间的关系是什么?
我知道我可能会有点无聊/讨厌这个问题,但有人可以用简单而详细的方式向我解释这个吗?
提前感谢您的耐心,时间和关注。
由于有些人真的不理解我想知道的事情,我道歉。以下是所有的摘要:
创建A的新实例,其中A是B的超类型,是允许的,也是可能的,即使A遵循某种抽象?如果这是允许的,它的后果是什么?当实例使用某些东西(属性或方法)时,它们之间的上下文堆栈在哪里?
总结一下:虽然我在问问题,但要意识到我在问这些继承的含义。只是......
答案 0 :(得分:1)
JavaScript 有两个大概念,你似乎在这里触及, Closure / Scope 和 Prototyping / inheritance 。这些与大多数其他现代语言的工作方式不同,因此可能会导致人们对来自更经典语言的 JavaScript 进行混淆。
基本上 JavaScript 中的所有内容都是 Object 。在 JavaScript 中, Objects 有一个名为 prototype 的东西,它是对父 Object 的引用,它可能具有继承的属性由后代对象。 原型也可能是null
,它发生在原型链的顶端。
原型中的通常结构是让更多特定对象从更抽象的对象继承,一直回到Object.prototype
或null
在对象上查找属性时,从该对象开始,如果它没有该属性,则在原型链中上升一级并再次查看。如果您到达null
,那么就没有这样的财产
这也意味着您可以通过在原型链下方具有相同名称的属性来隐藏继承的属性。
function Foo() {
}
Foo.prototype = {};
Foo.prototype.foobar = 'foo';
function Bar() {
// if required
// Foo.call(this);
}
Bar.prototype = Object.create(Foo.prototype); // inhert
Bar.prototype.foobar = 'bar';
var baz = new Bar();
baz.foobar; // "bar"
baz instanceof Bar; // true
baz instanceof Foo; // true
原型可以是任何对象,因此使用另一个对象的实例来设置原型链没有任何违法行为,但是如果您尝试扩展或创建子类型,通常认为这是不好的做法
这是查找变量和标识符的方式。它与原型无关,完全取决于定义的位置。标识符在范围内“陷阱”,其他范围可以“关闭”。
在 JavaScript 中,范围的主要提供者是函数
var a = 1;
function foo() {
var b = 2;
// foo can see `a` and `b` as well as `fizz`, `foo`, `bar` and `baz`
function bar() {
var c = 3;
// bar can see `a`, `b` and `c` as well as `fizz`, `foo`, `bar` and `baz`
}
function baz() {
var d = 4;
// baz can see `a`, `b` and `d` as well as `fizz`, `foo`, `bar` and `baz`
}
}
function fizz() {
var e = 1;
// fizz can see `a` and `e` as well as `fizz` and `foo`
}
但是,如果要在foo
上创建一个试图查找bar
的属性,则会出现 ReferenceError (或 undefined ),同样如果你试图继承foo
foo.meth = function () {return bar;};
foo.meth(); // undefined
function Hello() {
this.bar; // undefined
}
Hello.prototype = foo;
(new Hello()).bar; // undefined
访问bar
的唯一方法是foo
为您执行此操作,例如在return bar;
foo
如果bar
返回了foo
,则变量foo
可以在bar
中看到“生活在”上。如果bar
中没有eval样式的代码,智能垃圾收集器仍然可以清理无法访问的垃圾收集器。
正如函数中的变量不在多个调用之间共享一样,这些变量不会在多个foo
调用之间共享,但是它们将在多个bar
调用之间共享
function foo() {
var a = 0,
b = -1;
function bar() {
return ++a;
}
return bar; // pass `bar` out of `foo`
}
var fn1 = foo(), fn2 = foo();
// by this point, the `b` created by `foo()` may already be cleaned up
fn1(); // 1
fn1(); // 2
fn2(); // 1, it's a different `a` to the `a` in `fn1`
fn2(); // 2