【问题标题】:Constructor or Object Inheritance in JavaScriptJavaScript 中的构造函数或对象继承
【发布时间】:2013-04-08 14:27:52
【问题描述】:

我是 JavaScript 新手(本周开始学习)。我已经完成了 CodeCademy 课程(实际上只是 Objects 1 && 2 部分,其余部分很无聊)。我以为我学习了构造函数的原型继承,但我已经开始观看 Douglas Crockford: Advanced JavaScript

就在视频的开头,他提到了 2 种继承类型,而我已经意识到,我以为我知道的一切,我都不知道。所以我开始使用 Chrome JavaScript 控制台并尝试使用这两种继承模型来做一些事情。

//create constructor
function MyParent(){ 
    this.name = "joe";
    this.sayHi = function(){ 
        console.log(this.name); 
    }
}

//create child object and inherit from parent
var myChild1 = new MyParent();
myChild1.name
"joe"
myChild1.sayHi()
joe

//add method to MyParent, so it can be accessed from myChild1
MyParent.prototype.sayBye = function(){ console.log("bye");}

myChild1.sayBye()
bye

//create another constructor
function MyParent2(){
    this.sayHi = function(){ 
        console.log("hi"); 
    }
}

//make it inherit from MyParent, overwriting sayHi if object is instance of MyParent2
MyParent2.prototype = new MyParent();

//overwrote sayHi
var myChild11 = new MyParent2(); 
myChild11.sayHi();
hi

//still same as MyParent as its not overwriten in MyParent2
myChild11.name
"joe"

比我尝试使用 Object.create 做同样的事情:

//create an object
 var myParent = { 
    name : "joe",
    sayHi : function(){ 
        console.log(this.name) 
    } 
};

//it works
myParent.sayHi()
joe

//create child object
var myChild = Object.create(myParent);
myChild.sayHi()
joe

//add bye method to myChild
myChild.sayBye = function(){ console.log("bye"); }
myChild.sayBye();
bye

//add bye method to myParent - does not overwrite child's bye
myParent.sayBye = function(){ console.log("bye2"); }

//however adding sayBye2 to parent, becomes available on already created child
myParent.sayBye2 = function(){ console.log("bye2"); }
myChild.sayBye2();
bye2
//but its the parent property
myChild.hasOwnProperty("sayBye2")
false

//could make million objects inherit from each other vertically or horizontally
var myChild2 = Object.create(myChild);
myChild2.name
"joe"

到目前为止,就第一印象而言,我确实喜欢这两个模型,也许我更喜欢后者。它似乎更具表现力和强大。

我在vs. 主题上进行了一些搜索,但找不到可以解释每种方法的优缺点的好文章(除了后者仅由较新的浏览器支持,但有一个简单的解决方法)。我发现的其他帖子只是 tl: dr;有点无聊。

所以我的问题是,我应该使用哪种类型的继承。是否有任何相关的特权?两者都有什么主要的缺点吗?可以混用吗? (例如。我知道你可以将对象作为参数传递给new Constructor()))

谢谢

编辑: 如果浏览器不支持它,看看创建 Object.create() 替代方案的技巧,我意识到黑客是多么简单。它基本上是创建一个空的构造函数,将父对象分配为其原型,并将构建的构造函数作为新对象返回。多么整洁的黑客!

if(typeof Object.create !== "function") { Object.create = function (o) { function F() {}; F.prototype = o; return new F(); }; }

但是这意味着什么?有什么需要注意的问题吗?

【问题讨论】:

    标签: javascript


    【解决方案1】:

    两种方式同样强大。有人说如果你不接受原型方法,你就不是真正的 JavaScript 大师,但实际上一切仍然只是 原型继承,这是风格问题。

    就个人而言,我更喜欢使用经典方法,并使用 new 关键字和 constructor 函数。

    为什么?

    • 经典模型非常有名。
    • 我不必在我的对象上实现init 函数,只需执行constructor 已经执行的操作。您可能已经注意到,原型方法让您没有constructor,因此如果您需要为新创建的运行一些初始化逻辑,您将不得不实现一个初始化函数(通常称为init)对象。

    但是,我发现 原型方法不那么冗长,并且对于没有强大的实现经典继承的语言背景的程序员来说可能更容易理解。

    顺便说一句,使用经典模型应该避免的一件事是像上面那样使用 new 关键字进行继承,因为您运行的父级 constructor 逻辑是不必要的,而且它可能会产生不良副作用。

    您应该使用Object.create,如下所示:

    function A(val) { this.val = val; }
    A.prototype.alertVal = function () { alert(this.val); };
    
    function B(val) {
        A.call(this, val); //call parent constructor
    }
    
    B.prototype = Object.create(A.prototype); //inherit from A
    var b = new B('test');
    b.alertVal(); //test
    

    最后,这完全取决于品味,我尊重两种方式。也许其他人会想用一些真正的优点/缺点来改进这个答案,但我找不到任何东西。

    【讨论】:

    • A.apply 方法是如何工作的?在这种情况下它到底做了什么?
    • 它是否在调用 A 构造函数,并将 this(B 的范围)和参数(B 的范围)传递给 A?
    • @JanNetherdrake 我正在写一些东西,但你明白了,它确保 A 构造函数逻辑通过设置上下文对象(对象为其中this 指向)。如果您调用A();,则上下文对象将是window,这不是您想要的。现在,对于applycall 之间的区别,我建议你去阅读它;)我更新了示例,以便调用父级的构造函数更有意义。
    • 我对应用/调用有点困惑。使用 apply/call 的目的是为了将外部的“this”传递给被调用的函数吗?我想如果你有 2 种方法,内部和外部(内部是一种闭包),如果你没有使用“应用”,你需要把“这个”放在一个变量中,所以它可以被限定为闭包,否则它将无法访问,因为闭包会有自己的“this”?如果我理解错了,请纠正我。
    • @JanNetherdrake,为了简单起见,除非在调用函数时使用newcallapply,否则this 将指向函数所在的objectmyObject.someFunction(); 中调用(. 左侧的对象)。但是,如果someFunction();以这种方式调用,则与window.someFunction();相同,所以this将指向window。现在,对于上述情况,我们希望 A 函数将其上下文对象 (this) 设置为新的 B 实例。由于调用A(val); 会导致this 指向window,所以我们必须使用A.apply(this, val);
    【解决方案2】:

    new 构造对象被认为是邪恶的,因为你可能忘记使用它,然后你添加到this 的任何属性最终都会添加到全局对象中。出于这个原因,我支持第二种方法。

    我学到的另一件事是,您不应该尝试将“经典”OOD 应用于 JavaScript。它有它自己的方式应该被理解,实际上它非常好。我认为 JavaScript 中的大多数对象编程问题都来自尝试将“类”的概念延伸到 JavaScript 上。

    【讨论】:

    • 是的,Crockford 说构造函数名称以大写字母开头的原因是为了提醒我们使用“new”。我对不同的事物持开放态度,这就是我非常喜欢 JS 的原因。所以我不会尝试在 JS 中像 Java 那样思考。
    猜你喜欢
    • 2013-09-17
    • 1970-01-01
    • 2012-06-26
    • 2014-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-04
    • 2023-03-07
    相关资源
    最近更新 更多