JavaScript面向对象编程
怎样定义自定义范例
起首须要明白,JavaScript并非传统意义上的OO言语,它并没有class
的观点,
而是包含了另一套非常壮大的原型机制。它的范例系统、继续系统都竖立在原型基本之上。
为了投合传统的OO开发者,JavaScript言语的设计者经由历程这套原型系统模拟了传统面向对象言语的编码作风。
简朴来讲,竖立一个自定义范例只须要编写范例的组织函数即可:
javascript
function Person(name, age) { this.name = name; this.age = age; } // 实例化 Person 范例 Person person = new Person("John", 34);
Person
组织函数与平常的函数没有任何区分,只是挪用体式格局不太一样,
经由历程运用new
关键字,转变了平常函数挪用的行动,有点类似于下面如许:
1. Object obj = new Object();
2. Person.call(obj, "John", 34);
3. obj.__proto__ = Person.prototype;
4. return obj;
序号2
中call
函数挪用的作用是转变实行Person函数时的this
为obj
,
序号3
的作用是设置新建实例的原型(一个实例的__proto__
属性是这个实例的原型)。
记着,一切函数(如这里的Person)的prototype
属性默许都是一个Object
实例。
所以序号3
实行后,person.__proto__
恰是Person.prototype
,
这诠释了为何一切的援用范例都派生自Object
。
除此之外,从上面的引见还应当意想到Person
的一切实例的__proto__
属性都是Person.prototype
。
上面定义的属性都是实例的属性,也能够直接为某个范例增加范例的属性(记着,要领也是一个对象),
而这个属性没法经由历程范例的实例访问到,以下面的代码:
javascript
Person.country = "Canada";
另一个例子是ECMAScript 5引入的Object
范例的getPrototypeOf
要领,它能够取得一个实例的原型变量:
javascript
Object.getPrototypeOf = function(instance) { // some code.. };
假如想定义同一个范例一切实例同享的属性(比方要领),能够定义在范例的原型中:
javascript
Person.prototype.logName = function() { console.log(this.name); };
须要注重,经由历程Person
的实例只能读取原型中的属性,而不能重写;
假如尝试重写,实际上是在实例中定义了一个同名的属性,从而屏障了原型中的属性:
javascript
// 并没有转变 Person.prototype.logName的值 person.logName = function() { // some code.. }
形成屏障的原因是当运用对象.属性
时,
是从对象
开始查找属性
,假如没有找到再在其原型中查找,
假如还没有找到,再查找其原型的原型,以此类推在原型链上不停向上查找,
第一次查找到属性
后查找历程就完毕了。
假如想恢复被屏障的原型属性,能够运用delete
操作符:
javascript
delete person.logName;
最好的实践是将实例的属性定义在组织函数中,将要领定义在原型中。
如许每一个实例独享自身的属性,并和其他同范例的实例同享要领:
javascript
// 组织函数 function Person(name, age) { this.name = name; this.age = age; } // 原型 Person.prototype.logName = function() { console.log(this.name); }
以上这类体式格局定义的Person
范例,能够经由历程instanceof
来推断一个实例是不是是Person
范例的:
javascript
Person person = new Person("John", 34); console.log(john instanceof Person); // true
实际上instanceof
是经由历程实例的原型链来推断一个对象是不是某个范例的实例的,细致的细节后面会细致引见。
这里起首引见一下怎样取得一个实例的原型对象:
1. isPrototypeOf()
你能够推断一个对象是不是在另一个对象的原型链上涌现:
Person person = new Person("John", 34);
console.log(Person.prototype.isPrototypeOf(person)); // true
2. Object.getPrototypeOf()
你能够获得一个对象的原型。
这个要领是ECMAScript 5引入的,某些IE浏览器并不支撑:
// get the prototype of person instance
Object.getPrototypeOf(person);
3. __proto__
JavaScript中每一个对象都有一个指向其原型的内部属性,
在某些浏览器(如Chrome)中能够运用它们。
既然能够将属性定义在实例自身或它的原型链中,那末可不能够推断某个属性细致是在那里定义呢?固然能够:
1. hasOwnProperty()
假如属性在实例自身涌现,则返回true
:
console.log(person.hasOwnProperty("name")); // true
console.log(person.hasOwnProperty("logName")); // false
2. in
操作符,假如属性在实例或其原型链中涌现,则返回true
alert("logName" in person); // true
应用in
操作符,我们还能够罗列出一个实例和它原型链中一切可罗列的属性:
for (var propertyName in person) {
// log all property name and its value
console.log(propertyName + "\t\t" + person[propertyName]);
}
最厥后引见一下instanceof
的道理,假定实行下面的代码:
javascript
function Person(name, age) { this.name = name; this.age = age; } function Student(name, age, school) { Person.call(this, name, age); this.school = school; } // Student 继续了 Person Student.prototype = new Person(); Student student = new Student("Adam", 30, " School");
关于继续的细节以后再细致议论,这里只须要明白我们将Student
范例的原型赋值为一个Person
实例。
此时student
的原型链能够示意为:
student.__proto__
==>Person
实例(假定为person
)person.__proto__
==>Object
实例(假定为object
)object.__proto__
==>null
(到顶了)
或许会有人迷惑person
的原型为何是Object
实例,
这是由于一切的要领(比方这里的Person
、Student
)的prototype属性默许都是一个Object范例的实例,
这也证明了为何一切的内置范例和自定义范例无一破例全部都派生自Object
范例。
当实行下面的代码时:
javascript
console.log(student instanceof Student); // true
实际上是推断Student.prototype是不是在student的原型链中涌现,假如涌现了则返回true。
这里Student.prototype
是person
,是student的原型,所以返回true。
再看:
javascript
console.log(student instanceof Person); // true
同理由于存在Person.prototype === student.__proto__.__proto__
,所以返回true。
看到这里相信你应当对prototype
和__proto__
的关联有了比较清楚的理解了。
它们之间的关联能够总结为:
__proto__
:__proto__
is the actual object that is used in the lookup chain to resolve methods.
It is a property that all objects have.
This is the property which is used by the JavaScript engine for inheritance.
According to ECMA specifications it is supposed to be an internal property,
however most vendors allow it to be accessed and modified.prototype
:prototype
is a property belonging only to functions.
It is used to build__proto__
when the function
happens to be used as a constructor with the new keyword.
继续
能够说JavaScript是Python的另一个极度
——There’s always more than one way to do it.
完成继续也不破例,差别的完成形式有差别的运用场景,
各有上风和不足,这里只引见一个最常被运用的形式——组合继续形式,直接看例子:
javascript
/* * 基类,定义属性 */ function Person(name, age) { this.name = name; this.age = age; } /* * 基类,定义要领 */ Person.prototype.selfIntroduce = function () { console.log("name: " + this.name); console.log("age: " + this.age); } /* * 子类,定义子类的属性 */ function Student(name, age, school) { // 挪用基类的组织函数 Person.call(this, name, age); this.school = school; } // 使子类继续基类 Student.prototype = new Person(); /* * 定义子类的要领 */ Student.prototype.goToSchool = function() { // some code.. } /* * 扩大并挪用了超类的要领 */ Student.prototype.selfIntroduce = function () { Student.prototype.__proto__.selfIntroduce.call(this); console.log("school: " + this.school); } var student = new Student("John", 22, "My School"); student.selfIntroduce();