js构造函数--面向对象

Posted 码妈

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了js构造函数--面向对象相关的知识,希望对你有一定的参考价值。

js构造函数--面向对象

面向对象: 在程序中,先用对象集中保存属性和方法。然后,再按需使用保存好的属性和方法。(细品后文,了解面向对象思想)
三大特点: 封装、继承、多态

封装

  • 第一种方式 { } 创建对象

写法:
var 对象名={
属性名: 属性值,
属性名: 属性值,
… : …
方法名: function(){ … }
}

函数 和 方法 区别:
1). 相同点: 本质是完全一样的!都是function(){ … }
2). 不同点:
i. 函数: 特指不包含在任何对象中,可独立使用的function
ii. 方法: 特指包含在对象中,必须先找到对象,才能调用的function

如何访问对象中的属性和方法:
1). 访问对象中的属性: 必须先找到对象,再用.操作符,进入对象中,使用对象的属性
对象.属性
2). 访问对象中的方法: 必须先找到对象,再用.操作符,进入对象中,使用对象中的方法。但是必须加()调用
对象.方法名()

示例: 使用 { } 创建对象

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>对象第一种方式</title>
</head>
<body>
<script>
  // 创建一个对象,保存一个学生lilei的个人信息属性和自我介绍方法
  let lilei = {
    sname:"Li Lei",
    sage:11,
    intr:function(){
      console.log(this); // {sname: "Li Lei", sage: 11, intr: ƒ}
      console.log(`I'm ${this.sname}, I'm ${this.sage}`)
    }
  };

  console.log(lilei); // {sname: "Li Lei", sage: 11, intr: ƒ}
  console.log(lilei.sage); // 11 输出lilei的年龄
  lilei.intr(); //  // I'm Li Lei, I'm 11  请lilei做自我介绍

  lilei.sage++; // 过了一年,lilei长了一岁
  console.log(lilei.sage);// 12  再输出lilei的年龄
  lilei.intr(); // I'm Li Lei, I'm 12 再请lilei做自我介绍
  console.log(lilei); // {sname: "Li Lei", sage: 12, intr: ƒ}
</script>
</body>
</html>

结果
在这里插入图片描述

  • 第二种方式 用new来创建对象

用new来创建:
如何: 2步:
1. 先创建一个空对象:var 对象名=new Object()
2. 再强行向空对象中添加新属性和新方法
对象名.新属性=值
对象名.新属性=function(){ … this.属性名 … }

揭示: js中所有对象底层其实都是关联数组:
a. 对象和关联数组都是名值对儿的组合。
b. 无论访问数组的元素,还是访问对象中的属性,都有两种方式:
1). 标准写法: 数组名/对象名[“下标名或属性名”]
2). 简写: 数组名/对象名.下标明或属性名
3). 特例: 如果要访问的下标名或属性名不是写死的,来自于一个动态变化的变量,则既不能用.,也不能用[""],只能用[变量]
c. 访问数组或对象中不存在的成员,不会报错,而是返回undefined。
固定用法: 如何判断一个对象是否包含某个成员: 强行访问
数组或对象.下标名或属性名 !== undefined 说明包含该成员
数组或对象.下标名或属性名 === undefined 说明不包含该成员
d. 强行给数组或对象中不存在的位置赋值,不会报错,而是自动为数组或对象添加该新的成员。
固定用法: 如何给数组或对象中添加一个新成员: 强行赋值!
数组或对象.下标名或属性名=新值!
e. 都可以用for in循环遍历每个成员
//in会依次取出数组或对象中每个下标名或属性名
//保存在in前的变量中
for(var 变量 in 数组或对象){
//可以通过"数组或对象[变量]"方式来访问数组或对象中每个成员
}

示例: 使用new Object()创建对象

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>对象第二种方式</title>
</head>
<body>
<script>
  // 用new Object()创建一个对象,保存一个学生lilei的个人信息属性和自我介绍方法
  let lilei = new Object();
  lilei.sname = "Li Lei";
  lilei["sage"] = 11;
  lilei.intr = function(){
    console.log(`I'm ${this.sname}, I'm ${this.sage}`)
  };
  console.log(lilei); // {sname: "Li Lei", sage: 11, intr: ƒ}
  console.log(lilei.className); // undefined 想获取李磊的className属性,没有不报错返回undefined
  lilei.className = "初一2班"; // 为lilei强行添加className
  console.log(lilei); // {sname: "Li Lei", sage: 11, className: "初一2班", intr: ƒ}

  // 想遍历李磊的每个属性
  for(let shuxingming in lilei){
    //错误:in前的是一个变量,值随遍历不断变化,所以变量无论如何不能放在""中!
    //console.log(lilei["shuxingming"]);
    //错误: .是[""]的简写,在底层同样会被翻译为[""]原版。所以,错误原因和第一种情况是一样的!
    //console.log(lilei.shuxingming);
    //正确: 因为[]中需要一个字符串类型的下标名或属性名,而in前的变量,每次获得的刚好就是一个下标名或属性名的字符串。所以,刚好对象。
    console.log(lilei[shuxingming]);
  }
</script>
</body>
</html>

结果
在这里插入图片描述

  • 第三种方式 使用构造函数

构造函数: 描述同一类型所有对象的统一结构的函数
特点: 重用
何时: 只要想反复创建同一类型的多个相同结构的对象,只是属性值不同而已时,都用构造函数来创建

示例:构造函数

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>构造函数</title>
</head>
<body>
<script>
  //       创建学生类型的构造函数
  function Student(sname, sage){
    // 因为将来创建对象时,才能知道具体每个学生的姓名和年龄,而且,每个学生的姓名和年龄各不相同,
    // 所以,定义构造函数时,暂时用变量占位。
    // this是固定写法,只有前边加上this.的属性和方法,将来才能进入新对象中!反之,前边没有this.的,都无法进入将来的新对象中。
    this.sname = sname; // 习惯上,变量名应该和属性名相同!
    this.sage = sage;

    //方法。这里只是演示,实际开发时方法写在原型对象prototype中
    //因为将方法放在构造函数内,那么每创建一个新对象,都会重复创建相同方法的副本,导致浪费内存!
    this.intr = function(){ // 学生的自我介绍方法。这里只是演示,实际开发时方法写在原型对象prototype中
      console.log(`I'm ${this.sname}, I'm ${this.sage}`)
    }
  }
    //  使用构造函数创建的都必须用new来调用 
  let lilei = new Student("Li Lei",11); 
  let hmm = new Student("Han Meimei",12);
  console.log(lilei); // Student {sname: "Li Lei", sage: 11, intr: ƒ}
  lilei.intr(); // I'm Li Lei, I'm 11
  console.log(hmm); // Student {sname: "Han Meimei", sage: 12, intr: ƒ}
  hmm.intr(); // I'm Han Meimei, I'm 12
</script>
</body>
</html>

结果
在这里插入图片描述

继承

  1. 问题: 如果将方法定义放在构造函数内,那么每创建一个新对象,都会重复创建相同方法的副本——浪费内存!
  2. 解决: 今后,只要多个子对象,都要使用一个共同的功能时,都要用继承方式来实现!
  3. 什么是继承: 父对象中的成员,子对象无需重复创建,就可直接使用!
  4. 如何: js中都是采用继承原型对象的方式来实现继承
    (1). 什么是原型对象(prototype):每种类型中,替将来所有的子对象集中保存共有成员的父对象。
    (2). 何时: 今后只要多个子对象都需要共用一个功能时,都要将这个功能添加到原型对象中一份即可!
    (3). 结果: 只要保存在原型对象中的成员,所有子对象无需重复创建,就可直接使用
    (4). 如何创建原型对象:
    a. 原型对象不用自己创建!
    b. 只要我们每定义一个构造函数妈妈,都会自动附赠我们一个空的原型对象爸爸。
    (5). 如何访问原型对象:
    a. 每个构造函数内都自带一个隐藏的prototype属性,指向当前构造函数配对的原型对象。
    b. 构造函数.prototype
    (6). 如何向原型对象中添加共有成员: 只能强行赋值:
    构造函数.prototype.共有成员=新值或function(){ … }
    从此!构造函数中就不应该再包含任何方法定义!
    所有共有的方法,都要集中添加到原型对象中!
  5. 何时继承:
    (1). 不用手工设置继承!
    (2). 当用new调用构造函数,创建该类型的新对象时,new的第二步会自动设置新对象的_ proto 隐藏属性指向构造函数的原型对象,形成继承关系!
    (3). js规定,凡是从
    proto 指出的引用关系,就是继承关系!
    (4). 结果: 将来使用子对象访问一个成员时: 就近原则,先自己后原型
    a. 都会先在子对象自己身体里找,如果找到要用的成员,就优先使用自己身体里的成员
    b. 如果自己身体里没有要用的成员,则js引擎会自动延
    proto _去父对象原型对象中查找父对象中保存的共有属性。如果在父对象中找到了想用的成员,则子对象一样可以直接用.访问!就和访问自己的成员一模一样!

示例: 使用构造函数继承

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>继承</title>
</head>
<body>
<script>
  //       创建学生类型的构造函数
  function Student (sname, sage) {
    this.sname = sname;
    this.sage = sage;
    //从此构造函数中就不应该包含方法定义。
    //所有共用的方法都要集中添加到原型对象中。
  }
  //在Student构造函数的原型对象中为将来所有子对象添加共有的自我介绍方法
  Student.prototype.intr = function () {
    console.log(`I'm ${this.sname}, I'm ${this.sage}`)
  };
  // 创建学生对象
  let lilei = new Student("Li Lei",11);
  let hmm = new Student("Han Meimei",12);

  console.log(lilei); // Student {sname: "Li Lei", sage: 11}
  lilei.intr(); // I'm Li Lei, I'm 11
  console.log(hmm); // Student {sname: "Han Meimei", sage: 12}
  hmm.intr(); // I'm Han Meimei, I'm 12
</script>
</body>
</html>

结果

打印结果画图理解
在这里插入图片描述在这里插入图片描述

多态

  1. 什么是多态: 只要一个函数不同情况下表现出不同的状态。
  2. 包含2种情况:
    (1). 重载(overload): 一件事,传入不同的参数,执行不同的逻辑
    (2). 重写(override):
  3. 什么是重写: 如果子对象觉得从父对象继承来的成员不好用!子对象就可以在自己内部创建一个和父对象成员同名的自有成员。
  4. 结果: 从此,这个子对象,优先使用自己的自有成员,不再使用父对象中不好用的共有成员!
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
<script>
  function Student (sname, sage) {
    this.sname = sname;
    this.sage = sage;
  }
  //想让Student家的孩子,也有好用的toString
  Student.prototype.toString=function(){
    return `sname:${this.sname}, sage:${this.sage}`
  };
  let lilei = new Student("Li Lei",11);
  let arr = [1,2,3];
  let now = new Date();
  console.log( lilei.toString() ); // sname:Li Lei, sage:11
  console.log( arr.toString() ); // 1,2,3
  console.log( now.toString() ); // Thu May 06 2021 20:30:40 GMT+0800 (中国标准时间)
</script>
</body>
</html>

结果
在这里插入图片描述

以上是关于js构造函数--面向对象的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段——JS中的面向对象编程

VSCode自定义代码片段9——JS中的面向对象编程

纯原生JS面向对象构造函数方法实现贪吃蛇小游戏

JS面向对象笔记二

js构造函数--面向对象

JS高级. 02 面向对象创建对象构造函数自定义构造函数原型