In most programming languages, there are classes and objects, and one class can inherit other classes.
In JavaScript, inheritance is prototype-based, which means there are no classes in JavaScript, instead one object inherits another object. :)
1. inheritance, the proto
In JavaScript, when an object rabbit inherits another object animal, this means that there will be a special attribute in the rabbit object: rabbit.__proto__ = animal;
When accessing the rabbit object, if the interpreter cannot find the attribute in rabbit, then it will follow the __proto__ chain and look for
in the animal object.
The __proto__ attribute in the chestnut is only accessible in Chrome and FireFox. Please see a chestnut:
var animal = { eats: true } var rabbit = { jumps: true } rabbit.__proto__ = animal // inherit alert(rabbit.eats) // true
The eats attribute is accessed from the animal object.
If the property has been found in the rabbit object, the proto property will not be checked.
Another example, when the subclass also has the eats attribute, the one in the parent class will not be accessed.
var animal = { eats: true } var fedUpRabbit = { eats: false} fedUpRabbit.__proto__ = animal alert(fedUpRabbit.eats) // false
You can also add a function in animal, and it can also be accessed in rabbit.
var animal = { eat: function() { alert( "I'm full" ) this.full = true } } var rabbit = { jump: function() { /* something */ } } rabbit.__proto__ = animal
(1) rabbit.eat():
The rabbit.eat() function is executed in the following two steps:
First, the interpreter looks for rabbit.eat. There is no eat function in rabbit, so it looks up rabbit.__proto__ and finds it in animal.
The function is run with this = rabbit. The this value has absolutely nothing to do with the __proto__ attribute.
Therefore, this.full = true in rabbit:
Let's see what new discovery we have here. An object calls a parent class function, but this still points to the object itself. This is inheritance.
The object referenced by __proto__ is called a prototype, and animal is rabbit’s prototype (Translator’s Note: This is because rabbit’s __proto__ attribute refers to animal’s prototype attribute)
(2) Search when reading, not when writing
When reading an object, such as this.prop, the interpreter looks for the property in its prototype.
When setting a property value, such as this.prop = value, there is no reason to look for it, the property (prop) will be added directly to the object (here this). delete obj.prop is similar, it only deletes the properties of the object itself, and the properties in the prototype remain intact.
(3) About proto
If you're reading the guide, what we call __proto__ here is represented as [[Prototype]] in the guide. The double brackets are important because there is another attribute called prototype.
2. Object.create, Object.getPrototypeOf
__proto__ is a non-standard attribute provided by Chrome/FireFox and remains invisible in other browsers.
All modern browsers except Opera (IE > 9) support two standard functions to handle prototype issues:
Object.ceate(prop[,props])
Create an empty object with the given proto:
var animal = { eats: true } rabbit = Object.create(animal) alert(rabbit.eats) // true
The above code creates an empty rabbit object and sets the prototype to animal
After the rabbit object is created, we can add attributes to it:
var animal = { eats: true } rabbit = Object.create(animal) rabbit.jumps = true
The second parameter props of the Object.creat function is optional, which allows setting properties like a new object. It is omitted here because of the inheritance of our relationship.
(1) Object.getPrototypeOf(obj)
Returns the value of obj.__proto__. This function is standard and can be used in browsers that do not have direct access to the __proto__ attribute.
var animal = { eats: true } rabbit = Object.create(animal) alert( Object.getPrototypeOf(rabbit) === animal ) // true
Modern browsers allow reading the __proto__ attribute value, but not setting it.
3. The prototype
There are some good cross-browser ways to set the __proto__ attribute, which will use constructor functions. remember! Any function creates an object through the new keyword.
A chestnut:
function Rabbit(name) { this.name = name } var rabbit = new Rabbit('John') alert(rabbit.name) // John
The new operation sets the prototype's attributes to the __proto__ attribute of the rabbit object.
Let's take a look at how it works, for example, the new Rabbit object, and Rabbit inherits from animal.
var animal = { eats: true } function Rabbit(name) { this.name = name } Rabbit.prototype = animal var rabbit = new Rabbit('John') alert( rabbit.eats ) // true, because rabbit.__proto__ == animal
Rabbit.prototype = animal literal means: set __proto__ = animal
for all objects created by new Rabbit4. Cross-browser Object.create(proto)
The Object.create(prop) function is powerful because it allows direct inheritance from a given object. It can be simulated by the following code:
function inherit(proto) { function F() {} F.prototype = proto return new F }
inherit(animal) is completely equivalent to Object.create(animal), returning an empty object, and object.__proto__ = animal.
A chestnut:
var animal = { eats: true } var rabbit = inherit(animal) alert(rabbit.eats) // true alert(rabbit.hasOwnProperty('eats')) // false, from prototype
Let’s take a look at how it works:
function inherit(proto) { function F() {} // (1) F.prototype = proto // (2) return new F() // (3) }
(1) 创建了一个新函数,函数没有向this设置任何属性,以此`new F` 会创建一个空对象。
(2) `F.prototype`被设置为proto
(3) `new` F创建了一个空对象,对象的`__proto__ = F.prototype`
(4) Bingo! 我们得到了一个继承`proto`的空对象
这个函数广泛适用于各种库和框架之中。
你的函数接受了一个带有options 的对象
/* options contains menu settings: width, height etc */ function Menu(options) { // ... } 你想设置某些options function Menu(options) { options.width = options.width || 300 // set default value // ... }
。。。但是改变参数值可能会产生一些错误的结果,因为options可能会在外部代码中使用。一个解决办法就是克隆options对象,复制所有的属性到一个新的对象中,在新对象中修改,
怎样用继承来解决这个问题呢? P.S. options可以添加设设置,但是不能被删除。
Solution
你可以继承options,并且在它的子类的中修改或者添加新的属性。
function inherit(proto) { function F() {} F.prototype = proto return new F } function Menu(options) { var opts = inherit(options) opts.width = opts.width || 300 // ... }
所有的操作只在子对象中有效,当Menu方法结束时,外部代码仍然可以使用没有修改的过的options对象。delete操作在这里非常重要,如果width是一个prototype中的属性,delete opts.width不会产生任何作用
5. hasOwnProperty
所有的对象都有hasOwnProperty函数,它可以用来检测一个属性是否对象自身还是属于原型
一个栗子:
function Rabbit(name) { this.name = name } Rabbit.prototype = { eats: true } var rabbit = new Rabbit('John') alert( rabbit.hasOwnProperty('eats') ) // false, in prototype alert( rabbit.hasOwnProperty('name') ) // true, in object
6. Looping with/without inherited properties
for..in循环输出一个对象的所有属性,包括自身的和原型的。
function Rabbit(name) { this.name = name } Rabbit.prototype = { eats: true } var rabbit = new Rabbit('John') for(var p in rabbit) { alert (p + " = " + rabbit[p]) // outputs both "name" and "eats" }
用hasOwnProperty可以过滤得到属于对象自己的属性:
function Rabbit(name) { this.name = name } Rabbit.prototype = { eats: true } var rabbit = new Rabbit('John') for(var p in rabbit) { if (!rabbit.hasOwnProperty(p)) continue // filter out "eats" alert (p + " = " + rabbit[p]) // outputs only "name" }
7. Summary
JavaScript是通过一个特殊的属性proto来实现继承的
当访问一个对象的属性时,如果解释器不能在对象中找到,它就会去对象的原型中继续寻找 对函数属性来说,this指向这个对象,而不是它的原型。
赋值obj.prop = value, 删除delete obj.prop
管理proto:
Chrome和FireFox可以直接访问对象的__proto__属性,大多数现代浏览器支持用Object.getPrototypeOf(obj)只读访问。
Object.create(proto) 可以用给定的proto生成空的子对象,或者通过如下代码达到相同的功能:
function inherit(proto) { function F() {} F.prototype = proto return new F() }
其他方法:
for..in循环输出一个对象的所有属性(包括自身的和原型的)和对象的原型链。
如果一个属性prop属于对象obj那么obj.hasOwnProperty(prop)返回true,否则返回false。