资讯专栏INFORMATION COLUMN

JavaScript面向对象程序设计

wfc_666 / 2786人阅读

摘要:构造函数模式这种方法创建自定义的构造函数,从而自定义对象类型的属性和方法。借用构造函数在子类型构造函数的内部调用超类型构造函数,通过和方法来实现。

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

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

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

2. 工厂模式

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

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操作符更可靠。

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

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

无论何时,创建一个函数,都会自动创建一个prototype属性,指向其原型对象,正如前面所说,每个对象都有一个constructor属性,指向其构造函数。所以Animal.prototype.constructor指向Animal。判断原型对象与实例间关系可用isPrototypeOf()方法:

Animal.prototype.isPrototypeOf(animal1);

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

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);
    }
    //...
}   

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

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

二、继承

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();

其中的原型链:


用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高级程序设计(第三版)》

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/80355.html

相关文章

  • SegmentFault 技术周刊 Vol.32 - 七夕将至,你的“对象”还好吗?

    摘要:很多情况下,通常一个人类,即创建了一个具体的对象。对象就是数据,对象本身不包含方法。类是相似对象的描述,称为类的定义,是该类对象的蓝图或原型。在中,对象通过对类的实体化形成的对象。一类的对象抽取出来。注意中,对象一定是通过类的实例化来的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 马上就要到七夕了,离年底老妈老爸...

    李昌杰 评论0 收藏0
  • SegmentFault 技术周刊 Vol.32 - 七夕将至,你的“对象”还好吗?

    摘要:很多情况下,通常一个人类,即创建了一个具体的对象。对象就是数据,对象本身不包含方法。类是相似对象的描述,称为类的定义,是该类对象的蓝图或原型。在中,对象通过对类的实体化形成的对象。一类的对象抽取出来。注意中,对象一定是通过类的实例化来的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 马上就要到七夕了,离年底老妈老爸...

    Lyux 评论0 收藏0
  • SegmentFault 技术周刊 Vol.32 - 七夕将至,你的“对象”还好吗?

    摘要:很多情况下,通常一个人类,即创建了一个具体的对象。对象就是数据,对象本身不包含方法。类是相似对象的描述,称为类的定义,是该类对象的蓝图或原型。在中,对象通过对类的实体化形成的对象。一类的对象抽取出来。注意中,对象一定是通过类的实例化来的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 马上就要到七夕了,离年底老妈老爸...

    AaronYuan 评论0 收藏0
  • 面向对象JavaScript

    摘要:是完全的面向对象语言,它们通过类的形式组织函数和变量,使之不能脱离对象存在。而在基于原型的面向对象方式中,对象则是依靠构造器利用原型构造出来的。 JavaScript 函数式脚本语言特性以及其看似随意的编写风格,导致长期以来人们对这一门语言的误解,即认为 JavaScript 不是一门面向对象的语言,或者只是部分具备一些面向对象的特征。本文将回归面向对象本意,从对语言感悟的角度阐述为什...

    novo 评论0 收藏0
  • javascript 面向对象版块之理解对象

    摘要:用代码可以这样描述安全到达国外面向过程既然说了面向对象,那么与之对应的就是面向过程。小结在这篇文章中,介绍了什么是面向对象和面向过程,以及中对象的含义。 这是 javascript 面向对象版块的第一篇文章,主要讲解对面向对象思想的一个理解。先说说什么是对象,其实这个还真的不好说。我们可以把自己当成一个对象,或者过年的时候相亲,找对象,那么你未来的老婆也是一个对象。我们就要一些属性,比...

    lovXin 评论0 收藏0
  • JS对象(1)重新认识面向对象

    摘要:对象重新认识面向对象面向对象从设计模式上看,对象是计算机抽象现实世界的一种方式。除了字面式声明方式之外,允许通过构造器创建对象。每个构造器实际上是一个函数对象该函数对象含有一个属性用于实现基于原型的继承和共享属性。 title: JS对象(1)重新认识面向对象 date: 2016-10-05 tags: JavaScript 0x00 面向对象 从设计模式上看,对象是...

    superw 评论0 收藏0

发表评论

0条评论

wfc_666

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<