JavaScript面向对象

cuixiaogang

对象的创建、实例化

在JS中创建对象,通常使用预加载函数的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function LiangShan(name, nickname, ranking)
{
this.name = name;
this.nickname = nickname;
this.ranking = ranking;
this.introduce = function () {
console.log('I\'m ' + this.name + ', people send nickname ' + this.nickname + ', I\'m ' + this.ranking);
}
}
var p = new LiangShan('宋江', '呼保义', '魁首');
p.introduce();
````

# 对象的静态方法

JS中创建静态方法,需要直接给预加载函数赋值的方式进行创建

```javascript
LiangShan.work = function () {
console.log('劫富济贫,替天行道');
}
var p1 = new LiangShan('吴用', '智多星', '老二');
p1.introduce();
LiangShan.work();

原型链的使用

如果想了解原型链,就必须了解JS中的对象的定义,以及内部在new的过程中步骤

预加载函数的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
function Person(name, age)
{
this.name = name;
this.age = age;
this.work = function ()
{
console.log(this.name + ' is work');
}
//默认会返回this对象
//return this;
}
var p = new Person('xiaogang.cui', 28);
p.work();

预加载函数的过程

  • 创建一个新的对象p
  • 设置p对象中的this为一个空对象
  • 执行p对象中的代码(对this对象中赋值)
  • 在对象中返回this

由于预加载函数中默认存在第四步(默认返回this对象),才使得JS中可以使用对象。

引用类型的特性

JS的引用类型中包括数组、对象与函数,通过下面的代码找到引用类型的特性

1
2
3
4
5
6
7
8
9
10
11
12
function Person(name, age)
{
this.name = name;
this.age = age;
this.work = function ()
{
console.log(this.name + ' is work');
}
}
var p = new Person('xiaogang.cui', 28);
var data = [1,2,3,4,5];
var config = {a:1,b:2,c:3};
  • 所有的引用类型都具有对象的属性
1
2
3
p.sex = 1;
data.a = 12
config.db_name = 'localhost';
  • 所有的引用类型都有一个__proto__的属性,并且函数、对象的__proto__属性默认为Object,而数组的__proto__属性默认为Array。注意__proto__被称为隐式原型属性,不建议直接使用。
1
2
3
console.log(p);
console.log(data);
console.log(config);

__proto__属性效果图
__proto__属性效果图

  • 所有的函数中,都有一个prototype(显示原型)值,属性值也是一个对象。
1
console.log(Person.prototype);

代码运行结果
代码运行结果

  • 当调用对象的属性值时,如果该对象本身中不存在,则会前往对象的__proto__的属性中找,由于对象的__proto__的属性值与它的构造函数中的prototype的属性值相同,所以找的就是构造函数中的prototype。
1
2
3
4
5
6
7
8
9
10
var p1 = new Person('renling', 20);
Person.prototype.run = function ()
{
console.log(this.name + ' running');
}
//所有对象中都会存在
p.run();
p1.run();
p2 = new Person('佩玉鸣鸾', 18);
p2.run();

原型链介绍

当某个对象中,如果不存在某个方法或属性时,会从其构造函数的prototype中寻找,但是由于prototype也是一个对象,所以如果还是不存在,则继续从prototype中的__proto__的属性中寻找。这便是原型链。

1
p.toString();

代码执行结果
代码执行结果

由于p对象中不存在toString()方法,所以需要从__proto__中寻找,但是p对象的__proto__的对象中也不存在,则需要继续从p.proto.__proto__中寻找。

instanceof操作符

instanceof运算符用来判断一个构造函数的原型链是否存在另一个对象(可以用来判断一个构造函数是否继承的另一个对象)。

语法

1
obj instanceof Object;//true 实例obj在不在Object构造函数中

示例

  • 示例一
1
2
3
function Person(){};
var p =new Person();
console.log(p instanceof Person);//true
  • 示例二
1
2
3
4
5
6
7
function Person(){};
function Student(){};
var p =new Person();
Student.prototype=p;//继承原型
var s=new Student();
console.log(s instanceof Student);//true
console.log(s instanceof Person);//true

特殊对象

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
function Person() {}
console.log(Object instanceof Object); //true
//第一个Object的原型链:Object=>
//Object.__proto__ => Function.prototype=>Function.prototype.__proto__=>Object.prototype
//第二个Object的原型:Object=> Object.prototype

console.log(Function instanceof Function); //true
//第一个Function的原型链:Function=>Function.__proto__ => Function.prototype
//第二个Function的原型:Function=>Function.prototype

console.log(Function instanceof Object); //true
//Function=>
//Function.__proto__=>Function.prototype=>Function.prototype.__proto__=>Object.prototype
//Object => Object.prototype

console.log(Person instanceof Function); //true
//Person=>Person.__proto__=>Function.prototype
//Function=>Function.prototype

console.log(String instanceof String); //false
//第一个String的原型链:String=>
//String.__proto__=>Function.prototype=>Function.prototype.__proto__=>Object.prototype
//第二个String的原型链:String=>String.prototype

console.log(Boolean instanceof Boolean); //false
//第一个Boolean的原型链:Boolean=>
//Boolean.__proto__=>Function.prototype=>Function.prototype.__proto__=>Object.prototype
//第二个Boolean的原型链:Boolean=>Boolean.prototype

console.log(Person instanceof Person); //false
//第一个Person的原型链:Person=>
//Person.__proto__=>Function.prototype=>Function.prototype.__proto__=>Object.prototype
//第二个Person的原型链:Person=>Person.prototype

类的继承(对象冒充法)

对象冒充法就是使用call或者apply的方法把当前对象进行替换,存在的问题是在原型链中的方法不能被继承,另外,需要注意的地方时,如果需要额外增加方法或属性,应该在call或apply之后声明。

call与apply的方法将在最后介绍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function HongHuaHui(name, nickname, ranking)
{
LiangShan.call(this, name, nickname, ranking);
this.task = function()
{
console.log('反清复明');
}
}
function HongMen(name, nickname, ranking)
{
HongHuaHui.apply(this, arguments);
this.task = function ()
{
console.log('只为赚钱');
}
}
var p2 = new HongHuaHui('陈近南', '无外号', '魁首');
p2.introduce();
p2.task();

var p3 = new HongMen('不知道', '宵小之辈', '不知道');
p3.introduce();
p3.task();

类的继承(原型链继承法)

另一种继承方式是将父级的对象实例化完成后赋予到新的子类的原型链中,存在的问题是默认不能携带类传递的参数

1
2
3
4
5
function YiHeTuan(name, nickname, ranking) {}
YiHeTuan.prototype = new LiangShan();
var p4 = new YiHeTuan('清政府', '保家卫国', '不知道');
p4.introduce();
p4.know();

需要变种之后就可以了

1
2
3
4
5
6
7
8
9
function YiHeTuan(name, nickname, ranking) {
this.name = name;
this.nickname = nickname;
this.ranking = ranking;
}
YiHeTuan.prototype = new LiangShan();
var p4 = new YiHeTuan('清政府', '保家卫国', '不知道');
p4.introduce();
p4.know();

类的继承(组合继承法)

两种方式都用

1
2
3
4
5
6
7
8
function Group(name, nickname, ranking)
{
LiangShan.apply(this, arguments);
}
Group.prototype = new LiangShan();
var p5 = new Group('新领袖', '神', '一拳超人');
p5.getName();
p5.introduce();

全部的代码及结果展示

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//1.对象的创建
function LiangShan(name, nickname, ranking)
{
this.name = name;
this.nickname = nickname;
this.ranking = ranking;
this.introduce = function () {
console.log('I\'m ' + this.name + ', people send nickname ' + this.nickname + ', I\'m ' + this.ranking);
}
}
var p = new LiangShan('宋江', '呼保义', '魁首');
p.introduce();

//2.静态方法
LiangShan.work = function () {
console.log('劫富济贫,替天行道');
}
var p1 = new LiangShan('吴用', '智多星', '老二');
p1.introduce();
LiangShan.work();


//3.原型链中的方法
LiangShan.prototype.know = function () {
console.log('读水浒');
}
LiangShan.prototype.time = '宋朝';
LiangShan.prototype.getName = function () {
console.log(this.name);
}

//4.类的继承(对象冒充法,原型链中的方法不可继承)
function HongHuaHui(name, nickname, ranking)
{
LiangShan.call(this, name, nickname, ranking);
this.task = function()
{
console.log('反清复明');
}
}
function HongMen(name, nickname, ranking)
{
HongHuaHui.apply(this, arguments);
this.task = function ()
{
console.log('只为赚钱');
}
}

var p2 = new HongHuaHui('陈近南', '无外号', '魁首');
p2.introduce();
p2.task();

var p3 = new HongMen('不知道', '宵小之辈', '不知道');
p3.introduce();
p3.task();

//4.类的继承(原型链继承法,不可进行传参)
function YiHeTuan(name, nickname, ranking) {
this.name = name;
this.nickname = nickname;
this.ranking = ranking;
}
YiHeTuan.prototype = new LiangShan(1,2,3);
var p4 = new YiHeTuan('清政府', '保家卫国', '不知道');
p4.introduce();
p4.know();

//5.组合赋值
function Group(name, nickname, ranking)
{
LiangShan.apply(this, arguments);
}
Group.prototype = new LiangShan();
var p5 = new Group('新领袖', '神', '一拳超人');
p5.getName();
p5.introduce();

nodejs运行结果
nodejs运行结果

call与apply的方法说明

  • 每个函数都包含两个非继承而来的方法:call()方法和apply()方法。
  • 这两个方法的作用是一样的:都是在特定的作用域中调用函数,等于设置函数体内this对象的值,以扩充函数赖以运行的作用域。(一般来说,this总是指向调用某个方法的对象,但是使用call()和apply()方法时,就会改变this的指向)
  • apply接收两个参数,一个是函数运行的作用域(this),另一个是参数数组。
1
HongHuaHui.apply(this, arguments);
  • call接收至少一个参数,第一个参数和apply()方法的一样,但是传递给函数的参数必须列举出来。
1
LiangShan.call(this, name, nickname, ranking);