JavaScript面向对象

JavaScript面向对象

一、学习目标
  1. 了解对象和面向对象
  2. 掌握创建对象的方法
  3. 掌握简单工厂模式的使用
  4. 理解构造函数与原型对象
  5. 掌握对象继承的用法

二、什么是对象

对象是相关属性和方法的集合体

属性
方法

String对象

length属性
indexOf() 、replace()方法

Array对象

length属性
sort()、concat()、join()方法

Date对象

get×××:获取年、月、日、时、分、秒等等
set×××:设置年、月、日、时、分、秒等等

Math对象

round()、max()、min() 等方法

Boolean对象

toString()方法

RegExp对象

compile()、exec()、test()方法


三、常见内置对象

String对象

字符串字面量
new String()

Date对象

new Date()

Array对象

[ element0, element1, …, elementN ]
new Array()

Math对象所有属性和方法都是静态的

Boolean对象
new Boolean()

RegExp对象

直接量
new RegExp()


四、自定义对象方式分类

创建方式

  1. 基于Object对象创建自定义对象
  2. 使用字面量赋值方式创建自定义对象
  3. 简单工厂模式创建自定义对象
  4. 使用构造函数创建自定义对象
  5. 使用原型对象创建自定义函数(推荐指数:☆☆☆☆☆)

五、Object对象方式

语法

let 对象名称 = new Object();

示例

let student = new Object();
student.name = "张三";
student.age = 18;
student.email = "zhangsan@163.com";
student.hobby = "打球";
student.showName = function() { 
    alert( this.name );
}
student.showName();

六、字面量赋值方式

语法

let 对象名称 ={
    属性名1:属性值1,
    属性名2:属性值2,
    属性名n:属性值n,
    函数名:function(){
        //函数主体
    };
};

示例

let student = {
     name :"张三",
     age : 18,
     email : "zhangsan@163.com",
     hobby : "打球",
     showName : function() {
              alert( this.name );
     }
 }
student.showName();

七、简单工厂模式

问题

基于Object对象或使用字面量创建对象,如需批量创建对象,会产生大量重复代码

技巧

  • 使用简单工厂模式封装对象的创建逻辑

  • 模式是指在某一环境下某个问题的一种解决方案

  • 简单工厂模式是一种用来创建对象的软件设计模式

示例

将对象的创建逻辑封装在一个函数中

function createStudent( name, age, email, hobby ) {
       var p = new Object();
       p.name = name;
       ……
       p.showName = function() { 
           alert( this.name ); 
       }
       return p;
}

使用工厂函数创建对象

var person1 = createStudent("张三", 18, "zhang3@163.com", "打球");
person1.showName();

var person2 = createStudent("李四", 19, "lisi@163.com", "看书");
person2.showName();

八、构造函数

问题

前述创建对象的方法无法区分不同类型的对象

分析

定义构造函数,用以创建特定类型的对象

//构造函数名首字母大写,以区别于其他函数
function Student( name, age, email, hobby ) {
      this.name = name;
            ……
      this.showName = function() { 
          alert(this.name); 
      }
}

与工厂函数相比

  1. 没有显式创建Object对象,没有return语句
  2. 将属性和方法赋给this对象

示例

var stu1 = new Student( "张三", 18, "zhangsan@163.com", "打球" );
stu1.showName();

var stu2 = new Student( "李四", 19, "lisi@163.com", "看书" );
stu2.showName();

小结

构造函数执行过程中会经历以下4个步骤

  1. 创建一个新对象
  2. 将构造函数的作用域赋给新对象,因此this就指向了这个新对象
  3. 执行构造函数中的代码,为这个新对象添加属性及方法
  4. 返回新对象

九、构造函数问题分析

问题

上面示例中构造函数的定义方式会导致showName()方法在每个实例上都要重新创建一遍

function Student( name, age, email, hobby ) {
    //……
    this.showName = function() { 
        alert(this.name); 
    }
}

分析

  • 通过函数定义的方式把showName()方法转移到构造函数的外部
  • 所有实例共享全局作用域中定义的同一个showName()函数
function Student( name, age, email, hobby ) {
    //……
    this.showName = showName
}
function showName() {
    alert(this.name);
}

小结

通过分析改造确实决绝了上述问题,但是依然存在一定的设计缺陷,原因:

  • 全局函数实际上仅为某个对象调用
  • 全局函数破坏了自定义类型的封装性

所以此时原型对象因此而诞生…


十、原型对象

概念

  • 每个函数都有一个prototype属性,指向其原型对象
  • 使用原型对象方式添加属性和方法,所有实例会共享它所包含的属性和方法

语法

构造函数名.prototype.属性或方法

示例

function Student() { }
Student.prototype.name = "张三";
//……
Student.prototype.showName = function() { 
    alert(this.name); 
};
//……

不想被共享的属性或方法,可以定义在构造函数中

原型对象图解

当实例读取某个属性时

  • 首先在实例本身开始搜索,找到则返回
  • 如没有找到,则继续在原型对象中查找

 [
在这里插入图片描述


十一、原型链

概念

  • 一个类型的实例是另一个类型的原型对象
  • 相关的原型对象层层递进,构成了实例与原型的链条,即原型链

示例

//父类
function Person() { this.foot = 2; }
Person.prototype.getFoot = function() { return this.foot; }
//子类
function Woman() { this.head = 1; }
//Woman 继承 Person
Woman.prototype = new Person();

Woman.prototype.getHead = function() { return this.head; }
//创建子类实例对象 woman1
var woman1 = new Woman();

alert(woman1 instanceof Woman);  //true
alert(woman1 instanceof Person); //true
alert(woman1 instanceof Object); //true
alert(woman1.getFoot()); //2

原型链图解

在这里插入图片描述

woman1调用getFoot()的步骤:

  • 搜索实例 → 搜索 Woman.prototype → 搜索 Person.prototype

搜索的过程要一环一环地前行到原型链的末端才会停下来

完整的原型链

所有函数的默认原型都是Object对象的实例

在这里插入图片描述


十二、方法重写

概念

子类型可以通过 prototype 对象重写父类型中的方法

示例

//父类类型构造
function Person() { this.foot = 2; }
//父类型Person getFoot方法
Person.prototype.getFoot = function() { return this.foot; }

//子类类型构造
function Woman() { this.head = 1; }
//子类Woman 继承 父类Person
Woman.prototype = new Person();

Woman.prototype.getHead = function() { return this.head; }

//【子类重写】
Woman.prototype.getFoot = function() { return false; }

注意事项

Woman的实例调用 getFoot()方法时,调用的是重写后的方法,但是通过Person的实例调用getFoot()方法时,还会调用原来的方法.

十三、对象继承

问题

function Person() {
    this.skinColor = [ "black", "white" ]; // 肤色
}
function Woman() {}

Woman.prototype = new Person(); // 继承了Person

var woman1 = new Woman();
woman1.skinColor.push( "yellow" );
alert(woman1.skinColor); // 输出什么?

var woman2 = new Woman();
alert(woman2.skinColor); // 输出什么?

在这里插入图片描述


问题1: 两个实例输出的信息一样,为什么?


问题2: 创建子类型的实例时,不能向父类型的构造函数中传参

分析

第一个问题是来自包含引用类型值的原型,由于包含引用类型值的原型属性会被所有实例共享 ,在通过原型实现继承时,原型实际上会变成另一个类型的实例,因此,原先的实例属性也就变成了现在的原型属性了。

方案

构造函数借调。

十四、借用构造函数

概念

借用构造函数又被称为伪造对象经典继承,指在子类型构造函数的内部调用父类型的构造函数

示例

<script>
   function Person() {
       this.skinColor = ["black", "white"]; // 肤色
   }

   function Woman() {
       Person.call(this); // 也可以使用apply()方法
   }

   let woman1 = new Woman();

   woman1.skinColor.push("yellow");
   alert('woman1:' + woman1.skinColor); // 输出什么?

   let woman2 = new Woman();
   alert('woman2:' + woman2.skinColor); // 输出什么?
</script>

小结

Person.call(this)表示“借调”了父类型的构造函数,通过使用call()方法(也可以使用apply()方法)。实际上是在新创建的 Woman 实例环境下调用 Person 的构造函数,这样在新的Woman对象上执行 Person() 函数中定义的所有对象初始化代码。这样Woman每个实例都会具有自己的 skinColor属性的副本。

十五、借用构造函数传参

概念

借用构造函数可以在子类型构造函数中向父类型构造函数传参

示例

function Person(name) {
    this.name = name;
}
function Woman(){
    Person.call( this, "amy" );
    this.age = 18;
}
……

注意

为了确保父类型构造函数不会重写子类型的属性,可以在调用父类型构造函数后再添加应该在子类型中定义的属性

十六、组合继承

概念

  • 组合继承也叫做伪经典继承
  • 将原型链和借用构造函数的技术组合到一块,融合二者的优点并规避二者的缺陷,是JavaScript开发中最常用的一种继承模式
  • 使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承
  • 既实现了函数复用,又能够保证每个实例都有自己的属性

示例

<script>
   function Person(name) {
       this.name = name;
       this.skinColor = ["black", "white"]; // 肤色
   }

   Person.prototype.sayName = function () {
       alert(this.name);
   }

   function Woman(name, age) {
       Person.call(this, name); // 继承属性
       this.age = age;
   }

   Woman.prototype = new Person(); // 继承方法
   Woman.prototype.sayAge = function () {
       alert(this.age);
   }
</script>
十七、总结

总结思维图

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

众生云海,一念初见

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值