JavaScript面向对象编程深入分析凯发娱乐 - 凯发娱乐

JavaScript面向对象编程深入分析凯发娱乐

2019年02月16日09时29分58秒 | 作者: 冷霜 | 标签: 目标,特点,办法 | 浏览: 12138

一. Javascript 面向目标编程:封装

Javascript是一种依据目标(object-based)的言语,你遇到的一切东西简直都是目标。可是,它又不是一种真实的面向目标编程(OOP)言语,因为它的语法中没有class(类)。

那么,假如咱们要把"特点"(property)和"办法"(method),封装成一个目标,乃至要从原型目标生成一个实例目标,咱们应该怎么做呢?

1. 生成目标的原始形式

假定咱们把猫当作一个目标,它有"姓名"和"色彩"两个特点。

var Cat = {
name : ,
color : 
}

现在,咱们需求依据这个原型目标,生成两个实例目标。

var cat1 = {}; // 创立一个空目标
cat1.name = "大毛"; // 依照原型目标的特点赋值
cat1.color = "黄色";
var cat2 = {};
cat2.name = "二毛";
cat2.color = "黑色";

好了,这就是最简略的封装了。可是,这样的写法有两个缺陷,一是假如多生成几个实例,写起来就十分费事;二是实例与原型之间,没有任何办法,可以看出有什么联络。

2. 原始形式的改善

咱们可以写一个函数,处理代码重复的问题。

function Cat(name, color) {
 return {
 name: name,
 color: color
}

然后生成实例目标,就等所以在调用函数:

var cat1 = Cat("大毛","黄色");
var cat2 = Cat("二毛","黑色");

这种办法的问题依然是,cat1和cat2之间没有内涵的联络,不能反映出它们是同一个原型目标的实例。

3. 结构函数形式

为了处理从原型目标生成实例的问题,Javascript供给了一个结构函数(Constructor)形式。

所谓"结构函数",其实就是一个一般函数,可是内部运用了this变量。对结构函数运用new运算符,就能生成实例,而且this变量会绑定在实例目标上。

比方,猫的原型目标现在可以这样写,

function Cat(name, color) {
 this.name = name;
 this.color = color;
}

咱们现在就可以生成实例目标了。

var cat1 = new Cat("大毛", "黄色");
var cat2 = new Cat("二毛", "黑色");
alert(cat1.name); // 大毛
alert(cat1.color); // 黄色

 

这时cat1和cat2会主动含有一个constructor特点,指向它们的结构函数。

alert(cat1.constructor  Cat); //true
alert(cat2.constructor  Cat); //true

Javascript还供给了一个instanceof运算符,验证原型目标与实例目标之间的联系。

alert(cat1 instanceof Cat); //true
alert(cat2 instanceof Cat); //true

4. 结构函数形式的问题

结构函数办法很好用,可是存在一个糟蹋内存的问题。

请看,咱们现在为Cat目标增加一个不变的特点"type"(品种),再增加一个办法eat(吃老鼠)。那么,原型目标Cat就变成了下面这样:

function Cat(name, color) {
 this.name = name;
 this.color = color;
 this.type = "猫科动物";
 this.eat = function () { alert("吃老鼠"); };
}

 

仍是选用相同的办法,生成实例:

var cat1 = new Cat("大毛","黄色");
var cat2 = new Cat ("二毛","黑色");
alert(cat1.type); // 猫科动物
cat1.eat(); // 吃老鼠

 

表面上如同没什么问题,可是实际上这样做,有一个很大的坏处。那就是关于每一个实例目标,type特点和eat()办法都是如出一辙的内容,每一次生成一个实例,都有必要为重复的内容,多占用一些内存。这样既不环保,也缺少功率。

alert(cat1.eat  cat2.eat); //false

能不能让type特点和eat()办法在内存中只生成一次,然后一切实例都指向那个内存地址呢?答复是可以的。

5. Prototype形式

Javascript规则,每一个结构函数都有一个prototype特点,指向另一个目标。这个目标的一切特点和办法,都会被结构函数的实例承继。

这意味着,咱们可以把那些不变的特点和办法,直接界说在prototype目标上。

function Cat(name, color) {
 this.name = name;
 this.color = color;
Cat.prototype.type = "猫科动物";
Cat.prototype.eat = function () { alert("吃老鼠") };

然后,生成实例。

var cat1 = new Cat("大毛","黄色");
var cat2 = new Cat("二毛","黑色");
alert(cat1.type); // 猫科动物
cat1.eat(); // 吃老鼠

这时一切实例的type特点和eat()办法,其实都是同一个内存地址,指向prototype目标,因而就提高了运转功率。

alert(cat1.eat  cat2.eat); //true

6. Prototype形式的验证办法

6.1 isPrototypeOf()

这个办法用来判别,某个proptotype目标和某个实例之间的联系。

alert(Cat.prototype.isPrototypeOf(cat1)); //true
alert(Cat.prototype.isPrototypeOf(cat2)); //true

6.2 hasOwnProperty()

每个实例目标都有一个hasOwnProperty()办法,用来判别某一个特点到底是本地特点,仍是承继自prototype目标的特点。

alert(cat1.hasOwnProperty("name")); // true
alert(cat1.hasOwnProperty("type")); // false

6.3 in运算符

in运算符可以用来判别,某个实例是否含有某个特点,不论是不是本地特点。

alert("name" in cat1); // true
alert("type" in cat1); // true

in运算符还可以用来遍历某个目标的一切特点。

for(var prop in cat1) { alert("cat1["+prop+"]="+cat1[prop]); } 

 

二. Javascript 面向目标编程:结构函数的承继

本节首要介绍,怎么生成一个"承继"多个目标的实例。

比方,现在有一个"动物"目标的结构函数,

function Animal() {
 this.species = "动物";
}

 

还有一个"猫"目标的结构函数,

function Cat(name, color) {
 this.name = name;
 this.color = color;
}

 

怎样才能使"猫"承继"动物"呢?

1. 结构函数绑定

最简略的办法,大约就是运用call或apply办法,将父目标的结构函数绑定在子目标上,也就是在子目标结构函数中加一行:

function Cat(name, color) {
 Animal.apply(this, arguments);
 this.name = name;
 this.color = color;
var cat1 = new Cat("大毛", "黄色");
alert(cat1.species); // 动物

 

2. prototype形式

更常见的做法,则是运用prototype特点。

假如"猫"的prototype目标,指向一个Animal的实例,那么一切"猫"的实例,就能承继Animal了。

Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); // 动物

 

代码的榜首行,咱们将Cat的prototype目标指向一个Animal的实例。

Cat.prototype = new Animal();

 

它相当于彻底删除了prototype 目标原先的值,然后赋予一个新值。可是,第二行又是什么意思呢?

Cat.prototype.constructor = Cat;

 

本来,任何一个prototype目标都有一个constructor特点,指向它的结构函数。也就是说,Cat.prototype 这个目标的constructor特点,是指向Cat的。

咱们在前一步现已删除了这个prototype目标本来的值,所以新的prototype目标没有constructor特点,所以咱们有必要手动加上去,不然后边的"承继链"会出问题。这就是第二行的意思。

总归,这是很重要的一点,编程时务必要恪守。下文都遵从这一点,即假如替换了prototype目标,

o.prototype = {};

 

那么,下一步必定是为新的prototype目标加上constructor特点,并将这个特点指回本来的结构函数。

o.prototype.constructor = o;

 

3. 直接承继prototype

因为Animal目标中,不变的特点都可以直接写入Animal.prototype。所以,咱们也可以让Cat()越过 Animal(),直接承继Animal.prototype。

现在,咱们先将Animal目标改写:

function Animal(){ }
Animal.prototype.species = "动物";

 

然后,将Cat的prototype目标,然后指向Animal的prototype目标,这样就完成了承继。

Cat.prototype = Animal.prototype;
CatCat.prototype.constructor = Cat;
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); // 动物

 

与前一种办法比较,这样做的长处是功率比较高(不必履行和树立Animal的实例了),比较省内存。缺陷是 Cat.prototype和Animal.prototype现在指向了同一个目标,那么任何对Cat.prototype的修正,都会反映到Animal.prototype。

所以,上面这一段代码其实是有问题的。请看第二行

Cat.prototype.constructor = Cat;

 

这一句实际上把Animal.prototype目标的constructor特点也改掉了!

alert(Animal.prototype.constructor); // Cat

 

4. 运用空目标作为中介

因为"直接承继prototype"存在上述的缺陷,所以可以运用一个空目标作为中介。

var F = function(){};
F.prototype = Animal.prototype;
Cat.prototype = new F();
Cat.prototype.constructor = Cat;

 

F是空目标,所以简直不占内存。这时,修正Cat的prototype目标,就不会影响到Animal的prototype目标。

alert(Animal.prototype.constructor); // Animal

 

5. prototype形式的封装函数

咱们将上面的办法,封装成一个函数,便于运用。

function extend(Child, Parent) {
 var F = function () { };
 F.prototype = Parent.prototype;
 Child.prototype = new F();
 Child.prototype.constructor = Child;
 Child.uber = Parent.prototype;
}

 

运用的时分,办法如下

extend(Cat, Animal);
var cat1 = new Cat("大毛", "黄色");
alert(cat1.species); // 动物

 

这个extend函数,就是YUI库怎么完成承继的办法。

别的,阐明一点。函数体最终一行

Child.uber = Parent.prototype;

 

意思是为子目标设一个uber特点,这个特点直接指向父目标的prototype特点。这等所以在子目标上翻开一条通道,可以直接调用父目标的办法。这一行放在这儿,仅仅为了完成承继的齐备性,纯属备用性质。

6. 复制承继

上面是选用prototype目标,完成承继。咱们也可以换一种思路,朴实选用"复制"办法完成承继。简略说,假如把父目标的一切特点和办法,复制进子目标,不也可以完成承继吗?

首要,仍是把Animal的一切不变特点,都放到它的prototype目标上。

function Animal(){}
Animal.prototype.species = "动物";

 

然后,再写一个函数,完成特点复制的意图。

function extend2(Child, Parent) {
 var p = Parent.prototype;
 var c = Child.prototype;
 for (var i in p) {
 c[i] = p[i];
 c.uber = p;
}

 

这个函数的效果,就是将父目标的prototype目标中的特点,逐个复制给Child目标的prototype目标。

运用的时分,这样写:

extend2(Cat, Animal);
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); // 动物

 

三. Javascript面向目标编程:非结构函数的承继

本节介绍不运用结构函数完成"承继"。

1. 什么是"非结构函数"的承继?

比方,现在有一个目标,叫做"我国人"。

var Chinese = {
 nation: 我国
};

 

还有一个目标,叫做"医师"。

var Doctor = {
 career: 医师
}

 

请问怎样才能让"医师"去承继"我国人",也就是说,我怎样才能生成一个"我国医师"的目标?

这儿要注意,这两个目标都是一般目标,不是结构函数,无法运用结构函数办法完成"承继"。

2. object()办法

json格局的发明人Douglas Crockford,提出了一个object()函数,可以做到这一点。

function object(o) {
 function F() { }
 F.prototype = o;
 return new F();
}

这个object()函数,其实只做一件事,就是把子目标的prototype特点,指向父目标,然后使得子目标与父目标连在一起。

运用的时分,榜首步先在父目标的基础上,生成子目标:

var Doctor = object(Chinese);

 

然后,再加上子目标自身的特点:

 Doctor.career = 医师;

 

这时,子目标现已承继了父目标的特点了。

 alert(Doctor.nation); //我国

 

3. 浅复制

除了运用"prototype链"以外,还有另一种思路:把父目标的特点,悉数复制给子目标,也能完成承继。

下面这个函数,就是在做复制:

function extendCopy(p) {
 var c = {};
 for (var i in p) {
 c[i] = p[i];
 c.uber = p;
 return c;
}

 

运用的时分,这样写:

var Doctor = extendCopy(Chinese);
Doctor.career = 医师;
alert(Doctor.nation); // 我国

 

可是,这样的复制有一个问题。那就是,假如父目标的特点等于数组或另一个目标,那么实际上,子目标取得的仅仅一个内存地址,而不是真实复制,因而存在父目标被篡改的或许。

请看,现在给Chinese增加一个"出生地"特点,它的值是一个数组。

Chinese.birthPlaces = [北京,上海,香港];

经过extendCopy()函数,Doctor承继了Chinese。

var Doctor = extendCopy(Chinese);

然后,咱们为Doctor的"出生地"增加一个城市:

Doctor.birthPlaces.push(厦门);

 

发生了什么事?Chinese的"出生地"也被改掉了!

alert(Doctor.birthPlaces); //北京, 上海, 香港, 厦门
alert(Chinese.birthPlaces); //北京, 上海, 香港, 厦门

 

所以,extendCopy()仅仅复制根本类型的数据,咱们把这种复制叫做"浅复制"。这是前期jQuery完成承继的办法。

4. 深复制

所谓"深复制",就是可以完成真实意义上的数组和目标的复制。它的完成并不难,只需递归调用"浅复制"就行了。

function deepCopy(p, c) {
 var c = c || {};
 for (var i in p) {
 if (typeof p[i] = object) {
 c[i] = (p[i].constructor = Array) ? [] : {};
 deepCopy(p[i], c[i]);
 } else {
 c[i] = p[i];
 return c;
}

 

运用的时分这样写:

var Doctor = deepCopy(Chinese);

 

现在,给父目标加一个特点,值为数组。然后,在子目标上修正这个特点:

Chinese.birthPlaces = [北京,上海,香港];
Doctor.birthPlaces.push(厦门);

 

这时,父目标就不会受到影响了。

alert(Doctor.birthPlaces); //北京, 上海, 香港, 厦门
alert(Chinese.birthPlaces); //北京, 上海, 香港

 

现在,jQuery库运用的就是这种承继办法。

版权声明
本文来源于网络,版权归原作者所有,其内容与观点不代表凯发娱乐立场。转载文章仅为传播更有价值的信息,如采编人员采编有误或者版权原因,请与我们联系,我们核实后立即修改或删除。

猜您喜欢的文章