JavaScript面向对象程序设计

JavaScript中没有类的概念,它不是严格意义上的面向对象语言,而是基于对象(Object-based)的编程语言。下面是读《JavaScript高级程序设计(第三版)》的学习笔记,总结一些经常使用的建立对象和继承的方法。html

1、建立对象

1. 对象字面量

建立对象最简单的方式就是建立一个Object的实例。经过先建立一个对象,再为它添加属性和方法。此方法用对象字面量方式更为直观:
var animal = {
    name : "mimi",
    sayName : function(){
        console.log(this.name);
    }
    //...
    }

★ 这种方法在建立多个对象时会产生大量重复的代码,在建立单个对象时的首选模式。编程

2. 工厂模式

这种方法用函数来封装以特定接口建立对象的细节:app

function animal(name) {
    var o = new Object();//建立新对象
    o.name = "mimi";//给这个对象添加属性
    //...
    return o;//返回这个对象
}

★ 这种方法没有解决对象识别的方法,必定程度上解决建立多个类似对象的问题吧,不常使用。编程语言

3. 构造函数模式

这种方法建立自定义的构造函数,从而自定义对象类型的属性和方法。函数

function Animal(name) {
    this.name = name;//将属性和方法赋给this对象
    this.sayName = function(){
        console.log(this.name);
    }
    //...
}

它没有显式的建立对象也没有返回语句,但在使用new操做符调用后经历了四个步骤:
(1)建立一个对象
(2)将函数的做用域赋给新对象(this指向了新对象)
(3)执行函数中代码
(4)返回新对象性能

全部对象都有一个constructor属性,指向其构造函数。constructor能够用来标识对象类型,可是,要检测对象类型,instanceof操做符更可靠。学习

★ 这种方法的问题在于,每一个方法都会在每一个实例上从新建立一次。this

4. 原型模式

function Animal() {
}
Animal.prototype.name = "mimi";//将方法属性添加到Animal的prototype属性中
Animal.prototype.sayName = function(){
    console.log(this.name);
};
//...

不管什么时候,建立一个函数,都会自动建立一个prototype属性,指向其原型对象,正如前面所说,每一个对象都有一个constructor属性,指向其构造函数。因此Animal.prototype.constructor指向Animal。判断原型对象与实例间关系可用isPrototypeOf()方法:spa

Animal.prototype.isPrototypeOf(animal1);

判断属性存在于实例中,仍是存在与原型中:
属性存在于实例中时:prototype

animal1.hasOwnProperty(name);//true

属性能经过对象访问:

name in animal1;//true
  • 在实例中添加了与原型中同名的属性

咱们将在实例中建立该属性,而屏蔽原型中的属性。固然,咱们能够经过delete操做符彻底删除实例中的该属性而让咱们从新访问到原型中的属性(*  ̄︿ ̄)。

  • 用字面量来实现更简单的原型语法

function Animal() {
}
Animal.prototype = {
    constructor : Animal,//必须必须!由于这样至关于建立了一个新对象并赋值给Animal.prototype,
//此时这个新对象的constructor为默认的构造函数Object啊盆友们( ゚Д゚)ノ
    name : "mimi",
    sayName : function(){
    console.log(this.name);
    }
}

另外,很重要的一点:调用构造函数是会为实例添加一个指向最初原型的[[prototype]]指针,这个链接存在与实例和构造函数的原型对象之间,而不是实例与构造函数间。
咱们将上面将上面的代码稍加修改:

function Animal(name) {
}
animal1 = new Animal()//建立实例1
console.log(animal1.smell);//undefined;此时原型中还未添加smell属性,理所固然。
Animal.prototype.smell = "good";//添加原型属性
console.log(animal1.smell);//能够访问smell属性。

然而,当用对象字面量来添加原型属性时:

function Animal(name) {
}
animal1 = new Animal()//建立实例1
console.log(animal1.smell);//undefined;此时原型中还未添加smell属性,理所固然。
Animal.prototype = {//添加原型属性
    smell : "good";
}
console.log(animal1.smell);//undefined;
//由于animal1在建立时[[prototype]]指针指向的是最初的原型,而字面量法添加原型属性时将对象原型改变了,但[[prototype]]没有跟着一块儿变化,因此没法访问。

★ 这个方法的问题在于:1.它没办法传递初始化参数 2.对于引用类型值的属性(A)来讲,改变一个实例的A属性会直接引发全部实例中的A属性的变化,由于实例中的A属性只是其原型中A属性的一个引用。

5. 组合使用构造函数模式和原型模式

构造函数模式用于定义实例属性,原型模式用于定义方法和共享属性。

function Animal(name) {//添加实例属性
    this.name = name;
    this.color = ["yellow", "black"];
    //...
}
Animal.prototype = {//添加方法和共享属性
    constructor : Animal,
    sayName : function(){
    console.log(this.name);
    }
    //...
}

★ 这个方法就是使用最普遍、认同度最高的建立自定义类型的方法啦。

小总结:第一种和最后一种是比较经常使用的方法了,书中还有提到一些方法,是在前面这些方法都不适用时能够选择使用的,不过我还没碰上啦,因此等之后碰上了再来补充。

2、继承

JavaScript中不支持接口继承(继承方法签名),都是支持实现继承(继承实际的方法),主要依赖原型链来实现。

1. 原型链

将父类的实例赋值给子类的原型,此时,父类的实例中包含一个指向父类原型的指针。子类的原型经过这个指针访问到父类原型中的属性。

function Animal(name) {//父类
    this.name = name;
}
Animal.prototype = {//将属性添加到父类原型
    constructor : Animal,
    sayName : function(){
    console.log(this.name);
    }
}
function Cat() {//子类
}
Cat.prototype = new Animal();//将父类实例赋值给子类原型。
Cat.prototype.constructor = Cat;//prototype被换成另外一个对象,因此constructor属性要重写,不然会指向Animal.
var cat1 = new Cat();

其中的原型链:

clipboard.png
用instanceof操做符断定可得出cat1是Cat、Animal、Object的实例。

★ 此方法存在问题前面已有说起,就是包含引用类型值的原型属性会被全部实例共享,且不能传参。

2. 借用构造函数

在子类型构造函数的内部调用超类型构造函数,经过apply()和call()方法来实现。

function Animal(name) {//父类
    this.name = name;
}
function Cat(name) {//子类
    Animal.call(this, name);//执行了一遍父类型构造函数代码
}
var cat1 = new Cat();

★ 此方法问题在于:1.方法都在构造函数中定义,函数复用无从谈起。2.在超类的原型中定义的方法对子类型也不可见。并且用instanceof也没法断定cat1与Animal的联系。

3. 组合继承

使用原型链实现对原型属性和方法的继承,使用借用构造函数实现对实例属性的继承。

function Animal(name) {//父类添加实例属性
    this.name = name;
}
Animal.prototype = {//父类原型添加方法和属性
    constructor : Animal,
    sayName : function(){
    console.log(this.name);
    }
}
function Cat(name) {//子类
    Animal.call(this, name);//继承属性
}
Cat.prototype = new Animal();//继承方法
Cat.prototype.constructor = Cat;//将constructor指回子类

★ 此方法是JS中最经常使用的继承模式。并且用instanceof 和isPrototypeOf() 也能识别。

目前有实际用的就这些了,总结的比较基础,但愿也总结清楚了。

参考文献:《JavaScript高级程序设计(第三版)》

相关文章
相关标签/搜索