如何在JS中将原型附加到对象字面量

4
我正在尝试理解JavaScript的原型性质,并试图创建对象继承,而不使用类似于构造函数的class。
如果它们都是对象字面量,我如何将原型链从Animals附加到Cat和Dog中的每个对象?
此外,是否有一种方法可以使用字面形式(例如myCat)进行Object.create,并添加更多属性?
下面附上代码并粘贴到http://jsbin.com/eqigox/edit#javascript
var Animal = {
    name        : null,
    hairColor   : null,
    legs        : 4,

    getName : function() {
        return this.name;
    },
    getColor : function() {
        console.log("The hair color is " + this.hairColor);
    }
};

/* Somehow Dog extends Animal */

var Dog = {
    bark : function() {
        console.log("Woof! My name is ");
    }
};

/* Somehow Cat Extends Animal */

var Cat = {
    getName : function() {
        return this.name;
    },

    meow : function() {
        console.log("Meow! My name is " + this.name);
    }
};


/* myDog extends Dog */

var myDog = Object.create(Dog);

/* Adding in with dot notation */
myDog.name = "Remi";
myDog.hairColor = "Brown";
myDog.fetch = function() {
    console.log("He runs and brings back it back");
};


/* This would be nice to add properties in litteral form */
var myCat = Object.create(Cat, {
    name        : "Fluffy",
    hairColor   : "white",
    legs : 3, //bad accident!
    chaseBall : function(){
        console.log("It chases a ball");
    }

});

myDog.getColor();

3
除非通过非标准的 __proto__ 属性,否则无法更改现有对象的原型。 - user1106925
3个回答

3
你可以使用Object.create将你定义的对象作为原型。例如:
var Dog = Object.create(Animal, {
    bark : {
        value: function() {
            console.log("Woof! My name is " + this.name);
        }
    }
});

现在您可以创建一个新的Dog对象:
var myDog = Object.create(Dog);
myDog.bark();  // 'Woof! My name is null'
myDog.getColor();  // 'The hair color is null'

示例: http://jsfiddle.net/5Q3W7/1/

或者,如果您没有使用Object.create,可以使用构造函数:

function Animal() {
    this.name = null;
    this.hairColor = null;
    this.legs = 4;
};

Animal.prototype = {
    getName : function() {
        return this.name;
    },
    getColor : function() {
        console.log("The hair color is " + this.hairColor);
    }
}

function Dog() {
}

Dog.prototype = new Animal;
Dog.prototype.bark = function() {
    console.log("Woof! My name is " + this.name);
};

例子: http://jsfiddle.net/5Q3W7/2/ 关于Object.create的更多信息: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/create/ 关于构造函数和原型链的更多信息: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/constructor https://developer.mozilla.org/en/JavaScript/Guide/Inheritance_and_the_prototype_chain提供了有关继承和原型链的更多信息。

哇,谢谢!这正是我正在寻找的 :)我没意识到它已经内置于ES5中了...非常酷!! - SkinnyG33k

0

编辑:完全忽略了Object.create。@jMerliN的答案更合适,除非您需要更广泛的向后兼容性。


AnimalCatDog之间共享Object.prototype,因为它们都是Object的实例,但这并不是你想要放置方法的地方。

你可以通过使用一个extend函数来模拟继承:

var Dog = {
    bark : function() {
        console.log("Woof! My name is ")
    }
}

jQuery.extend(Dog, Animal)

这可能是underscore的_.extend函数或者你自己编写的extend函数,它会简单地复制它的属性:

function extend(obj, source){
    for (var prop in source) {
        obj[prop] = source[prop]
    }
} 

但是要小心引用:像字符串、数字、布尔值这样的“原始”值将被复制,而对象、函数和数组将被引用:

var Animal = {
    tags: [1,2,3]
}

var Dog = {
    //...
}

_.extend(Dog, Animal)

Dog.tags.push(4)

Dog.tags // [1,2,3,4]
Animal.tags // [1,2,3,4] oh no!

没有必要这样做,它容易出错且内存效率低下;直接使用构造函数即可 :)


3
我看不到原帖中有jQuery标签,而且在这种情况下使用如此大的库似乎并不适当,特别是当原帖作者试图理解JavaScript(即ECMAScript)时。此外,extend函数检查自有属性。 - RobG
Object.create 也会设置 [[Prototype]]:这不仅仅是构造函数才会做的。 - gsnedders
@RobG,别这样,我说过 一个扩展函数,提到下划线并给出了实现代码(下划线的代码没有进行自身检查)。没必要对 jQuery 表现出敌意。 - Ricardo Tomasi
@SkinnyGeek1010 不完全是。CoffeeScript使用Crockford的继承技巧(http://javascript.crockford.com/prototypal.html)加上一些补充,这就是真正的原型继承。 - Ricardo Tomasi
@RobG 我感觉你只是执行了 grep jQuery 而没有真正阅读我的答案。它在顶部说了 模拟继承,并提供了一个非常简单的扩展方法(不是 jQuery 的)... - Ricardo Tomasi
显示剩余4条评论

-1

从ES2015及以后的版本开始,您可以在对象字面量中使用文字__proto__作为属性名称来设置原型:

const myProto = {
  propertyExists: function(name) {
    return name in this;
  },
};
const myNumbers = {
  __proto__: myProto,
  array: [1, 6, 7],
};
myNumbers.propertyExists('array'); // => true
myNumbers.propertyExists('collection'); // => false

这里是一篇关于这个主题的很好的帖子,也是我得到例子的来源。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接