目录
一、了解面向对象
面向对象是我们的一种开发方式。
例子:一份烤鸭
在面向过程中:所有事情都是自己来做,要考虑顺序(先杀鸭子,先烤鸭子,还是先切鸭子);要考虑步骤(杀鸭子的时候,先干什么,后干什么);要考虑细节(胡萝卜的宽窄程度,酱的咸甜口等)。而在面向对象中,我们只需要点一份烤鸭,等待烤鸭上桌就好。(会有专门的人来处理这些事情)
1. 面向过程:一种关注过程的开发方式
=> 在开发过程中,我们要关注每一个细节,步骤,顺序。
2. 面向对象:一种面向对象的开发方式
=> 在开发过程中,我们看看有没有一个对象能帮助我们完成任务。
开发过程中的例子:全选(详见之前的文章:JavaScript案例(二):全选;html+css+js实现全选功能_小张小张的博客-CSDN博客)
面向过程:考虑获取哪些元素;考虑给哪些元素绑定点击事件;考虑点击的时候,做什么。
面向对象:考虑有没有一个对象能帮我们完成全选;如果有,那么我们就直接让这个对象帮我完成;如果没有,我们就需要制造这样一个对象来帮我完成。
我们需要这样一个‘机器’:这个机器能给我生产一个对象;生产出来的对象能完成全选功能;我们只要准备好这个机器就可以了;本次开发过程中,我们制造机器,制造好之后,用机器生成一个对象完成全选功能;当你第二次想进行全选功能开发的时候,那么我们就不用再次制造机器了;只要用原先准备的机器,再制造一个新的全选对象就可以了。
我们制造“机器”的过程,就是面向对象封装的过程,面向对象的核心:高内聚,低耦合(就是对面向过程的高度封装)
总结:核心就是找到一个机器,能批量生产对象;
生产出来的对象,每一个对象都有自己的属性和方法;
每一个对象可能类似,但是内容不太一样;可以像函数一样传递参数,可以给每一个对象添加一些不同的内容,利用构造函数去创建一个可以完成任务的对象,依赖这个对象完成功能。
这个机器要生产有属性有方法的合理的对象。(机器:就是构造函数; 属性:直接写在构造函数体内; 方法: 写在构造函数的原型上)
二、创建对象的方式
1. 字面量方式创建对象
var obj = { ... }
//1. 字面量方式创建对象
var obj = {
name: 'jack',
age: 18,
sayHi: function () {console.log('hello world!')}
}
//如果想创建第二个对象
var obj2 = {
name: 'Rose',
age: 20,
sayHi: function () {console.log('hello world!')}
}
重复写同样的方法,name等,不符合批量制造的原则,因此这种方式不经常用。
2. 内置构造函数创建对象
var obj = new Object()
可后期动态添加
//2. 内置构造函数创建对象
var obj = new Object()
obj.name = 'Jack'
obj.age = 18
obj.sayHi = function () { console.log('hello world')}
这种方式同样也不方便批量制造对象,所以也不经常用。
3. 工厂函数创建对象
1)制造一个工厂函数;
2)使用工厂函数去创建对象
//3. 工厂函数创建对象
//3.1 创建工厂函数
function createObj(name, age) {
//手动创建一个对象
var obj = {}
obj.name = name
obj.age = age
obj.sayHi = function () { console.log('hello world')}
//手动返回这个对象
return obj
}
//3.2 使用工厂函数去创建对象
var obj1 = createObj('Jack', 18)
var obj2 = createObj('Rose', 20)
console.log(obj1, obj2)
4. 自定义构造函数创建对象
1)制造一个自定义构造函数
2)使用自定义构造函数去创建对象
//4. 自定义构造函数创建对象
//4.1 制造一个自定义构造函数
// 构造函数会自动创建对象,自动返回这个对象,我们只需要手动想里面添加内容就可以了
function createObj(name, age) {
//自动创建对象
//手动向兑现里面添加成员
//这里的 this 指向当前实例(new 前面的变化)
this.name = name
this.age = age
this.sayHi = function () { console.log('hello world')}
// 自动返回对象
}
// 4.2 使用自定义构造函数去创建对象
// 构造函数在使用的时候,需要和 new 关键字连用,如果不连用,那么就没有意义
// 第一次调用 creteObj 的时候,和new 关键字连用了
// 我们 createObj 里面的 this 指向了 obj1
// 函数内部的代码在执行的时候,就是在向 obj1 添加了name, age 和 sayHi 三个成员
var obj1 = new createObj('Jack', 18)
// 第二次调用 creteObj 的时候,和new 关键字连用了
// 我们 createObj 里面的 this 指向了 obj2
// 函数内部的代码在执行的时候,就是在向 obj2 添加了name, age 和 sayHi 三个成员
var obj2 = new createObj('Rose', 20)
console.log(obj1, obj2)
三、构造函数的使用
1. 构造函数和普通函数没有区别
只不过在调用的时候和new关键字连用。(但是出现了得看看函数的具体内容才能判断这个函数是不是构造函数的问题)
2. 构造函数名首字母大写
书写构造函数时,函数名首字母大写,这样的话只用看看名字,就可以判断是否为构造函数,并知道要和new关键字连用。
function person() {
console.log('hello world')
}
function Person() {
this.name = 'Jack'
}
3. 要和new关键字连用
调用的时候,需要和new关键字连用,因为只有和new 关键字连用的时候,这个函数才会有自动创建和返回对象的能力。(因为在之前我们之所以书写构造函数,就是为了使用它去批量生产对象,如果不和new关键字连用,那么该函数没有创建对象的能力,就没有自动返回对象的能力)
var obj = new Person()
console.log(obj)
var obj2 = new Person()
console.log(obj2)
4. 不用写小括号
调用构造函数的时候,如果不需要传递参数,那么可以不用写小括号。
但是如果需要传递参数,那么必须写上小括号,但是书写规范推荐我们必须写上小括号。
//4.
var obj = new Person() //推荐
console.log(obj)
var obj2 = new Person //不推荐
console.log(obj2)
5. 直接书写 this.xxx = 值
当你的函数名和new 关键字连用的时候,函数内部的 this 指向当前实例(new 关键字前面的变量),我们直接在函数体内书写 this.xxx = 值,就是在向自动创建出来的对象里面添加内容。
6. 不用写return
构造函数内部不用写return,因为当 return 一个剧本数据类型的时候,写了等于白写;当 return 一个复杂数据类型的时候, 构造函数等于白写。
function Person() {
this.name = 'Jack'
this.age = 18
return 123
}
var obj = new Person()
console.log(obj)
当 return 一个剧本数据类型的时候,并没有返回 return 中返回的 ‘123’ 的值。
function Person() {
this.name = 'Jack'
this.age = 18
return [1,2,3]
}
var obj = new Person()
console.log(obj)
当 return 一个复杂数据类型的时候, 构造函数中 name 和 age 的值并没有返回。
四、构造函数的不合理
当在构造函数体内书写方法的时候,需要向对象上添加方法,只要创建一次对象(new 一次)就会有一个函数在占用空间,创建 100 次对象,就会有 100 个一模一样的函数出现,但是其中有99个是重复的,没有必要存在。
<script>
//1. 准备一个构造函数
function Person(name, age) {
this.name = name
this.age = age
this.sayHi = function () { console.log('hello world') }
}
//2. 创建对象
var p1 = new Person('Jack', 18)
var p2 = new Person('Rose', 20)
console.log(p1, p2)
//调用两个对象的sayHi 方法
p1.sayHi()
p2.sayHi()
//看一下是不是真的是两个对象
console.log(p1 == p2) //false,确实是两个对象
//两个对象里面的函数
console.log(p1.sayHi)
console.log(p2.sayHi)
console.log(p1.sayHi == p2.sayHi) //false, 是两个函数
</script>
代码分析:
p1 第一次执行Person 函数,会把函数内部的this 指向p1,会把函数体内的代码全部执行一遍,向p1 上添加 name 属性,向p1 上添加 age 属性,向 p1 上添加 sayHi 方***创建一个函数出来。
p2 第二次执行Person 函数,会把函数内部的this 指向p2,会把函数体内的代码全部执行一遍,向p2 上添加 name 属性,向p2 上添加 age 属性,向 p2 上添加 sayHi 方***创建一个函数出来。
五、原型prototype
定义:每一个函数天生自带一个属性,叫做 prototype,是一个对象。构造函数也是函数,也会有这个自带的空间 prototype。
既然prototype 是一个对象,我们就可以使用对象操作的语法,向里面添加一些内容。
对象:每一个对象,在你访问他的成员的时候,如果自己没有这个属性,会自动去所属构造函数的 prototype 上查找。自定义构造函数创建的对象也是对象,当你访问某一个成员的时候,如果没有,也会自动去所属构造函数的原型上查找,哪一个构造函数创建的对象,这个对象就属于哪一个构造函数,因为构造函数在创建对象的过程,我们起名为 实例化 的过程,创建出来的对象叫做这个构造函数的一个 实例化对象。
<script>
function Person() {}
Person.prototype.sayHi = function () { console.log('我是 Person 原型上的方法') }
console.log(Person.prototype)
//创建一个实例化对象
//因为 p1 是 Person 实例化出来的对象, p1就是属于Person 这个构造函数的
//当你访问 p1 的 sayHi 成员的时候,p1 自己是没有的,会自动去 Person 的原型(prototype)上查找
var p1 = new Person()
console.log(p1)
p1.sayHi()
//创建第二个实例化对象
//因为 p2 也是 Person 的实例化对象,p2 没有 sayHi 成员,也会自动去 Person 的原型上查找
var p2 = new Person()
console.log(p2)
p2.sayHi()
/* p1 的 sayHi 方法和 p2 的sayHi 方法都是使用的 Person 构造函数的原型上的方法
我只要向 Person 的原型上添加一些方法
所有的 Person 的每一个实例都可以使用
并且使用的都是同一个函数,不会出现浪费空间的行为
*/
console.log(p1.sayHi === p2.sayHi) //true, 说明是一个函数
</script>
面向对象开发:选项卡
1. 书写一个构造函数
2. 创建一个能够完成选项卡的对象(属性:选项卡中能够点击的按钮; 属性:选项卡中用于切换的盒子; 方法:控制点击的按钮添加点击事件,给盒子进行切换操作)
3. 使用构造函数创建一个选项卡事件
六、案例:简单版面向对象选项卡
详见下一篇文章:JavaScript案例(六):选项卡;JavaScript简单版面向对象案例:选项卡_小张小张的博客-CSDN博客
文章参考视频:b站千锋前端学习营:千锋前端JavaScript全套教程_JS零基础完美入门到项目实战https://www.bilibili.com/video/BV1W54y1J7Ed?share_source=copy_web