27-第二十七章 面向对象oop 构造函数(一)
Javascript是一种基于对象
(object-based)
的语言。但是,它又不是一种真正的面向对象编程(OOP)
语言,因为它的语法中没有class
(类)—–es6
以前是这样的
面向对象程序设计具有许多优点:
1、开发时间短,效率高,可靠性高,所开发的程序更强壮。由于面向对象编程的可重用性,可以在应用程序中大量采用成熟的类库,从而缩短了开发时间。
2、应用程序更易于维护、更新和升级。继承和封装使得应用程序的修改带来的影响更加局部化。
new
执行的函数,函数内部默认生成了一个对象
函数内部的this默认指向了这个new
生成的对象
new
执行函数生成的这个对象,是函数的默认返回值
一、 面向对象四个基本特征
对象:对象这个词,本身就是抽象的,也就是把客观事物抽象的成类
封装:封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
继承:通过继承创建的新类称为“子类”或“派生类”。继承的过程,就是从一般到特殊的过程。
多态:对象的多功能,多方法,及方法
二、对象是什么?
对象有”属性“
(property)
和”方法“(method)
,
三 对象实例化方式
原始模式
var Car = {
color: 'red', // 车的颜色
wheel: 4, // 车轮数量
}
alert(Car.color); // red
生成两个实例对象
var Car = {
color: 'red',
wheel: 4,
}
var Car2 = {
color: 'blue',
wheel: 4,
}
这样的写法有两个缺点,一是如果多生成几个(100个!)实例,写起来就非常麻烦;二是实例与原型之间,没有任何办法,可以看出有什么联系。
二、 原始模式的改进
我们可以写一个函数,解决代码重复的问题。
function createCar(color,wheel) {
return {
color: color,
wheel: wheel
}
}
// 然后生成实例对象,就等于是在调用函数:
var cat1 = createCar("红色","4");
var cat2 = createCar("蓝色","4");
alert(cat1.color) // 红色
三、 工厂模式
function createCar(color,wheel){ // createCar工厂
var obj = new Object; // 或obj = {} 原材料阶段
obj.color = color; // 加工
obj.wheel = wheel; // 加工
return obj; // 输出产品
}
// 实例化
var cat1 = createCar("红色","4");
var cat2 = createCar("蓝色","4");
alert(cat1.color); //红色
四、 构造函数模式
为了解决从原型对象生成实例的问题,
Javascript
提供了一个构造函数(Constructor)
模式。
所谓”构造函数”,其实就是一个普通函数,但是内部使用了this
变量。对构造函数使用new
运算符,就能生成实例,并且this
变量会绑定在实例对象上。
function CreateCar(color,wheel){ // 构造函数首字母大写
// 不需要自己创建对象了
this.color = color; // 添加属性,this指向构造函数的实例对象
this.wheel = wheel; // 添加属性
// 不需要自己return了
}
// 实例化
var cat1 = new CreateCar("红色","4");
var cat2 = new CreateCar("蓝色","4");
alert(cat1.color); // 红色
new
函数构造内部变化:
自动生成一个对象
this
指向这个对象
函数自动返回这个对象
五、 构造函数注意事项
1、此时
CreateCar
称之为构造函数,也可以称之类,构造函数就是类
2 、cat1,cat2
均为CreateCar
的实例对象
3、CreateCar
构造函数中this
指向CreateCar
实例对象即new CreateCar( )
出来的对象
4、 必须带new
5、 构造函数首字母大写,这是规范,官方都遵循这一个规范,如Number() Array()
6、constructor
这时cat1
和cat2
会自动含有一个constructor
属性,指向它们的构造函数,即CreateCar。
/kənˈstrʌktə(r)/
构造函数
要看一个实例的原型是哪一个构造函数的,只需要看
constructor
即可
alert(cat1.constructor === CreateCar); // true
alert(cat2.constructor === CreateCar); // true
7、
object instanceof constructor
运算符,验证构造函数与实例对象之间的关系。
instanceof
运算符用于测试构造函数的prototype
属性是否出现在对象的原型链中的任何位置
instanceof
运算符/'ɪnst(ə)ns/
实例,例子
alert(cat1 instanceof CreateCar ); // true
alert(cat2 instanceof CreateCar ); // true
obj1 instanceof obj2
用于确定obj2
对象是否在obj1
对象的原型上
8、实例的
__proto__
等于 实例对象对应的类的prototype
9、要扩展多个方法采用重新赋值,重新赋值 要修改
constructor
的指向
单个方法直接.
扩展就行了
10、
for in
不仅会遍历对象,还会遍历原型
function CreateFun(name, len) {
this.name = name,
this.len = len
// 实例的 `__proto__` 等于 实例对象对应的类的 `prototype`
// p1.__proto__ = CreateFun.prototype
}
CreateFun.prototype = {
constructor: CreateFun, // 对应的类(构造函数),
add(){}
}
11、原型可以改变
CreateFun.prototype = Array.prototype;
这个实例的原型拥有数组所有的方法
这个实例对象被CreateFun
实例出来的,也被Array
实例出来的
六、 JavaScript值类型(基本类型)和引用类型:
(1)值类型:数字、字符串、布尔值、null、undefined。
值类型理解:对变量赋值 会开辟新的内存地址
变量的交换等于在一个新的地方按照连锁店的规范标准(统一店面理解为相同的变量内容)新开一个分店,这样新开的店与其它旧店互不相关、各自运营。
(2)引用类型:对象、数组、函数。
引用类型理解:对变量赋值 指向相同的内存地址
变量的交换等于把现有一间店的钥匙(变量引用地址)复制一把给了另外一个老板,此时两个老板同时管理一间店,两个老板的行为都有可能对一间店的运营造成影响。
但是函数不会造成影响,因为函数内部的逻辑是不会被改变的
七、 构造函数的问题:
构造函数方法很好用,但是存在一个浪费内存的问题。
请看,我们现在为再添加一个方法 ·showWheel·。那么,·CreateCar·就变成了下面这样:
function CreateCar(color, wheel) {
this.color = color;
this.wheel = wheel;
this.showWheel = function () { // 添加一个新方法
alert(this.wheel);
}
}
//还是采用同样的方法,生成实例:
var cat1 = new CreateCar("红色", "4");
var cat2 = new CreateCar("蓝色", "4");
表面上好像没什么问题,但是实际上这样做,有一个很大的弊端。那就是对于每一个实例对象,
type
属性和eat()
方法都是一模一样的内容,每一次生成一个实例,都必须为重复的内容,多占用一些内存。这样既不环保,也缺乏效率。
alert(cat1.showWheel == cat2.showWheel); // false
八、 Prototype 原型
Javascript
规定,每一个构造函数都有一个prototype
属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。
这意味着,我们可以把那些不变的属性和方法,直接定义在prototype
对象上。
function CreateCar(color, wheel) {
// 属性写构造函数里面
this.color = color;
this.wheel = wheel;
}
// 方法写原型里面
CreateCar.prototype.showWheel = function () {
alert(this.wheel);
}
CreateCar.prototype.showName = function () {
alert('车');
}
//然后,生成实例。
var cat1 = new CreateCar("红色", "4");
var cat2 = new CreateCar("蓝色", "4");
alert(cat1.showName); // '车'
alert(cat1.showWheel == cat2.showWheel); // true
alert(cat1.showName == cat2.showName); // true
// 这时所有实例的showWheel属性和showName方法,
// 实都是同一个内存地址,指向prototype对象,因此就提高了运行效率。
alert(cat1.showWheel == cat2.showWheel); // true
九、面向对象 ES6 写法
class
(ES5
保留字,ES6
就是关键字)ES6
定义一个类
ES6
实例的时候还是new
ES5
原型上可以定义普通属性,但是不推荐
ES6
是不可以的,constructor
以外的只能是函数
私有属性后面不要加任何符号
公有属性后面也不要加任何符号
class CreateFun{
constructor(name, age) { // 私有属性
this.name = name,
this.age = age
} // 不能加符号
// 下面是公有属性
add(value) {
console.log(value);
} // 不能加符号
sub(value) {
console.log(value);
}
}
// 实例对象是被对应的类实例出来的
const obj = new CreateFun('大帅逼', '18');
十、面向过程 改写面向对象:
要求:不要出现函数嵌套
变量-> 属性
函数->方法
难点:this指向
案例:用面向对象改写 拖拽
案例:用面向对象改写 轮播