在Javascript中扩展对象

Wit*_*tuz 151 javascript extends prototype function object

我目前正在从Java转换为Javascript,我有点难以弄清楚如何按照我希望的方式扩展对象.

我见过互联网上有几个人使用一种名为extend on object的方法.代码如下所示:

var Person = {
   name : 'Blank',
   age  : 22
}

var Robot = Person.extend({
   name : 'Robo',
   age  : 4
)}

var robot = new Robot();
alert(robot.name); //Should return 'Robo'
Run Code Online (Sandbox Code Playgroud)

有谁知道如何使这项工作?我听说你需要写

Object.prototype.extend = function(...);
Run Code Online (Sandbox Code Playgroud)

但我不知道如何使这个系统工作.如果不可能,请告诉我另一个扩展对象的替代方案.

osa*_*oun 191

编辑:
在使用代码之前,请检查user2491400的评论报告有关简单分配的副作用prototype.

原始答案:

你想从Person的原型对象'继承':

var Person = function(name){
  this.name = name;
  this.type = 'human';
}

Person.prototype.info = function(){
  console.log("Name:", this.name, "Type:", this.type);
}

var Robot = function(name){
  Person.apply(this,arguments)
  this.name = name;
  this.type = 'robot';
}

Robot.prototype = Person.prototype;        // Set prototype to Person's
Robot.prototype.constructor = Robot;   // Set constructor back to Robot

person = new Person("Bob");
robot = new Robot("Boutros");

person.info();
// Name: Bob Type: human

robot.info();
// Name: Boutros Type: robot
Run Code Online (Sandbox Code Playgroud)

  • @AlexisWilke:是的,你应该调用`Person.apply(this,arguments);`.使用`Robot.prototype = Object.create(Person.prototype);`而不是`new Person();`也会更好. (20认同)
  • 这个例子完全错了.通过这样做,你改变了人的原型.这不是继承,你可能会在Person类中造成巨大的混乱.请参阅建议使用Object.create()的答案.这是做事的正确方法. (18认同)
  • 正如Felix所说,'Robot.prototype = Person.prototype;' 如果有人希望'Robot'类型拥有自己的原型实例,那么这是一个坏主意.添加新的机器人特定功能也会将其添加到人. (17认同)
  • @osahyoun这个答案在谷歌的搜索中排名很高.我真的建议你按照其他评论的建议修复代码并更正原型链. (6认同)
  • 我有一个问题:当你执行`new Robot()`时,如何调用`Person()`构造函数?在我看来,你应该调用那个基类构造函数,而不是在`Robot()`构造函数中执行`this.name = name;`... (4认同)
  • 而不是分配Person.prototype; 到Robot.prototype,使用:Object.create(Person.prototype) (3认同)
  • @ user2491400这不是完全错误的.它将完成扩展Person对象的基本工作.但更重要的是,它适用于IE <= 8.如果你没有任何IE 6/7/8用户使用你的javascript,那么一定要使用新的和改进的Object.create方法. (2认同)
  • @DaveS它_is_完全错误,因为它没有扩展Person类.扩展一个类至少需要创建一个新的原型.这里如果我们想要向Robot添加方法,我们不能在不修改Person原型的情况下这样做.Person和Robot之间的唯一区别是构造函数.没有Object.create()不是借口,因为在Object.create()存在之前很久就存在类扩展技术.简单的形式是`Robot.prototype = new Person();`.这个答案是一个耻辱,它应该删除,因为它可能误导初级开发人员. (2认同)

Cal*_*twr 87

没有"新"关键字的世界.

使用Object.create()的语法更简单.

我在营地认为Javascript应该尝试没有"新"的生活.它是一种无类语言,它不需要构造函数.您只需创建对象,然后扩展或变形它们.当然,存在陷阱,但这更加强大和简单:

// base `Person` prototype
const Person = {
   name : '',
   age  : 22,
   type : 'human',
   greet() {
       console.log('Hi, my name is ' + this.name + ' and I am a ' + this.type + '.' )
   }
}

// create an instance of `Person`:
const skywalker = Object.create(Person)
skywalker.name = 'Anakin Skywalker'
skywalker.greet() // 'Hi, my name is Anakin Skywalker and I am a human.'
Run Code Online (Sandbox Code Playgroud)

扩展基础原型

// create a `Robot` prototype by extending the `Person` prototype:
const Robot = Object.create(Person)
Robot.type = 'robot'
Robot.variant = '' // add properties for Robot prototype

// Robots speak in binaries, so we need a different greet function:
Robot.greet = function() { //some function to convert strings to binary }
Run Code Online (Sandbox Code Playgroud)

更深层次

// create a new instance `Robot`
const Astromech = Object.create(Robot)
Astromech.variant = 'astromech'

const r2d2 = Object.create(Astromech)
r2d2.name = 'R2D2'
r2d2.greet() // '0000111010101011100111....'

// morphing the `Robot` object doesn't affect `Person` prototypes
skywalker.greet() // 'Hi, my name is Anakin Skywalker and I am a human.'
Run Code Online (Sandbox Code Playgroud)

进一步阅读

**更新3月10日18.采用ES6语法并使用constlet.添加了示例,说明如何使属性不可变.

**更新于1月17日.包括ES6 Object.assign().

如您所见,分配需要多个语句.使用ES6,您可以使用#assign方法缩短分配.(对于在旧版浏览器上使用的polyfill,请参阅ES6上的MDN.)

//instead of this
const Robot = Object.create(Person)
Robot.name = "Robot"
Robot.madeOf = "metal"
Robot.powerConsumption_kW = 5

//you can do this
const Robot = Object.create(Person)
Object.assign(Robot, {
    name: "Robot",
    madeOf: "metal",
    powerConsumption_kWh: 5,
    fullCharge_kWh: 10,
    currentCharge_kWh: 5
})

//attach some methods unique to Robot prototype.
Robot.charge = function(kWh) {
    let self = this
    this.currentCharge_kWh = Math.min(self.fullCharge_kWh, self.currentCharge_kWh + kWh)
    var percentageCharged = this.currentCharge_kWh / this.fullCharge_kWh * 100
    console.log(this.name + (percentageCharged === 100) ? ' is fully charged.' : ' is ' + percentageCharged +'% charged.')
}

Robot.charge(5) // outputs "Robot is fully charged."
Run Code Online (Sandbox Code Playgroud)

你也可以使用Object.create()的第二个参数aka propertiesObject,我发现它有点过于冗长.在#assign上使用它的唯一原因是你需要对值进行更多控制,即可写性/可配置性等等.注意如何Robot严格地用金属制成.

const Robot = Object.create(Person, {
    madeOf: { 
        value: "metal",
        writable: false,
        configurable: false,
        enumerable: true
    },
    powerConsumption: {
        value: "5kWh",
        writable: true,
        configurable: true,
        enumerable: true   
    }
})
Run Code Online (Sandbox Code Playgroud)

并且所有原型Robot都不能用其他东西制成.

const polymerRobot = Object.create(Robot)

polymerRobot.madeOf = 'polymer'

console.log(polymerRobot.madeOf) // outputs 'metal'
Run Code Online (Sandbox Code Playgroud)

这种模式有些可能导致"经典训练"的程序员绊倒.尽管如此,我发现这种模式更具可读性.

  • 有一个不使用构造函数的upvote. (6认同)
  • 1)`#Object.assign`确实像一个好的选择.但浏览器支持较低的atm.2)您将使用对象的`__proto__`属性来访问其原型的greet函数.然后你调用原型greet函数,传入被调用者的作用域.在这种情况下,函数是一个控制台日志,因此无法"追加".但是通过这个例子,我认为你得到了漂移.`skywalker.greet = function(){this .__ proto __.greet.call(this); console.log('a greet override'); }` (2认同)

小智 50

如果还没有想出办法,可以使用JavaScript对象的关联属性将扩展函数添加到Object.prototype如下所示.

Object.prototype.extend = function(obj) {
   for (var i in obj) {
      if (obj.hasOwnProperty(i)) {
         this[i] = obj[i];
      }
   }
};
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用此功能,如下所示.

var o = { member: "some member" };
var x = { extension: "some extension" };

o.extend(x);
Run Code Online (Sandbox Code Playgroud)

  • 请注意,当在'parent'类中使用对象/数组时,这将创建指向'child'类中原始对象的指针.详细说明:如果父类中有对象或数组,则在该基类上扩展的子类中对其进行修改,实际上会对在同一基类上扩展的所有子类进行修改. (17认同)

Lio*_*rom 29

不同的方法:Object.create

根据@ osahyoun的回答,我发现以下是从Person的原型对象'继承'的更好和有效的方法:

function Person(name){
    this.name = name;
    this.type = 'human';
}

Person.prototype.info = function(){
    console.log("Name:", this.name, "Type:", this.type);
}

function Robot(name){
    Person.call(this, name)
    this.type = 'robot';
}

// Set Robot's prototype to Person's prototype by
// creating a new object that inherits from Person.prototype,
// and assigning it to Robot.prototype
Robot.prototype = Object.create(Person.prototype);

// Set constructor back to Robot
Robot.prototype.constructor = Robot;
Run Code Online (Sandbox Code Playgroud)

创建新实例:

var person = new Person("Bob");
var robot = new Robot("Boutros");

person.info(); // Name: Bob Type: human
robot.info();  // Name: Boutros Type: robot
Run Code Online (Sandbox Code Playgroud)

现在,通过使用Object.create:

Person.prototype.constructor !== Robot
Run Code Online (Sandbox Code Playgroud)

另请查看MDN文档.

  • 只是想说@GaretClaborn它可以正常工作,但你没有将`name`参数传递给父构造函数,如下所示:https://jsfiddle.net/3brm0a7a/3/(差异在第8行) (2认同)

KrI*_*HnA 23

在ES6中,有Object.assign复制属性值.{}如果您不想修改目标对象(传递的第一个参数),请将其用作第一个参数.

Object.assign

有关详情,请参阅链接,

MDN - Object.assign()

如果您需要的是Polyfill for ES5,链接也提供它.:)


Har*_*old 18

另一年后,我可以告诉你还有另一个很好的答案.

如果您不喜欢原型设计的工作方式以扩展对象/类,请参考:https://github.com/haroldiedema/joii

可能性的快速示例代码(以及更多):

var Person = Class({

    username: 'John',
    role: 'Employee',

    __construct: function(name, role) {
        this.username = name;
        this.role = role;
    },

    getNameAndRole: function() {
        return this.username + ' - ' + this.role;
    }

});

var Manager = Class({ extends: Person }, {

  __construct: function(name)
  {
      this.super('__construct', name, 'Manager');
  }

});

var m = new Manager('John');
console.log(m.getNameAndRole()); // Prints: "John - Manager"
Run Code Online (Sandbox Code Playgroud)


Ali*_*baz 10

那些仍在努力寻求简单和最佳方法的人,可以Spread Syntax用来扩展对象.

var person1 = {
      name: "Blank",
      age: 22
    };

var person2 = {
      name: "Robo",
      age: 4,
      height: '6 feet'
    };
// spread syntax
let newObj = { ...person1, ...person2 };
console.log(newObj.height);
Run Code Online (Sandbox Code Playgroud)

注意:请记住,最右边的属性将具有优先权.在这个例子中,person2在右侧,因此newObj将在其中包含名称Robo.


250*_*50R 8

您可能要考虑使用助手库像underscore.js,它有它自己的实现extend().

通过查看它的源代码,这也是一种很好的学习方式.带注释的源代码页非常有用.


Nie*_*ael 6

Mozilla'宣布'从ECMAScript 6.0扩展的对象:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/extends

注意:这是一项实验技术,是ECMAScript 6(Harmony)提案的一部分.

class Square extends Polygon {
  constructor(length) {
    // Here, it calls the parent class' constructor with lengths
    // provided for the Polygon's width and height
    super(length, length);
    // Note: In derived classes, super() must be called before you
    // can use 'this'. Leaving this out will cause a reference error.
    this.name = 'Square';
  }

  get area() {
    return this.height * this.width;
  }

  set area(value) {
    this.area = value;     } 
}
Run Code Online (Sandbox Code Playgroud)

这项技术可在Gecko(谷歌浏览器/火狐) - 2015年3月夜间版本中使用.


Cez*_*wak 5

在大多数项目中都有一些对象扩展的实现:underscore、jquery、lodash: extend

还有纯 javascript 实现,它是 ECMAscript 6 的一部分:Object.assign: https: //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign