JavaScript面向对象编程简明教程

JavaScript面向对象编程

怎样定义自定义范例

起首须要明白,JavaScript并非传统意义上的OO言语,它并没有class的观点,
而是包含了另一套非常壮大的原型机制。它的范例系统、继续系统都竖立在原型基本之上。
为了投合传统的OO开发者,JavaScript言语的设计者经由历程这套原型系统模拟了传统面向对象言语的编码作风。

简朴来讲,竖立一个自定义范例只须要编写范例的组织函数即可:

javascriptfunction 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;

序号2call函数挪用的作用是转变实行Person函数时的thisobj
序号3的作用是设置新建实例的原型(一个实例的__proto__属性是这个实例的原型)。
记着,一切函数(如这里的Person)的prototype属性默许都是一个Object实例。
所以序号3实行后,person.__proto__恰是Person.prototype
这诠释了为何一切的援用范例都派生自Object

除此之外,从上面的引见还应当意想到Person的一切实例的__proto__属性都是Person.prototype

上面定义的属性都是实例的属性,也能够直接为某个范例增加范例的属性(记着,要领也是一个对象),
而这个属性没法经由历程范例的实例访问到,以下面的代码:

javascriptPerson.country = "Canada";

另一个例子是ECMAScript 5引入的Object范例的getPrototypeOf要领,它能够取得一个实例的原型变量:

javascriptObject.getPrototypeOf = function(instance) {
  // some code..
};

假如想定义同一个范例一切实例同享的属性(比方要领),能够定义在范例的原型中:

javascriptPerson.prototype.logName = function() {
  console.log(this.name);
};

须要注重,经由历程Person的实例只能读取原型中的属性,而不能重写;
假如尝试重写,实际上是在实例中定义了一个同名的属性,从而屏障了原型中的属性:

javascript// 并没有转变 Person.prototype.logName的值
person.logName = function() {
  // some code..
}

形成屏障的原因是当运用对象.属性时,
是从对象开始查找属性,假如没有找到再在其原型中查找,
假如还没有找到,再查找其原型的原型,以此类推在原型链上不停向上查找,
第一次查找到属性后查找历程就完毕了。
假如想恢复被屏障的原型属性,能够运用delete操作符:

javascriptdelete person.logName;

最好的实践是将实例的属性定义在组织函数中,将要领定义在原型中。
如许每一个实例独享自身的属性,并和其他同范例的实例同享要领:

javascript// 组织函数
function Person(name, age) {
    this.name = name;
    this.age = age;
}
// 原型
Person.prototype.logName = function() {
  console.log(this.name);
}

以上这类体式格局定义的Person范例,能够经由历程instanceof来推断一个实例是不是是Person范例的:

javascriptPerson 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的道理,假定实行下面的代码:

javascriptfunction 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实例,
这是由于一切的要领(比方这里的PersonStudent)的prototype属性默许都是一个Object范例的实例,
这也证明了为何一切的内置范例和自定义范例无一破例全部都派生自Object范例。

当实行下面的代码时:

javascriptconsole.log(student instanceof Student);    // true

实际上是推断Student.prototype是不是在student的原型链中涌现,假如涌现了则返回true。
这里Student.prototypeperson,是student的原型,所以返回true。
再看:

javascriptconsole.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();
    原文作者:bedew
    原文地址: https://segmentfault.com/a/1190000003018161
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞