用Javascript扩展对象

2020/10/24 20:01 · javascript ·  · 0评论

我目前正在从Java转换为Javascript,这对我来说很难找出如何以我想要的方式扩展对象。

我已经看到互联网上有几个人使用一种称为对象扩展的方法。该代码将如下所示:

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

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

var robot = new Robot();
alert(robot.name); //Should return 'Robo'

有人知道如何进行这项工作吗?我听说你需要写

Object.prototype.extend = function(...);

但是我不知道如何使该系统正常工作。如果不可能,请告诉我另一种扩展对象的方法。

您想从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.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

没有“ new”关键字的世界。

使用Object.create()更简单的“类似于散文”的语法。

*此示例已针对ES6类和TypeScript更新。

首先,事实上,Javascript是一种原型语言,而不是基于类的。它的真实本质以下面的原型形式表示,您可能会发现它非常简单,类似散文,但功能强大。

TLDR;

const Person = { 
    name: 'Anonymous', // person has a name
    greet: function() { console.log(`Hi, I am ${this.name}.`} 
} 
    
const jack = Object.create(Person)   // jack is a person
jack.name = 'Jack'                   // and has a name 'Jack'
jack.greet()                         // outputs "Hi, I am Jack."

这消除了有时复杂的构造函数模式。新对象继承自旧对象,但可以拥有自己的属性。如果我们尝试从新对象(#greet())中获取新成员所jack缺少Person的成员,则旧对象将提供该成员。

道格拉斯·克罗克福德(Douglas Crockford)的话说:“对象从对象继承。还有什么比面向对象更面向对象的呢?”

您不需要构造函数,不需要new实例化(请阅读为什么不使用new),不需要super,不需要自制__construct您只需创建对象,然后对其进行扩展或变形。

这种模式还提供了不变性(部分或全部)以及getters / setter方法

打字稿

与TypeScript等效的外观相同:

interface Person { 
    name:  string,  
    greet: Function
}

const Person =  {
    name:  'Anonymous',  
    greet:  function(): void  { console.log(`Hi, I am ${this.name}.` }
}  
const jack: Person = Object.create(Person)
jack.name = 'Jack'
jack.greet()

类似于散文的语法:Person原型

const Person = {

   //attributes
   firstName : 'Anonymous', 
   lastName: 'Anonymous',
   birthYear  : 0,
   type : 'human',

   //methods
   name() { return this.firstName + ' ' + this.lastName },
   greet() {
       console.log('Hi, my name is ' + this.name() + ' and I am a ' + this.type + '.' )
   },
   age() {
      // age is a function of birth time.
   }
}

const person = Object.create(Person). // that's it!

干净利落。它的简单性不会影响功能。继续阅读。

创建后代/副本 Person

注意:正确的术语是prototypesdescendants/copies没有classes,也没有必要instances

const Skywalker = Object.create(Person)
Skywalker.lastName = 'Skywalker'

const anakin = Object.create(Skywalker)
anakin.firstName = 'Anakin'
anakin.birthYear = '442 BBY'
anakin.gender = 'male' // you can attach new properties.
anakin.greet() // 'Hi, my name is Anakin Skywalker and I am a human.'

Person.isPrototypeOf(Skywalker) // outputs true
Person.isPrototypeOf(anakin) // outputs true
Skywalker.isPrototypeOf(anakin) // outputs true

如果您觉得不太安全,可以直接将构造函数扔掉,就可以了。一种常见的方法是附加一个#create方法:

Skywalker.create = function(firstName, gender, birthYear) {

    let skywalker = Object.create(Skywalker)

    Object.assign(skywalker, {
        firstName,
        birthYear,
        gender,
        lastName: 'Skywalker',
        type: 'human'
    })

    return skywalker
}

const anakin = Skywalker.create('Anakin', 'male', '442 BBY')

Person原型分支Robot

RobotPerson原型分支后代时,不会影响Skywalkeranakin

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

附加于 Robot

Robot.machineGreet = function() { 
    /*some function to convert strings to binary */ 
}

// Mutating the `Robot` object doesn't affect `Person` prototype and its descendants
anakin.machineGreet() // error

Person.isPrototypeOf(Robot) // outputs true
Robot.isPrototypeOf(Skywalker) // outputs false

在TypeScript中,您还需要扩展Person接口:

interface Robot extends Person {
    machineGreet: Function
}
const Robot: Robot = Object.create(Person)
Robot.machineGreet = function(): void { console.log(101010) }

而且您可以使用Mixins-因为..达斯·维达(Darth Vader)是人还是机器人?

const darthVader = Object.create(anakin)
// for brevity, property assignments are skipped because you get the point by now.
Object.assign(darthVader, Robot)

达斯·维达(Darth Vader)获得以下方法Robot

darthVader.greet() // inherited from `Person`, outputs "Hi, my name is Darth Vader..."
darthVader.machineGreet() // inherited from `Robot`, outputs 001010011010...

随着其他奇怪的事情:

console.log(darthVader.type) // outputs robot.
Robot.isPrototypeOf(darthVader) // returns false.
Person.isPrototypeOf(darthVader) // returns true.

优雅地反映了“现实生活”的主观性:

“他现在比人更是机器,扭曲而邪恶。” -Obi-Wan Kenobi

“我知道你有好处。” - 卢克·天行者

与ES6之前的“经典”等效项进行比较:

function Person (firstName, lastName, birthYear, type) {
    this.firstName = firstName 
    this.lastName = lastName
    this.birthYear = birthYear
    this.type = type
}

// attaching methods
Person.prototype.name = function() { return firstName + ' ' + lastName }
Person.prototype.greet = function() { ... }
Person.prototype.age = function() { ... }

function Skywalker(firstName, birthYear) {
    Person.apply(this, [firstName, 'Skywalker', birthYear, 'human'])
}

// confusing re-pointing...
Skywalker.prototype = Person.prototype
Skywalker.prototype.constructor = Skywalker

const anakin = new Skywalker('Anakin', '442 BBY')

// #isPrototypeOf won't work
Person.isPrototypeOf(anakin) // returns false
Skywalker.isPrototypeOf(anakin) // returns false

如果要提高代码的可读性,则必须使用ES6类,该类在采用率和浏览器兼容性方面有所提高(或与babel无关):

ES6类

class Person {
    constructor(firstName, lastName, birthYear, type) {
        this.firstName = firstName 
        this.lastName = lastName
        this.birthYear = birthYear
        this.type = type
    }
    name() { return this.firstName + ' ' + this.lastName }
    greet() { console.log('Hi, my name is ' + this.name() + ' and I am a ' + this.type + '.' ) }
}

class Skywalker extends Person {
    constructor(firstName, birthYear) {
        super(firstName, 'Skywalker', birthYear, 'human')
    }
}

const anakin = new Skywalker('Anakin', '442 BBY')

// prototype chain inheritance checking is partially fixed.
Person.isPrototypeOf(anakin) // returns false!
Skywalker.isPrototypeOf(anakin) // returns true

诚然,ES6类消除了其中的一些问题。当我五年前写下答案时,ES6类正在萌芽,现在已经成熟了。

但是在ES6类的内部却隐藏了Javascript的真实原型性质。因此,我自然对其实施感到失望。

尽管如此,这并不是说ES6类很差。它提供了许多新功能并标准化了一种可读性强的方式。虽然它确实应该没有使用运营商classnew混淆整个问题。

进一步阅读

可写性,可配置性和免费的Getter和Setter!

对于免费的getter和setter或其他配置,可以使用Object.create()的第二个参数(也称为propertiesObject)。它也可以在#Object.defineProperty#Object.defineProperties中使用

为了说明其有用性,假设我们希望所有对象都Robot严格由金属制成(通过writable: false),并标准化powerConsumption值(通过获取器和设置器)。

const Robot = Object.create(Person, {
    // define your property attributes
    madeOf: { 
        value: "metal",
        writable: false,
        configurable: false,
        enumerable: true
    },
    // getters and setters
    powerConsumption: {
        get() { return this._powerConsumption },
        set(value) { 
            if (value.indexOf('MWh')) return this._powerConsumption = value.replace('M', ',000k') 
            this._powerConsumption = value
            throw new Error('Power consumption format not recognised.')
        }  
    }
})

const newRobot = Object.create(Robot)
newRobot.powerConsumption = '5MWh'
console.log(newRobot.powerConsumption) // outputs 5,000kWh

并且所有的原型Robot不能是madeOf其他东西:

const polymerRobot = Object.create(Robot)
polymerRobot.madeOf = 'polymer'
console.log(polymerRobot.madeOf) // outputs 'metal'

如果您还没有找到解决方法,请使用JavaScript对象的关联属性将扩展功能添加到Object.prototype,如下所示。

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

然后可以如下所示使用此功能。

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

o.extend(x);

不同的方法: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;

创建新实例:

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

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

现在,通过使用Object.create

Person.prototype.constructor !== Robot

另请参阅MDN文档。

在ES6中,您可以使用像

var mergedObj = { ...Obj1, ...Obj2 };

请注意,Object.assign()触发设置器,而扩展语法则不会。

有关更多信息,请参见链接,MDN-扩展语法


旧答案:

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

var mergedObj = Object.assign({}, Obj1, Obj2);

有关更多详细信息,请参见链接,MDN-Object.assign()

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

再过一年,我可以告诉你还有一个不错的答案。

如果您不喜欢为了扩展对象/类而进行原型设计的方式,请查看以下内容: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"

仍在努力寻求简单最佳方法的人,可以使用它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);

注意:请记住,最右边的属性具有优先权。在此示例中,person2在右侧,因此newObj名称为Robo

您可能要考虑使用诸如underscore.js之类的帮助程序库,它具有的自己的实现extend()

通过查看源代码,这也是学习的好方法。注释的源代码页面非常有用。

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

https://developer.mozilla.org/zh-CN/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;     } 
}

Gecko(谷歌浏览器/ Firefox)中提供了此技术-2015年3月3日每晚生成。

在大多数项目中,都有一些对象扩展的实现:下划线,jquery,lodash:extend

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

Function.prototype.extends=function(ParentClass) {
    this.prototype = new ParentClass();
    this.prototype.constructor = this;
}

然后:

function Person() {
    this.name = "anonym"
    this.skills = ["abc"];
}
Person.prototype.profile = function() {
    return this.skills.length // 1
};

function Student() {} //well extends fom Person Class
Student.extends(Person)

var s1 = new Student();
s1.skills.push("")
s1.profile() // 2

更新01/2017:

请忽略我的2015年答案,因为Javascriptextends自ES6(Ecmasctipt6)开始支持关键字

-ES6:

class Person {
   constructor() {
     this.name = "anonym"
     this.skills = ["abc"];
   }

   profile() {
    return this.skills.length // 1
   }

}

Person.MAX_SKILLS = 10;
class Student extends Person {


} //well extends from Person Class

//-----------------
var s1 = new Student();
s1.skills.push("")
s1.profile() // 2

-ES7:

class Person {
    static MAX_SKILLS = 10;
    name = "anonym"
    skills = ["abc"];

    profile() {
      return this.skills.length // 1
    }

}
class Student extends Person {


} //well extends from Person Class

//-----------------
var s1 = new Student();
s1.skills.push("")
s1.profile() // 2

概要:

Javascript使用一种称为原型继承的机制在对象上查找属性时,使用原型继承。当我们在javascript中扩展属性时,我们将从实际对象继承这些属性。它以以下方式工作:

  1. 当请求对象属性时(例如myObj.foomyObj['foo']),JS引擎将首先在对象本身上寻找该属性
  2. 当在对象本身上找不到此属性时,它将爬到原型链上,并查看原型对象。如果在这里也找不到此属性,它将继续攀爬原型链,直到找到该属性。如果找不到该属性,它将引发参考错误。

当我们想从javascript对象扩展时,我们可以简单地在原型链中链接该对象。有很多方法可以实现这一点,我将介绍2种常用方法。

例子:

1。 Object.create()

Object.create()是将对象作为参数并创建新对象的函数。作为参数传递的对象将是新创建的对象的原型。例如:

// prototype of the dog
const dogPrototype = {
  woof: function () { console.log('woof'); }
}

// create 2 dog objects, pass prototype as an argument
const fluffy = Object.create(dogPrototype);
const notFluffy = Object.create(dogPrototype);

// both newly created object inherit the woof 
// function from the dogPrototype
fluffy.woof();
notFluffy.woof();

2.明确设置原型属性

使用构造函数创建对象时,我们可以将add属性设置为其原型对象属性。使用new关键字创建的对象构成了构造函数,其原型设置为构造函数的原型。例如:

// Constructor function object
function Dog (name) {
   name = this.name;
}

// Functions are just objects
// All functions have a prototype property
// When a function is used as a constructor (with the new keyword)
// The newly created object will have the consturctor function's
// prototype as its prototype property
Dog.prototype.woof = function () {
  console.log('woof');
}

// create a new dog instance
const fluffy = new Dog('fluffyGoodBoyyyyy');
// fluffy inherits the woof method
fluffy.woof();

// can check the prototype in the following manner
console.log(Object.getPrototypeOf(fluffy));

您可以使用以下方法简单地做到这一点:

Object.prototype.extend = function(object) {
  // loop through object 
  for (var i in object) {
    // check if the extended object has that property
    if (object.hasOwnProperty(i)) {
      // mow check if the child is also and object so we go through it recursively
      if (typeof this[i] == "object" && this.hasOwnProperty(i) && this[i] != null) {
        this[i].extend(object[i]);
      } else {
        this[i] = object[i];
      }
    }
  }
  return this;
};

更新:我检查,this[i] != null因为null是一个对象

然后像这样使用它:

var options = {
      foo: 'bar',
      baz: 'dar'
    }

    var defaults = {
      foo: false,
      baz: 'car',
      nat: 0
    }

defaults.extend(options);

这样很好地导致:

// defaults will now be
{
  foo: 'bar',
  baz: 'dar',
  nat: 0
}

请添加原因以供下载

  • 无需使用任何外部库进行扩展

  • 在JavaScript中,所有内容都是一个对象(三种基本数据类型除外,甚至在需要时它们也会自动用对象包装)。此外,所有对象都是可变的。

JavaScript中的类人

function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype = {
    getName: function() {
        return this.name;
    },
    getAge: function() {
        return this.age;
    }
}

/* Instantiate the class. */
var alice = new Person('Alice', 93);
var bill = new Person('Bill', 30);

修改特定的实例/对象

alice.displayGreeting = function() 
{
    alert(this.getGreeting());
}

修改班级

Person.prototype.getGreeting = function() 
{
    return 'Hi ' + this.getName() + '!';
};

或简单地说:扩展JSON和OBJECT都相同

var k = {
    name : 'jack',
    age : 30
}

k.gender = 'male'; /*object or json k got extended with new property gender*/

多亏罗斯的伤害,达斯坦·迪亚兹

这将扩展您的属性,并使用对象参数原型创建新的对象,而无需更改传递的对象。

function extend(object) {
    if (object === null)
        throw TypeError;
    if (typeof object !== "object" && typeof object !== "function")
        throw TypeError;
    if (Object.create)
        return Object.create(object);
    function f() {}
    ;
    f.prototype = p;
    return new f();
}

但是,如果要扩展对象而不修改其参数,则可以向对象添加extendProperty。

var Person{
//some code
extend: extendProperty
}

//Enforce type checking an Error report as you wish
    function extendProperty(object) {
        if ((object !== null && (typeof object === "object" || typeof object === "function"))){
            for (var prop in object) {
                if (object.hasOwnProperty(prop))
                    this[prop] = object[prop];
            }
        }else{
            throw TypeError; //Not an object
        }
    }

原型制作是一种不错的方法,但是原型制作有时很危险,并可能导致错误。我更喜欢将其封装到基础对象中,就像Ember.js对Ember.Object.extend和Ember.Object.reopen所做的那样。使用起来更安全。

我创建了一个要点,说明如何设置Ember.Object所使用的类似的东西。

这是链接:https : //gist.github.com/WebCloud/cbfe2d848c80d4b9e9bd

本文地址:http://javascript.askforanswer.com/yongjavascriptkuozhanduixiang.html
文章标签: ,   ,   ,   ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!