JavaScript 面向对象

面向对象

面向队形的语言有一个标志,那就是他们都有类的概念,而通过类可以创建多个具有相同属性和方法的对象。

1
2
3
4
5
6
7
var person = new Object();

person.name = "闪电侠"
person.age = 18
person.job = function () {
return this.name
}

属性类型

分为访问器属性,数据属性

  • 数据属性四个特性
1
2
3
4
5
6
7
8
9
10
11
12
13
var persoon = {
name: '张三'
}
// 全局对象的属性
// window.Object
// 修改对象属性特性
Object.defineProperty(persoon,"name",{
// 具体特性
configurable: false, // 删除属性从而重新定义属性,能否修改属性的特性
enumerable: false, // 能否通过for-in循环返回属性
writable: true, // 能够修改属性值
value: '李四' // 属性数据值
})
  • 访问器属性四个特性
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
 // 定义对象
var send = {
name: 1,
// 访问器属性写法》?? _符号
_getName: '张飒',
setName: '李四'
};

// 修改对象特性
Object.defineProperty(send, "getName",{
configurable: false, // 删除属性从而重新定义属性,能否修改属性的特性
enumerable: false, // 能否通过for-in循环返回属性
get: function () {
return this._getName // 之前this声明并没有拿到正确的函数返回值,现在试试
}, //定义函数
set: function (number1) {
if(number1 > 0){
alert(this.setName)
} else {
return this.name
}
}
})

// 如何调用上面声明的对象特性 set写入 写入后应该是李四
send.getName = 10

// get读取
console.log(send.getName)

// 说明写入 和 读取是分开进行的 并且写入不修改原对象属性值

defineProperties() 定义多个属性特性

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
Object.defineProperties(person, {
// 这个时候就很明确了
name: {
value: '张三'
},
_job: {
value: 10
},
job: {
get: function () {
return this._job
},
set: function (num) {
if (num > 0) {
console.log(this.name)
} else {
return this._job
}
}
}
});

// 首先我们先看下这个对象其中一个属性的值
console.log(person.name);
person.job = 11;
console.log(person.job);

getOwnPropertyDescriptor() 读取属性特性

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
var person = {};

Object.defineProperties(person, {
// 这个时候就很明确了
name: {
value: '张三'
},
_job: {
value: 10
},
job: {
get: function () {
return this._job
},
set: function (num) {
if (num > 0) {
console.log(this.name)
} else {
return this._job
}
}
}
});

// 从字面上看这个方法是获取特性

// 这个方法的使用方式(对象,"属性名")

var getList = Object.getOwnPropertyDescriptor(person, "job")
// 我们来看看获取的是什么数据值
console.log(getList)

工程模式与构造函数

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
// 工厂模型

// 定义一个全局函数进行多次调用可以创建多个相同属性的对象,对象名不同,属性相同。
function functionName(arg1, arg2, arg3) {
var list = new Object();
// 赋值
list.arg1 = arg1;
list.arg2 = arg2;
// 对象属性中包含函数时为什么没有直接执行? 函数作用域
list.pushNum = function (arg3) {
if(arg3 === 1){
return arg3;
}
}
return list;
}

var getList = functionName("张飒","李四", 1)

console.log(getList)

// 工厂模式解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)

// 构造函数模型
// 构造函数名始终以大写字母开头
function Person(name,age,sex){
this.name = name;
this.age = age;
this.sex = function(){
return sex;
}
}

var person = new Person("张三",18,"2");

console.log(person)

// 工厂模型和构造函数模型之间的不同
// 没有显式的创建对象
// 直接将属性和方法赋值给this对象
// 没有return语句

// 1. 将构造函数当作函数
// 任何函数,只要通过New操作符来调用,那它就可以构造函数,而任何函数,如果不通过New调用,那就是普通函数
// 2. 构造函数问题
// 构造函数的缺点,每个方法都要在实例上重新创建一遍

原型模型

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

prototype就是通过调用构造函数而创建的那个对象实例的原型对象。

优点:原型对象可以让所有对象实例共享它包含的属性和方法。

1
2
3
4
5
6
7
8
9
10
function Person() {}

Person.prototype.name = '张飒'
Person.prototype.age = function () {
return this.name
}

// isPrototypeOf() 确认属性是否跟原型对象存在关系
// hasOwnProperty() 检测一个属性是否存在于实例中
// for-in操作符

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 构造函数
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.friends = ["张飒", "李四"];
}

// 原型模型
Persoon.prototype = {
constructor : persoon,
sayName : function(){
alert(this.name)
}
}

var person1 = new Person("张三",10,"soga");
var person2 = new Person("李四",10,"dou);

// 数组的操作
person1.friends.push("王五")

在这个代码中,实例属性都是在构造函数中定义的,而由所有实例共享的属性constructor和方法sayName()则是在原型中定义的。而修改person.friends,并不影响person2.friends,因为他们分别引用不同的数组。

这种构造函数与原型混成的模式,是目前ECMAScript中使用最广泛、认同度最高的一种创建自定义类型的方法。可以说,这是用来定义引用类型的一种默认模式。

动态原型模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
funtion Person(name,age,job){
this.name = name;
this.age = age;
this.job = job

if(typeof this.sayName != "function") {
Person.prototype.sayName = function () {
alert(this.name);
}
}
}

var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName();

寄生构造函数模式

稳妥构造函数模式

继承

原型链

ECMAScript中描述了原型链的概念,并将原型链座位实现继承的主要方法。其中思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。

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
// 函数
function SuperType(){
this.property = true;
}

// 函数添加一个属性
SuperType.prototype.getSuperValue = function () {
return this.property;
}

// 函数
function SubType(){
this.subproperty = false;
}

// 继承了SuperType
SubType.prototype = new SuperType(); // true

// 添加
Subtype.prototype.getSubValue = function (){
return this.subproperty;
}

var instance = new SubType();

alert(instance.getSuperValue());
  • 原型式继承,可以在不必预先定义构造函数的情况下实现继承,其本质是执行对给定对象的浅复制。而赋值得到的副本还可以得到进一步改造。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function createAnother(original){
var clone = object(original);
clone.sayHi = function(){
alert('hi');
};
return clone;
}

var person = {
name: "张飒",
friends: ['1','2','3']
};

var anotherPerson = createAnother(person);
anotherPerson.sayHi();
  • 寄生式继承,与原型式继承非常相似,也是基于某个对象或某些信息创建一个对象,然后增强对象,最后返回对象。为了解决组合继承模式由于多次调用超类型构造函数而导致的低效率问题,可以将这个模式与组合继承一起使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function SuperType(name){
this.name = name;
this.colors = ['red', 'blue', 'green'];
}

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);
}
  • 寄生组合式继承,集寄生式继承和组合继承的优点与一身,是实现基于类型继承的最有效方式。