面向对象与面向过程
面向对象:站在指挥者的角度(重点在执行的结果)
面向过程:站在执行者的角度(重点在执行过程)
面向对象不是替代面向过程,而是封装了面向过程
编程思想=面向对象+面向过程
面向对象的三大特征:
封装:用在对象封装
继承:在js中,是指对象之间的关系(比如父类与子类的继承关系:子类可以使用父类是的方法或数据)
多态:在js中没有多态的概念,在强类型语言中才有
强类型语言:强制类型定义的语言,一旦某一个变量被定义类型,如果不进行强制转换,则它永远就是该数据类型了,强类型语言包括Java、.net 、Python、C++等语言
弱类型语言:弱类型定义的语言,某一个变量被定义类型,该变量可以根据环境变化自动进行转换,不需要经过显性强制转换。弱类型语言包括vb 、PHP、javascript等语言
强弱类型语言之间的区别:是否会隐性的进行变量类型转变
弱类型语言速度比强类型快一些,但是强类型语言的严谨性能避免不必要的错误
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div,
p {
height: 100px;
width: 500px;
background-color: #f99;
margin: 10px;
}
</style>
</head>
<body>
<div></div>
<div></div>
<div></div>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<script>
// 面向过程的做法
// 1.获取元素-div
/* var divs = document.querySelectorAll('div')
// 2.for循环遍历,给每一个div添加边框
for (let i = 0; i < divs.length; i++) {
divs[i].style.border = '10px solid #ccc'
}
// 1.获取元素-p
var ps = document.querySelectorAll('p')
// 2.for循环遍历,给每一个div添加边框
for (let i = 0; i < ps.length; i++) {
ps[i].style.border = '10px solid #ccc'
} */
// 以上代码实现了功能,但是代码大量重复
// 封装代码
// 获取元素
/* function getEle(ele) {
return document.querySelectorAll(ele)
}
// 设置边框
function setBorder(ele) {
for (let i = 0; i < ele.length; i++) {
ele[i].style.border = '10px solid #ccc'
}
}
// 确定元素,添加样式
setBorder(getEle('div'))
setBorder(getEle('p')) */
// 上面封装的函数解决了代码重复的问题,造成全局污染的问题
// 面对对象的做法
var tools = {
// 获取元素
getEle: function(ele) {
return document.querySelectorAll(ele)
},
// 设置边框
setBorder: function(ele) {
for (let i = 0; i < ele.length; i++) {
ele[i].style.border = '10px solid #ccc'
}
}
// ...设置字体大小,实现动画效果
}
tools.setBorder(getEle('div'))
tools.setBorder(getEle('p'))
</script>
</body>
</html>
创建对象的四种方式
1.Object创建对象(new Object) ——存在缺点:麻烦
2.对象字面量 ——简单些,存在的问题:不能批量的创建对象
3.工厂函数 (这是普通函数,不是构造函数)————可以批量创建对象,存在的问题:无法识别类型
4.自定义构造函数
构造函数:1.函数首字母大写(规范),2.必须要和new一起使用
给创建出来的对象添加属性和方法,通过this添加
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 创建对象的方式 -->
<script>
// 1.Object创建对象 ——存在缺点:麻烦
/* var obj = new Object()
obj.name = 'zs'
obj.age = 20
obj.sayHi = function() {
console.log('hello');
}
console.log(obj.name, obj.age);
obj.sayHi() */
// 2.对象字面量 ——简单些,存在的问题:不能批量的创建对象
/* var obj = {
name: 'zs',
age: 20,
sex: '男',
sayHi: function() {
console.log('hello');
}
}
console.log(obj.name, obj.age, obj.sex);
obj.sayHi() */
// 3.工厂函数 (这是普通函数,不是构造函数)————可以批量创建对象,存在的问题:无法识别类型
/* function person(name, age, sex) {
var obj = {
name: name,
age: age,
sex: sex,
sayHi: function() {
console.log('hello');
}
}
return obj
}
var p1 = person('zs', 20, '男')
p1.sayHi()
console.log(p1);
console.log(p1.name); */
// 4.自定义构造函数
// 构造函数:1.函数首字母大写(规范),2.必须要和new一起使用
// 给创建出来的对象添加属性和方法,通过this添加
function Person() {
this.name = 'zs'
this.age = 22
this.sayHi = function() {
console.log('hi');
}
}
var p2 = new Person()
p2.sayHi()
console.log(p2);
</script>
</body>
</html>
new 做了四件事
1.创建了一个空对象
2.将this指向创建的空对象
3.执行构造函数内的代码
4.把新创建的对象给返回出去
自定义构造函数后创建对象的问题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 自定义构造函数创建对象的问题
function Person(name, age) {
this.name = name
this.age = age
this.sayHi = function() {
console.log('hello');
}
}
var p1 = new Person('李四', 20)
/* p1.sayHi()
console.log(p1.name, p1.age); */
var p2 = new Person('张三', 22)
/* p2.sayHi()
console.log(p2.name, p2.age); */
// 复杂数据类型的相等比较,比较的是地址,两个对象的sayHi方法是否是同一个函数
console.log(p1.sayHi == p2.sayHi); //false,说明不是同一个地址,每个方法有各自的地址(各自占一份内存)
// 经过上面的比较,得知构造函数创建对象存在的问题:浪费了内存,在内存中只需要一份sayHi方法即可
</script>
</body>
</html>
构造函数创建对象存在的问题:浪费内存
术语:
1.实例(实例对象):通过构造函数创建出来的对象(实例可以有多个)
2.实例化:构造函数创建对象的过程
3.成员:对象中的属性和方法的统称
解决构造函数创建对象的问题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 当成普通的函数,可以解决内存浪费的问题,但是又造成了全局污染的问题(方法名有可能会同名)
function sayHi() {
console.log('hello');
}
function run() {
console.log('running');
}
// 使用对象来封装,减少全局污染的问题
var obj = {
sayHi: function() {
console.log("hello");
},
run: function() {
console.log('running');
}
}
// 自定义构造函数
function Person(name, age, sex) {
this.name = name
this.age = age
this.sex = sex
this.sayHi = obj.sayHi
this.run = obj.run
}
var zs = new Person('zs', 20, '男')
console.log(zs);
var ls = new Person('ls', 26, '男')
console.log(ls);
console.log(zs.sayHi == ls.sayHi); //true,说明两个方法指向同一个地址(obj对象中的方法)
</script>
</body>
</html>
小结:解决的思想:把构造函数中的方法提取到构造函数的外面,可以放在一个对象里面当成员,这样内存只会那有一份方法
原型
1.任何函数,都有prototype属性
2.函数的prototype属性值是一个对象,把这个对象叫做原型(原型对象)
3.原型的作用:通过构造函数创建的实例对象,可以直接访问构造函数的prototype属性上的任意成员
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>原型</title>
</head>
<body>
<script>
// 自定义构造函数
function Person() {}
// 给原型对象添加color属性
Person.prototype.color = 'red'
// 把sayHi方法添加到原型中
Person.prototype.sayHi = function() {
console.log('hello');
}
Person.prototype.run = function() {
console.log('running');
}
console.log(Person.prototype);
// p1是Person的实例对象
var p1 = new Person()
p1.sayHi()
p1.run()
console.log(p1.color);
// console.log(p1);
// p2也是Person的实例对象
var p2 = new Person()
p2.sayHi()
p2.run()
console.log(p2.color);
// console.log(p2);
//p1的sayHi和p2的sayHi方法是同一个方法,都是来源于Person的原型对象上的sayHi方法
console.log(p1.sayHi == p2.sayHi); //true,说明指向同一个地址
</script>
</body>
</html>
小结:
解决构造函数创建对象造成的内存浪费问题的最终解决方法:使用原型(prototype)
原型:函数的prototype属性
原型的作用:构造函数创建的实例对象,可以直接访问构造函数的prototype属性上的任意成员,解决了构造函数创建对象造成的内存浪费