本帖最后由 Clover_ZhuWQ 于 2017-10-29 19:01 编辑
第一章 Javascript面向对象(上)
本章主要对Javascript面向对象的用法做详细讲解。
1.1 什么是对象
什么是对象?对象是对属性、方法的封装。每一个对象中,都是以属性名:属性值的形式存在。 一言以蔽之:对象就是键值对的集合。
1.2 1.2 什么是面向对象 这个问题很难回答,但这也是面试中最常见的问题。
要搞清楚这个问题,必须弄清楚面向对象和面向过程的区别:
面向过程:完成一件事情的所有步骤都亲力亲为,一步一步来完成。例如要吃饺子,就必须自己剁馅儿、和面、擀饺子皮儿、包饺子... 面向对象:完成一件事情,直接去找能做这件事的人。例如吃饺子,直接找老婆(如果没有就去饺子馆吧)。
面向过程:是事件的执行者。 面向对象:是指挥者。
一言以蔽之:面向对象就是对面向过程的封装!
1.3 为什么要面向对象 问这个问题,其实就是在问面向对象有什么好处,有哪些好处呢?
1) 提高代码的复用性,简化开发 2) 使代码结构清晰 3) 在JS中有一个特殊的作用:防止代码污染
什么是代码污染?请看下面的实例代码:
[HTML] 纯文本查看 复制代码 <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
// 演示:代码污染
// 引入第一个JS文件
function sum(a,b) {
return a + b;
}
</script>
<script>
// 引入第二个JS文件
// 定义一个变量sum
var sum = 100;
</script>
<script>
// 调用求和函数进行求和
console.log(sum(12, 5));
</script>
</body>
</html>
如何解决代码污染?
我们可以把定义的方法放到对象中,然后用对象调用自己的方法:
[HTML] 纯文本查看 复制代码 <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
// 演示:代码污染
// 引入第一个JS文件
// 在对象里面定义一个求和函数
var itcast = {
sum : function(a,b) {
return a + b;
}
};
</script>
<script>
// 引入第二个JS文件
// 定义一个变量sum,因为sum函数已经变成了对象的属性
var sum = 100;
</script>
<script>
// 调用求和函数进行求和
console.log(itcast.sum(12, 5));
</script>
</body>
</html>
1.4 创建对象的两种方式
需求:创建一个人,要求有姓名、年龄、性别、sayHello方法
1.4.1 对象的直接量 l 做法: 在定义对象的时候,直接使用JSON格式来定义一个JS对象,并且为这个对象赋值属性和方法,称为对象的直接量(字面量) 这种方式创建的对象,默认是Object类型的对象
[JavaScript] 纯文本查看 复制代码 <script>
// 演示:通过对象的直接量的形式来创建对象
// 需求:创建一个人,要求有姓名、年龄、性别、sayHello方法
// JSON结构
var p = {
// 属性
name : 'Jack',
age : 38,
gender : '妖',
// 方法
sayHello : function() {
// this 当前对象
console.log('Hello, I am: ' + this.name);
}
};
// 访问属性以及方法
console.log(p.name);
console.log(p.age);
console.log(p.gender);
p.sayHello();
</script>
l 缺陷 n 只能使用1次,无法重复利用,每次都要编写相同代码创建新对象 l 使用场景 n 对象只需要一次使用的时候。比如作为传递参数; n 自己封装JS框架(函数)的时候。
1.4.2 构造函数创建对象l 构造函数创建对象的一般步骤: 1) 通过function指定一个构造函数,与普通函数没差别,只是约定首字母大写以区分 2) 然后在构造函数中通过this.属性名=属性值来完成对象属性初始化。 3) 最后通过new关键字创建对象(实例) l 案例代码: [JavaScript] 纯文本查看 复制代码 <script>
// 使用构造函数的形式创建对象
// 1. 定义一个构造函数(类),构造函数其实就是普通的函数,构造函数首字母大写
function Person(name,age,gender) {
// 2. 在构造函数里面使用this.属性名=属性值的形式来初始化属性
this.name = name;
this.age = age;
this.gender = gender;
this.sayHello = function() {
console.log('Hello, I am : ' + this.name);
}
}
// 3. 使用new关键字来创建对象(实例)
var p = new Person('Jack',38,'妖');
console.log(p.name);
p.sayHello();
var p2 = new Person('Rose',18,'female');
console.log(p2.name);
console.log(p2.age);
console.log(p2.gender);
p2.sayHello();
console.log(p.name == p2.name); // false
console.log(p.sayHello == p2.sayHello); // false
</script> l 效果:
问题:对象中的函数是否相同? 测试: [JavaScript] 纯文本查看 复制代码 // 两个对象中的sayHello方法是否相等
console.log(p.sayHello == p2.sayHello); // false
结果: 事实上,查看内存中对象的属性,发现p1和p2中各有一份 sayHello 方法:
结论: 通过上面的实验我们发现,对象中的所有属性,包括定义的方法,都是每个对象独有的,不会共享。 属性不共享可以理解,但是方法都是一样的,如果也不共享,那么就是对内存的极大浪费。
l 优势: u 对象可以重复创建 l 缺点: u 对象中相同的方法,无法实现共享,内存占用高
1.1 1.5 对象的原型(prototype)
1.5.1 原型的概念
l 什么是prototype ? 在JS中,每一个构造函数都会有一个prototype(也是一个对象)属性,任何由同一个构造函数创建的对象,都会共享这个prototype对象,并且可以具备这个prototype的所有属性(成员)。
1.5.2 原型的基本使用
l 没有设置原型的时候 一开始,我们定义一个空的构造函数,并且只给出了name 属性,尝试打印对象的 age属性,结果是undefined。因为没有该属性 案例代码:
[AppleScript] 纯文本查看 复制代码 <script>
// 演示:原型的基本使用
// 定义构造函数
function Person(name) {
this.name = name;
}
var p = new Person('Jack');
console.log(p.name);
console.log(p.age);
var p2 = new Person('Rose');
console.log(p2.name);
console.log(p2.age);
</script>
结果: 打印控制台中是undefined的。
查看对象属性也是只有name属性,没有age:
l 使用原型给构造函数添加新的属性
在对象定义后,使用prototype添加新属性: [JavaScript] 纯文本查看 复制代码 <script>
// 演示:原型的基本使用
// 定义构造函数
function Person(name) {
this.name = name;
}
// 通过原型去添加一个共享属性
Person.prototype.age = 20;
var p = new Person('Jack');
console.log(p.name);
console.log(p.age);
var p2 = new Person('Rose');
console.log(p2.name);
console.log(p2.age);
</script>
打印控制台已经有结果了:
此时查看对象的属性:
发现p1和p2对象中虽然依然没有age,但是构造函数Person的prototype中已经有age了,又因为对象会共享prototype的成员,所以每个对象都可以使用age属性了!
l 给对象从原型中继承的属性赋值
尝试修改对象中,继承自prototype的属性值age:
[JavaScript] 纯文本查看 复制代码 // 尝试给从对象继承过来的属性赋值
p.age = 30;
// 我们发现p.age 是新值,而p2还是原来的值
console.log(p.age); // 30
console.log(p2.age); // 20
结果:p1的age已经变为30, p2没变
问题,既然是共享的属性,那么为什么p1修改了,p2值没变? 查看内存中的状态:
我们发现:prototype中的age没有改变,而是在对象p1中新增了自己的age属性,并且赋值为30
结论:当原型和对象自身都有相同属性时,对象中的属性优先!
l 总结 1) 当我们访问对象中的属性时,默认会先在当前对象中查找;如果在当前对象找不到,就会去对象的prototype中查找 a) 事实上,prototype也是对象,因此在prototype对象中找不到,会到prototype对象的prototype中找,逐级向上形成“原型链”,原型链的最顶级的是Object.prototype b) 那么Object.prototype也是对象,再向上还有吗?没有了,再向上是null。Object.prototype就是原型链的根 2) 修改对象的属性,不会修改prototype中的属性,而是在对象中创建新的属性
3) 某些浏览器支持__proto__属性,与prototype是同一个东西,但是某些浏览器还不支持。所以一般不要用
1.5.3 通过原型改造构造函数
为了减少对象内存占用,我们一般创建构造函数的时候,会把经常变化的属性在构造函数中声明,每个对象都有自己的属性。然后把公用的不变化的东西(通常是函数)通过原型来添加。
[JavaScript] 纯文本查看 复制代码 <script>
// 通过原型改造构造函数
// 1. 定义一个构造函数(类),构造函数其实就是普通的函数,构造函数首字母大写
function Person(name,age,gender) {
// 2. 在构造函数里面使用this.属性名=属性值的形式来初始化属性
this.name = name;
this.age = age;
this.gender = gender;
}
// 通过原型去添加一个共享方法
Person.prototype.sayHello = function() {
console.log('Hello, I am :' + this.name);
};
// 3. 使用new关键字来创建对象(实例)
var p = new Person('Jack',38,'妖');
console.log(p.name);
p.sayHello();
var p2 = new Person('Rose',18,'female');
console.log(p2.name);
console.log(p2.age);
console.log(p2.gender);
p2.sayHello();
console.log(p.name == p2.name); // false
console.log(p.sayHello == p2.sayHello); // false
</script>
验证结果:
每个对象都可以调用sayHello方法,证明共享了方法。 各个对象的sayHello方法判断是相等的。证明内存中只有一份。
总结构造函数创建对象的方法: 1、 构造函数里面写变化属性。 2、 原型上去添加方法。
1.5.4 通过原型扩展JS原生对象
事实上不仅仅我们自己定义的对象有原型,JS中原生的对象也有原型。我们可以通过原生对象的原型对原生的JS进行扩展!
原生的JS中,Array对象并没有sum方法,尝试调用报错:
[JavaScript] 纯文本查看 复制代码 // 使用原型来扩展原生JS对象
var arr = [10,20,30];
// 直接调用arr.sum方法,报错
console.log(arr.sum()); // sum is not a function
我们使用原型进行扩展: [JavaScript] 纯文本查看 复制代码 <script>
// 使用原型来扩展原生JS对象
var arr = [10,20,30];
// console.log(arr.sum());
// 封装一个对数组求和的方法
function sum(arr) {
// 求和
var res = 0;
for (var i = 0; i < arr.length; i++) {
res += arr[i];
}
return res;
}
console.log(sum(arr));
// 不好 不够智能
// 通过原型给数组添加一个sum的方法
Array.prototype.sum = Array.prototype.sum || function() {
var res = 0;
for (var i = 0; i < this.length; i++) {
res += this[i];
}
return res;
};
console.log(arr.sum());
console.log(arr2.sum());
</script>
结果:
|