JavaScript高级程序设计6学习笔记之面向对象程序设计

首先 JavaScript是没有类这个概念的

ECMA-262把对象定义为:“无序属性的集合,其属性可以包含基本值,对象或者函数”

创建对象的方法:

var person = new Object();
person.name = "niko";
person.age = 20;
person.say = function(){alert(this.name);};

上面的例子用对象字面量语法可以写为:

var Person = {
name : "niko";
age : 20;
say : function(){alert(this.name);}
};

虽然Object构造函数或对象字面量可以用来创建单个对象,但是这些方式有明显的缺点,使用同一个接口创建很多对象,会产生大量重复的代码,为了解决这个问题,人们开始使用工厂模式的一种变体

工厂模式抽象了创建具体对象的过程 实现的例子如下

function createPerson(name,age,job){
 var o = new Objcect();
 o.name = name;
 o.age = age;
 o.job = job;
 o.sayName = function(){ alert(this.name);};
 return o;//返回的就是这个对象
}

构造函数模式 : 按照惯例,构造函数始终都应该以一个大写字母开头,而非构造函数则应该以一个小写字母开头

以上的代码使用构造函数模式可以写成这个样子:

function Person(name,age,job){
 this.name = name;
 this.age = age;
 this.job = job;
 this.sayName = function(){
   alert(this.name);
   };
}

构造函数的问题:

使用构造函数的主要问题就是每个方法都要在每个实例上重新创建一遍,因为在ECMAScript中的函数就是对象,因此没定义一个函数,也就是实例化了一个对象

当然我们大可像下面这样把函数定义转移到构造函数外部来解决这个问题

function Person(name,age,job){
 this.name = name;
 this.age = age;
 this.job = job;
 this.sayName = sayName;
}
function sayName(){
 alert(this.name);
}

在这个例子中,我们在构造函数内部,将sayName属性设置成为全局的sayName属性,因为sayName包含的是一个指向函数的指针,因此person1和person2就共享了在全局作用域中定义的同一个sayName()函数 这样做问题又来了:在全局作用域中定义的函数实际上只能被某个对象调用,这让全局作用域有点名不副实,更加让人无法接受的是,如果要定义很多方法,那么就要定义很多个全局函数

原型模式:

我们创建的每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含由特定类型的所有实例共享的属性和方法,使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法

function Person(){}
Person.prototype.name = "niko";
Person.prototype.age = 28;
Person.prototype.job = "software engineer";
Person.prototype.sayhi = function(){alert(this.name);};

hasOwnProperty()方法可以检测一个属性是否在对象的实例中

原型与in操作符:单独使用时,in操作符会在通过对象能够访问给定的属性时返回true,无论该属性是存在于实例中还是原型中

要取得对象上所有可以枚举的实例属性,可以使用ECMAScript的Object.keys()方法 这个方法接收一个对象作为参数,返回一个包含所有可以枚举属性的字符串数组

更简单的原型语法:

前边的例子中没添加一个属性和方法都要敲一遍Person.prototype 为减少不需要输入,可以这样做

function Person(){}
Person.prototype = {
  name : "niko";
  age : 29;
  sayName : function(){alert(this.name);}
}

原型的动态性:

由于在运行中查找值的过程是一次搜索,因此我们队原型对象所做的任何修改都能够立即从实例上反映出来--即使是先创建了实例后修改原型也照样如此

var friend = new Person();
Person.prototype.sayHi = function(){alert(hi);};
friend.sayHi(); //"hi"

当我们调用friend.sayHi()时 她会首先从实例中搜索名为sayHi的属性 在没有找到的情况下会继续搜索原型,因为实例与原型之间的链接只不过是一个指针,而非一个副本 实例中的指针仅指向原型,而不指向构造函数

function Person(){}
var friend = new Person();
Person.prototype = {
 constructor : Person,
 name : "niko",
 age : 20,
 sayName : function(){alert("this.name");}
};
friend.sayName(); //error

这是因为重写原型对象切断了现有原型和任何之前已经存在的对象实例之间的联系,他们引用的任然是最初的原型

原生对象的原型

原型模式的重要性不仅体现在创建自定义类型方面,就连所有原声的引用类型,都是采用这种模式创建的,所有原声引用类型(Object,String,Array等等)都在其构造函数的原型上定义了方法。

原型对象的问题:

原型模式最大的问题是由其共享的本质所导致的,原型中所有的属性是被很多实例共享的,这种共享对于函数来说非常合适,对于那些包含基本值的属性倒也说得过去,但是对于包含引用类型值的属性来说,问题就比较突出了

function Person(){}
Person.prototype={constructor:Person,name:"niko",age:"20",friends:["shelby","sansa"],sayName:function(){alert(this.name);}};
var person1 = new Person();
var person2 = new Person();
person1.friends.push("Van");
alert(person1.friends);//shelby,sansa,Van
alert(person2.friends);//shelby,sansa,Van

再次,Person.prototype对象有一个名为friends的属性,该属相包含一个字符串数组,然后,创建了两个Person的实例,接着,修改了person1.friends引用的数组,向数组中添加了一个字符串,由于friends数组存在于Person.prototype而非person1中,所以刚刚提到的修改也会通过person2.friends反映出来 可是,实例一般要有属于自己的全部的属性,所以这正是我们很少有人看到使用原型模式的原因。

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

创建自定义类型的最常见的方式,就是组合使用构造函数模式与原型模式,构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性,结果,每一个实例都有有一份实例属性的副本,但同时又共享着对方法的引用,最大限度的节省了内存

function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.friends=["shelly","sansa"];
}//这是用构造函数模式,每个实例都会初始化构造函数模式下的方法
Person.prototype = {
constructor : Person,
sayName : function(){alert(this.name);}
}
var person1 = new Person();//这里传入三个参数
var person2 = new Person();//这里同样传入三个参数
person1.friends.push("van");
alert(person1.friends);//shelly sansa van
alert(person2.friends);//shelly sansa 

继承:ECMAScript只支持实现继承

原型链:原型链的基本思想是利用原型让一个引用类型继承另一个引用类型 实现原型链有一种基本方式 代码大致如下

function SuperType(){this.property=true;}
SuperType.prototype.getSuperValue=function(){return this.property};
function SubType(){this.subproperty = false;}
SubType.prototype = new SuperType();//继承了SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function(){return this.subproperty;};
var instance = new SubType();
alert(instance.getSuperValue());//true

原型链的问题:

function SuperType(){
 this.colors=["red","green"];
}
function SubType(){}
SubType.prototype = new SuperType();
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors);//red.green.black
var instance2 = new SubType();
alert(intance2.colors);//red green black

借用构造函数实现继承:这种方式叫做伪继承或者经典继承

function SuperType(){this.colors=["red","blue"];}
function SubType(){SuperType.call(this);}
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors);//red blue black
var instance2 = new SubType();
alert(instance2.colors);//red blue

组合继承:

function SuperType(name){
this.name = name;
this.colors = ["red","blue"];
}
SuperType.prototype.sayName = function(){alert(this.name);};
function SubType(name,age){
SuperType.call(this,name);//继承属性
this.age = age;
}
//继承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType();
SubType.prototype.sayAge = function(){alert(this.age);};

var instance1 = new SubType("niko",20);
instance1.colors.push("black");
instance1.sayName();//niko
instance1.sayAge();//20

var instance2 = new SubType("Sansa",19);
alert(instance2.colors);
instance2.sayName();//Sansa
instance2.sayAge();//29

  

时间: 2024-11-09 21:34:05

JavaScript高级程序设计6学习笔记之面向对象程序设计的相关文章

《JavaScript高级程序设计》学习笔记(5)——面向对象编程

欢迎关注本人的微信公众号"前端小填填",专注前端技术的基础和项目开发的学习. 本节内容对应<JavaScript高级程序设计>的第六章内容. 1.面向对象(Object-Oriented, OO)的语言有一个标志,那就是它们都有类的概念,而通过类可以创建任意多个具有相同属性和方法的对象.前面提到过,ECMAScript中没有类的概念,因此它的对象也与基于类的语言中的对象有所不同. ECMA-262把对象定义为:"无序属性的集合,其属性可以包含基本值.对象或者函数.

《JavaScript高级程序设计》学习笔记12篇

写在前面: 这12篇博文不是给人看的,而是用来查的,忘记了什么基础知识,点开页面Ctrl + F关键字就好了 P.S.如果在对应分类里没有找到,麻烦告诉我,以便尽快添上.当然,我也会时不时地添点遗漏的东西进去 目录 JS学习笔记1_基础与常识 JS学习笔记2_面向对象 JS学习笔记3_函数表达式 JS学习笔记4_BOM JS学习笔记5_DOM JS学习笔记6_事件 JS学习笔记7_表单脚本 JS学习笔记8_错误处理 JS学习笔记9_JSON JS学习笔记10_Ajax JS学习笔记11_高级技巧

C++ Primer 学习笔记_73_面向对象编程 --再谈文本查询示例

面向对象编程 --再谈文本查询示例 引言: 扩展第10.6节的文本查询应用程序,使我们的系统可以支持更复杂的查询. 为了说明问题,将用下面的简单小说来运行查询: Alice Emma has long flowing red hair. Her Daddy says when the wind blows through her hair, it looks almost alive, like a fiery bird in flight. A beautiful fiery bird, he

C++ Primer 学习笔记_74_面向对象编程 --再谈文本查询示例[续/习题]

面向对象编程 --再谈文本查询示例[续/习题] //P522 习题15.41 //1 in TextQuery.h #ifndef TEXTQUERY_H_INCLUDED #define TEXTQUERY_H_INCLUDED #include <iostream> #include <fstream> #include <sstream> #include <vector> #include <set> #include <map&g

《Javascript权威指南》学习笔记之十一:处理字符串---String类和正则表达式

一.正则表达式的基本语法 1.概念:正则表达式由普通字符和特殊字符(元字符)组成的文本模式,该模式描述在查找字符串主体时待匹配的一个或者多个字符串.正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配. 普通字符包括所有的大小写字母字符.所有数字.所有标点符号及一些特殊符号.普通字符本身可以组成一个正则表达式,也可以和元字符组合组成一个正则表达式:而元字符则具有特殊的含义,包括().[].{}./.^.$.*.+.?...|.-.?:.?=.?! 2.基本语法 3.优先权含义 二.使用

cocos2dx游戏开发学习笔记3-lua面向对象分析

在lua中,可以通过元表来实现类.对象.继承等.与元表相关的方法有setmetatable().__index.getmetatable().__newindex. 具体什么是元表在这里就不细说了,网上很多介绍,这里主要讲与cocos2dx相关联的部分. 在lua-binding库中extern.lua里,有如下方法: --Create an class. function class(classname, super) local superType = type(super) local c

C++ Primer 学习笔记_66_面向对象编程 --定义基类和派生类[续]

算法旨在用尽可能简单的思路解决问题,理解算法也应该是一个越看越简单的过程,当你看到算法里的一串概念,或者一大坨代码,第一感觉是复杂,此时不妨从例子入手,通过一个简单的例子,并编程实现,这个过程其实就可以理解清楚算法里的最重要的思想,之后扩展,对算法的引理或者更复杂的情况,对算法进行改进.最后,再考虑时间和空间复杂度的问题. 了解这个算法是源于在Network Alignment问题中,图论算法用得比较多,而对于alignment,特别是pairwise alignment, 又经常遇到maxim

C++ Primer 学习笔记_34_面向对象编程(5)--虚函数与多态(二):纯虚函数、抽象类、虚析构函数、动态创建对象

C++ Primer 学习笔记_34_面向对象编程(5)--虚函数与多态(二):纯虚函数.抽象类.虚析构函数.动态创建对象 一.纯虚函数 1.虚函数是实现多态性的前提 需要在基类中定义共同的接口 接口要定义为虚函数 2.如果基类的接口没办法实现怎么办? 如形状类Shape 解决方法 将这些接口定义为纯虚函数 3.在基类中不能给出有意义的虚函数定义,这时可以把它声明成纯虚函数,把它的定义留给派生类来做 4.定义纯虚函数: class <类名> { virtual <类型> <函

C++ Primer 学习笔记_65_面向对象编程 --概述、定义基类和派生类

面向对象编程 --概述.定义基类和派生类 引言: 面向对象编程基于的三个基本概念:数据抽象.继承和动态绑定. 在C++中,用类进行数据抽象,用类派生从一个类继承另一个:派生类继承基类的成员.动态绑定使编译器能够在运行时决定是使用基类中定义的函数还是派生类中定义的函数. 继承和动态绑定在两个方面简化了我们的程序:[继承]能够容易地定义与其他类相似但又不相同的新类,[派生]能够更容易地编写忽略这些相似类型之间区别的程序. 面向对象编程:概述 面向对象编程的关键思想是多态性(polymorphism)