JS面向对象编程

澳门新葡亰3522平台游戏 7

Javascript语言的继承机制,它没有”子类”和”父类”的概念,也没有”类”(class)和”实例”(instance)的区分,全靠一种很奇特的”原型链”(prototype
chain)模式,来实现继承。

学过C++等语言的话,你一定明白面向对象的两个基本概念:

这部分知识也是JavaScript里的核心重点之一,同时也是一个难点。我把学习笔记整理了一下,方便大家学习,同时自己也加深印象。这部分代码的细节很多,需要反复推敲。那我们就开始吧。

  • 类:类是对象的类型模板,例如,定义Student类来表示学生,类本身是一种类型,Student表示学生类型,但不表示任何具体的某个学生;
  • 实例:实例是根据类创建的对象,例如,根据Student类可以创建出xiaoming、xiaohong等多个实例,每个实例表示一个具体的学生,他们全都属于Student类型。

系列目录

  • 深入浅出JavaScript之闭包(Closure)
  • 深入浅出JavaScript之this
  • 深入浅出JavaScript之原型链和继承

所以,类和实例是大多数面向对象编程语言的基本概念。

小试身手

原型链例子(要点写在注释里,可以把代码复制到浏览器里测试,下同)

function foo(){}              //通过function foo(){}定义一个函数对象
foo.prototype.z = 3;          //函数默认带个prototype对象属性   (typeof foo.prototype;//"object")

var obj =new foo();           //我们通过new foo()构造器的方式构造了一个新的对象
obj.y = 2;                    //通过赋值添加两个属性给obj
obj.x = 1;                    //通过这种方式构造对象,对象的原型会指向构造函数的prototype属性,也就是foo.prototype

obj.x; // 1                 //当访问obj.x时,发现obj上有x属性,所以返回1
obj.y; // 2                 //当访问obj.y时,发现obj上有y属性,所以返回2
obj.z; // 3                 //当访问obj.z时,发现obj上没有z属性,那怎么办呢?它不会停止查找,它会查找它的原型,也就是foo.prototype,这时找到z了,所以返回3

//我们用字面量创建的对象或者函数的默认prototype对象,实际上它也是有原型的,它的原型指向Object.prototype,然后Object.prototype也是有原型的,它的原型指向null。
                                   //那这里的Object.prototype有什么作用呢?
typeof obj.toString; // ‘function'  

//我们发现typeof obj.toString是一个函数,但是不管在对象上还是对象的原型上都没有toString方法,因为在它原型链的末端null之前都有个Object.prototype方法,
//而toString正是Object.prototype上面的方法。这也解释了为什么JS基本上所有对象都有toString方法
'z' in obj; // true               //obj.z是从foo.prototype继承而来的,所以'z' in obj返回了true
obj.hasOwnProperty('z'); // false   //但是obj.hasOwnProperty('z')返回了false,表示z不是obj直接对象上的,而是对象的原型链上面的属性。(hsaOwnProperty也是Object.prototype上的方法)

澳门新葡亰3522平台游戏 1

刚才我们访问x,y和z,分别通过原型链去查找,我们可以知道:当我们访问对象的某属性时,而该对象上没有相应属性时,那么它会通过原型链向上查找,一直找到null还没有话,就会返回undefined。

BUT,JavaScript不区分类和实例的概念,而是通过原型(prototype)来实现面向对象编程。

基于原型的继承

澳门新葡亰3522平台游戏 2

function Foo(){
   this.y = 2;     
}

Foo.prototype.x = 1;
var obj3 = new Foo();  //①当使用new去调用的时候,函数会作为构造器去调用②this会指向一个对象(这里是obj3),而这个对象的原型会指向构造器的prototype属性(这里是Foo.prototype)
obj3.y; //2 
obj3.x; //1    //可以看到y是对象上的,x是原型链上的原型(也就是Foo.prototype上)

创建对象

JavaScript的所有数据都可以看成对象(Object)。
三种创建对象的方法:

  1. 对象字面量

 var xiaoming = {
    name: '小明',
    hello: function () {
        console.log('Hello, ' + this.name + '!');
    }
};
  1. 构造函数

function Student(name) {
    this.name = name;
    this.hello = function() {
         console.log('Hello, ' + this.name + '!');
    }
}
var xiaoming = new Student("小明")
  1. Object.create()构造
    Object.create(proto [, propertiesObject ])
    是E5中提出的一种新的对象创建方式,第一个参数是要继承的原型,如果不是一个子函数,可以传一个null,第二个参数是对象的属性描述符,这个参数是可选的。

var Student = {
    name: 'Robot',
    hello: function () {
        console.log('Hello, ' + this.name + '!');
    }
};
var xiaoming = Object.create(Student);

其中,第二种构造函数,如果不写new,Student就是一个普通函数(也可以说是方法Function),它返回undefined。但是,如果写了new,它就变成了一个构造函数,它绑定的this指向新创建的对象,并默认返回this,也就是说,不需要在最后写return
this;了。

澳门新葡亰3522平台游戏,prototype属性与原型

澳门新葡亰3522平台游戏 3

澳门新葡亰3522平台游戏 4

我们再来看看Foo.prototype是什么样的结构,当我们用函数声明去创建一个空函数的时候,那么这个函数就有个prototype属性,并且它默认有两个属性,constructor和__proto__,

constructor属性会指向它本身Foo,__proto__是在chrome中暴露的(不是一个标准属性,知道就行),那么Foo.prototype的原型会指向Object.prototype。因此Object.prototype上

的一些方法toString,valueOf才会被每个一般的对象所使用。

function Foo(){}
typeof Foo.prototype; // "object"
Foo.prototype.x = 1;
var obj3 = new Foo();

总结一下:我们这里有个Foo函数,这个函数有个prototype的对象属性,它的作用就是当使用new
Foo()去构造实例的时候,这个构造器的prototype属性会用作new出来的这些对象的原型。

所以我们要搞清楚,prototype和原型是两回事,prototype是函数对象上的预设属性,原型通常是构造器上的prototype属性。

proto属性与prototype属性

我们只看前两种方法,第一种是直接创建对象,第二种是通过方法(Function)来创建对象。
JavaScript对每个创建的对象都会设置一个__proto__属性,指向该对象的构造函数的原型对象。而如果是通过第二种构造函数的方法来创建的创建的对象还会多出一个prototype属性。
解释如下:

//构造函数
function Student(name) {
    this.name = name;
    this.hello = function() {
         console.log('Hello, ' + this.name + '!');
    }
}
var xiaoming = new Student("小明")
var xiaosha = new Student("小傻")

//对象字面量
var Student1 = {
 name: 'XXX',
 hello: function () {
     console.log('Hello, ' + this.name + '!');
 }
};
var xiaohong = {
    name: "xiaohong"
}
xiaohong.__proto__ = Student1;

在创建xiaohong的时候我们直接把xiaohong.proto =
Student1;为甚么这样写呢,因为它不是Function创建的对象,当然也就不具备prototype属性哈哈。但是上述代码仅用于演示目的。在编写JavaScript代码时,不要直接用obj.__proto__去改变一个对象的原型,并且,低版本的IE也无法使用__proto__

澳门新葡亰3522平台游戏 5

对象的原型链

澳门新葡亰3522平台游戏 6

img3

澳门新葡亰3522平台游戏 7

Img1

说明几点:

  1. 由Img1可以看出,通过构造函数创建的对象除了__proto__属性还有prototype属性。事实上,只要是Function就会有该属性,也即普通函数也有prototype属性,原因是方法(Function)这个对象搞特殊,他还有自己特有的属性——原型属性(prototype),这个属性是一个指针,指向一个对象(这个被指向的对象我们后面细说)。
    2.在JS中,方法(Function)是对象,方法的原型(Function.prototype)也是对象啊