我目前正在从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
注意:正确的术语是
prototypes
和descendants/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
Robot
从Person
原型分支后代时,不会影响Skywalker
和anakin
:
// 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类很差。它提供了许多新功能并标准化了一种可读性强的方式。虽然它确实应该没有使用运营商class
和new
混淆整个问题。
进一步阅读
可写性,可配置性和免费的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.assign:https :
//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中扩展属性时,我们将从实际对象继承这些属性。它以以下方式工作:
- 当请求对象属性时(例如
myObj.foo
或myObj['foo']
),JS引擎将首先在对象本身上寻找该属性 - 当在对象本身上找不到此属性时,它将爬到原型链上,并查看原型对象。如果在这里也找不到此属性,它将继续攀爬原型链,直到找到该属性。如果找不到该属性,它将引发参考错误。
当我们想从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
文章标签:extends , function , javascript , object , prototype
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!
评论已关闭!