喵星之旅-成长的雏鹰-Web前端-6-JavaScript对象及初识面向对象

对象

对象是包含相关属性和方法的集合体

什么是面向对象
面向对象仅仅是一个概念或者编程思想
通过一种叫做原型的方式来实现面向对象编程

内置对象

常见的内置对象:

String(字符串)对象

length属性
indexOf( )方法、replace( )方法

Date(日期)对象

get×××:获取年、月、日、时、分、秒等等
set×××:设置年、月、日、时、分、秒等等

Array(数组)对象

length属性
sort( )、concat( )、join( )方法

Boolean(逻辑)对象

true或者false
toString( )方法

Math(算数)对象

round( )、max( )、min( )等方法

RegExp对象

RegExp是正则表达式的缩写

自定义对象

操作符new创建对象

1
2
3
4
5
6
7
8
var flower=new Object();
flower.name="长春花";
flower.genera="夹竹桃科 长春花属";
flower.area="非洲、亚热带、热带以及中国大陆的华东、西南、中南等地";
flower.uses="观赏或用药等";
flower.showName=function(){ alert(this.name); }
flower.showName();

使用字面量赋值的方式在定义对象

1
2
3
4
5
6
7
8
9
var flower={
name:"长春花",
genera:"夹竹桃科 长春花属",
area:"非洲、亚热带、热带以及中国大陆的华东、西南、中南等地",
uses:"观赏或用药等",
showName:function(){ alert(this.name); }
}
flower.showName();

构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//构造函数始名称终都应该以一个大写字母开头

function Flower(name,genera,area,uses){
this.name=name;
…….
this.showName=function(){
alert(this.name);
}
}
var flower1=new Flower("长春花","夹竹桃科 长春花属",
"非洲、亚热带、热带以及中国大陆的华东、西南、中南等地",
"观赏或用药等")
flower1.showName();

调用构函数的4个步骤

  • 创建一个新对象
  • 将构造函数的作用域赋给新对象(this就指向了这个新对象)
  • 执行构造函数中的代码
  • 返回新对象

constructor属性指向Flower

1
2
3
4
alert(flower1.constructor==Flower);
alert(flower2.constructor==Flower);
alert(flower3.constructor==Flower);

返回结果未true

使用instanceof操作符检测对象类型

1
2
3
4
5
6
7
alert(flower1 instanceof Object);
alert(flower1 instanceof Flower);
alert(flower2 instanceof Object);
alert(flower2 instanceof Flower);
alert(flower3 instanceof Object);
alert(flower3 instanceof Flower);

原型对象

什么是原型对象

无论何时,我们创建的每一个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,这个对象包含了通过调用该构造函数所创建的对象共享的属性和方法。其实我们平常的叫法就是指:prototype就是通过该构造函数创建的某个实例的原型对象,但是其实prototype是每个构造函数的属性而已,只能说万物皆对象罢了。
   原型对象的优点是:所有的对象实例都可以共享它包含的属性和方法。这一点可以在构造函数里就可以看出来,因为构造函数在函数里面就定义了对象的实例信息,而原型对象可以在任何地方定义属性和方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Flower(){
}
Flower.prototype.name="曼陀罗花";
Flower.prototype.genera="茄科 曼陀罗属";
Flower.prototype.area="印度、中国北部";
Flower.prototype.uses="观赏或药用";
Flower.prototype.showName=function() {
alert(this.name);
}
var flower1=new Flower();
flower1.showName();
var flower2=new Flower();
flower2.showName();
alert(flower1.showName==flower2.showName);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Flower(){

}
Flower.prototype.name="曼陀罗花";
Flower.prototype.genera="茄科 曼陀罗属";
Flower.prototype.area="印度、中国北部";
Flower.prototype.uses="观赏或药用";
Flower.prototype.showName=function() {
alert(this.name);
}
var flower1=new Flower();
var flower2=new Flower();
flower1.name="长春花";
alert(flower1.name);
alert(flower2.name);

原型对象和prototype

我们创建了一个函数,就会根据ECMAscript特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性是一个指向prototype属性所在函数的指针。就像前面的例子,Person.prototype.constructor指向Person,而通过这个构造函数,我们还可以继续为原型对象添加其他属性和方法。

  • 创建一个自定义的构造函数后,它的原型对象默认只会有constructor属性,其他属性和方法都是从Object继承而来的;
  • 当调用构造函数创建一个新实例后,该实例的内部将包含一个指针,指向构造函数的原型对象。ECMA5中称这个指针叫[[prototype]]。虽然在脚本中没有标准的方式访问[[prototype]],但Firefox、Safari和Chrome在每个对象上都支持一个属性_proto_;而在其他浏览器中,这个属性则是完全不可见的。不过要明确的真正一点就是,这个连接存在于 实例 和 构造函数的原型对象 之间,而不是存在于实例和构造函数之间,用前面使用Person构造函数和Person.prototype创建实例的代码为例,各个对象之间的关系如下图所示:
    Alt text
    上图展示了Person构造函数、Person的原型以及Person现有的两个实例之间的关系,再次,Person.prototype指向了原型对象,而Person.prototype.constructor又指回了Person。原型对象中除了包含constructor属性之外,还包括后来添加的其他属性。Person的每个实例——person1和person2都包含一个内部属性,该属性指向了Person.prototype;换句话说,他们与构造函数没有直接关系。
      注意:虽然这两个实例都不包含属性和方法,但我们却可以调用name、age、job、dream()方法和属性,这是通过查找对象属性的过程来实现的。
      虽然我们不能访问到[[prototype]],但可以通过isPrototypeOf()方法来确定对象之间是否存在这种关系。因为这两个实例都有一个指向Person.prototype的指针,因此,都返回true如下所示:
    1
    2
    console.log(Person.prototype.isPrototypeOf(person1));//true
    console.log(Person.prototype.isPrototypeOf(person2));//true
    ECMA5增加了一个方法,叫Object.getPrototypeOf(),这个方法返回[[Prototype]]的值,也就是括号里面对象的原型,例如:
    1
    2
    console.log(Object.getPrototypeOf(person1) == Person.prototype; // true
    console.log(Object.getPrototypeOf(person1).name); //'bangbang'

用hasOwnProperty()方法可以检测一个属性是存在于实例中还是原型中。还是上面的例子:

1
2
3
4
5
6
7
console.log(person1.hasOwnProperty('name'); //false
person1.name = 'qiqi';
console.log(person1.hasOwnProperty('name'); //true —— 来自实例
console.log(person1.name); //'qiqi' ——来自实例
delete person1.name;
console.log(person1.name); //'bangbang' —— 来自原型
console.log(person1.hasOwnProperty('name'); //true ——来自原型

当我们为person1添加name属性的时候,如下图图所示:
Alt text

  • 创建自定义类型的最常见方式就是组合使用构造函数模式和原型模式。构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。每个实例都会有自己的一份实例属性的副本,但同时又共享着对方发的引用,最大限度的节省了内存。如下例所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.love = ['qiqi','lili'];
}
Person.prototype = {
constructor : Person,
dream : function(){
console.log(this.love[0]);
}
}
var person1 = new Person('bangbang',18,'programmer');
var person2 = new Person('xiaoya',18,'teacher');
console.log(person1.love);
console.log(person2.love)
console.log(person1.love === person2.love); //false
//给person1的love属性添加元素
person1.love.push('niuniu');
console.log(person1.love) //["qiqi", "lili", "niuniu"]
console.log(person2.love) //["qiqi", "lili"]
console.log(person1.love === person2.love); //false
console.log(person1.dream === person2.dream);//true

继承

原型链

什么是原型链

每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针,那么,假如我们让原型对象等于另一个类型的实例,结果会是怎么样呢?显然,此时的原型对象将包含一个指向另一个原型的指针,相应的,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实例与原型的链条,这就是原型链的基本概念。

Alt text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function Humans(){
this.foot=2;
}
Humans.prototype.getFoot=function(){
return this.foot;
}
function Man(){
this.head=1;
}

Man.prototype=new Humans(); //继承了Humans
Man.prototype.getHead=function(){
return this.head;
}
var man1=new Man();
alert(man1.getFoot()); //2
alert(man1 instanceof Object); //true
alert(man1 instanceof Humans); //true
alert(man1 instanceof Man); //true

构造函数和原型之间的关系

Alt text

完整的原型链

Alt text

对象继承

利用原型让一个引用类型继承另一个引用类型的属性和方法

1
2
3
4
5
6
7
8
9
10
11
12
function Humans(){
this.clothing=["trousers","dress","jacket"];
}
function Man(){ }
//继承了Humans
Man.prototype=new Humans();
var man1=new Man();
man1.clothing.push("coat");
alert(man1.clothing);
var man2=new Man();
alert(man2.clothing);

在子类型的构造函数的内部调用超类型构造函数,通过 apply,call 方法实现

1
2
3
4
5
6
7
8
9
10
11
function Humans(name){
this.name=name;
}
function Man(){
Humans.call(this,"mary"); //继承了Humans,同时还传递了参数
this.age=38; //实例属性
}
var man1=new Man();
alert(man1.name); //输出mary
alert(man1.age); //输出38

组合继承

有时也叫做伪经典继承
将原型链和借用构造函数的技术组合到一块,发挥二者之长的一种继承模式
使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>组合继承</title>
</head>
<body>
<script>
function Humans(name){
this.name=name;
this.clothing=["trousers","dress","jacket"];
}
Humans.prototype.sayName=function(){
alert(this.name);
};
function Man(name,age){
Humans.call(this,name); //继承属性
this.age=age;
}
Man.prototype=new Humans(); //继承方法
Man.prototype.sayAge=function(){
alert(this.age);
};

var man1=new Man("mary",38);
man1.clothing.push("coat");
alert(man1.clothing); //输出"trousers,dress,jacket,coat"
man1.sayName(); //输出mary
man1.sayAge(); //输出38
var man2=new Man("tom",26);
alert(man2.clothing); //输出"trousers,dress,jacket"
man2.sayName(); //输出tom
man2.sayAge(); //输出26
</script>
</body>
</html>
文章目录
  1. 对象
    1. 内置对象
    2. 自定义对象
      1. 操作符new创建对象
      2. 使用字面量赋值的方式在定义对象
  2. 构造函数
  3. 原型对象
    1. 什么是原型对象
    2. 原型对象和prototype
  4. 继承
    1. 原型链
      1. 什么是原型链
      2. 构造函数和原型之间的关系
      3. 完整的原型链
    2. 对象继承
    3. 组合继承
|