JS学习第10天——ES5中面向对象(原型和原型链、forEach、map、filter、some、every、trim、Object.keys、Object.defineProperty方法的使用)

ES6中的类:面向对象(ES6中的类class、constructor构造函数、类的继承extends、super关键字)

一、构造函数和原型

在ES6之前,JS中没有类的概念,用构造函数来实现面向对象
创建对象的三种方式:① 对象字面量 ② new Object() ③ 自定义构造函数

1、构造函数

构造函数就是一个普通函数,习惯首字母大写,调用方式必须使用new关键字。
构造函数主要用来初始化对象,即为对象成员变量赋初始值

// 将方法写在构造函数内
function Star(uname, age) {
    this.uname = uname;
    this.age = age;
   	this.sing = function() {
    	console.log('我会唱歌');
    }
}
var ldh = new Star('刘德华', 18);
var zxy = new Star('张学友', 19);
console.log(ldh.sing === zxy.sing); // 结果:false,资源浪费
// new 在执行时会做4件事:① 在内存中创建一个新的空对象② 让this指向这个新对象③ 执行函数内的代码,给新对象添加属性和方法 ④返回新对象(因此构造函数不需要return)

《1》静态成员和动态成员
成员可以添加在构造函数本身,也可以添加到构造函数内部的this上
静态成员:在构造函数本身创建的成员,只能由构造函数本身访问
实例成员:在构造函数内部创建的成员,只能由实例对象访问

2、原型prototype(显式原型属性)

《1》构造函数存在浪费内存的问题
每创建一个实例对象,都要创建一次sing方法,在内存中开辟一个新的空间,这样存在浪费内存的问题
在这里插入图片描述
《2》原型prototype
将sing方法放到另外一个地方,让所有的实例都可以访问到,这就是原型prototype。

每一个构造函数都有一个prototype属性指向另一个对象,这个对象的所有属性和方法都可以被构造函数所拥有。
原型prototype属性值是一个对象,因此也叫原型对象我们可以把那些不变的方法,直接定义在原型对象上,这样所有实例对象就可以共享这些方法

// 将方法写在原型对象prototype上
function Cats(name) {
  this.name = name;
}
Cats.prototype.say = function () {
  console.log("喵喵");
};
var boniu = new Cats("波妞");
var hangua = new Cats("憨瓜");
console.log(boniu.say === hangua.say);   // 结果:true

3、隐式原型__proto__(隐式原型属性)

每一个对象都有一个属性__proto__,这个属性值就是当前实例所属构造函数的原型prototype。
对象能够使用构造函数原型对象prototype的属性和方法,就是因为对象有__proto__属性的存在。
对象原型__proto__和原型对象prototype是等价的
(实例的隐式原型属性永远指向其缔造者的原型对象)

function Cat(name) {
  this.name = name;
}
var a = new Cat("波妞");
console.log(a.__proto__ === Son.prototype);   // true

在这里插入图片描述

4、constructor构造函数

对象原型__proto__构造函数原型对象prototype里面都有一个属性constructor,constructor被称为构造函数,因为它指向函数本身。

function Cat(name) {
  this.name = name;
}
var a = new Cat("波妞");
console.log(Cat.prototype.constructor === Cat);   // true
console.log(a.__proto__.constructor === Cat.prototype.constructor);   // true

constructor 主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数。

如果有多个方法,可以给原型对象prototype采取对象形式赋值,这样会覆盖原型对象原来的内容,修改后的原型对象constructor 就不再指向当前构造函数了,因此,需要在修改后的原型对象中,添加一个constructor指向原来的构造函数

5、构造函数、实例、原型对象三者的关系

在这里插入图片描述
① 构造函数的prototype属性上的方法,每一个实例对象都可以访问到,属于一个公共区域
② 原型对象prototype自带一个属性constructor,其指向函数对象
③ 每一个对象都带一个属性__proto__,其属性值就是当前实例所属构造函数的原型prototype
④ 隐式原型对象__proto__自带一个属性constructor,其指向函数对象

function Cat(name, age) {
  this.name = name;
  this.age = age;
}
Cat.prototype.say = function () {
  console.log("喵喵");
};
var a = new Cat("波妞", "3");
var b = new Cat("憨瓜", "4");
console.log(a.say === b.say); // true,对应第一条
console.log(Cat.prototype.constructor === Cat);  // true,对应第二条
console.log(a.__proto__ === Cat.prototype);  // true,对应第三条
console.log(a.__proto__.constructor === Cat);  // true,对应第四条

6、原型链

在这里插入图片描述

7、JS成员查找机制

① 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性
② 如果没有就查找它的原型(也就是 __proto__指向的 prototype 原型对象
③ 如果还没有就查找原型对象的原型(Object的原型对象
④ 依此类推一直找到 Object 为止(null)。
⑤ __proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线

8、原型对象this指向

构造函数中的this 指向实例对象.
原型对象里的方,这个方法里面的this 指向的是这个方法的调用者, 也就是这个实例对象

9、扩展内置对象

可以通过原型对象,对原来的内置对象进行扩展自定义的方法。(eg:给数组增加自定义求偶数和的功能)
数组和字符串内置对象不能给原型对象覆盖操作

Array.prototype = {}   // 错误写法

Array.prototype = function(){}   // 正确写法

二、ES5中的继承

ES6之前并没有extends继承,是通过构造函数+原型对象模拟实现继承、被称为组合继承。

1、call()

修改函数运行时的this指向

fun.call(thisArg,arg1,arg2,...);
// thisArg:当前调用函数this的指向对象
// arg1,arg2:传递的其他参数

2、通过构造函数继承父类型属性

核心原理:通过call()函数把父类的this指向子类的this

function Person(name, age) {
  this.name = name;
  this.age = age;
}
// 子类
function Student(name, age, sex) {
  Person.call(this, name, age); // 此时父类的 this 指向子类的 this,同时调用这个函数
  this.sex= sex;
}
var s1 = new Student("波妞", 3, "女");
console.dir(s1);

3、通过原型对象继承父类型方法

一般情况下,对象的方法都在构造函数的原型对象中设置,通过构造函数无法继承父类方法。
核心原理:将子类所共享的方法提取出来,让子类的prototype = new 父类()

本质:子类prototype等于实例化父类,因为父类实例化之后另外开辟空间,就不会影响原来父类原型对象

三、ES5中的新增方法

1、数组方法

遍历方法:forEach()map()filter()some()every()
《1》forEach()
不改变原始数组没有返回值

arr.forEach(function(currentValue,index,arr));
// currentValue:数组当前项的值, index:当前的索引,arr:数组对象本身
var arr = [3, 5, 6, 2];
var a = arr.forEach(function (currentValue) {
  if (currentValue > 5) return currentValue;
});
console.log(a);   // 结果:undefined

《2》map()(与forEach方法类似)
返回创建一个新数组,新数组的每个元素都是回调函数的返回值

arr.map(function(currentValue,index,arr));
var arr = [3, 5, 6, 2];
var a = arr.map(function (currentValue) {
  if (currentValue > 5) return currentValue;
});
console.log(a);   // 结果:[undefined, undefined, 6, undefined]

《3》filter()(主要用于筛选数组)
返回创建一个新数组

arr.filter(function(currentValue,index,arr));
var arr = [3, 5, 6, 2];
var a = arr.filter(function (currentValue) {
  if (currentValue > 5) return currentValue;
});
console.log(a); // 结果:[6]

《4》some()(检测数组中是否存在满足条件的元素)
查找到第一个符合要求的元素就终止程序,返回值为true

arr.some(function(currentValue,index,arr));
var arr = [3, 5, 6, 2];
var a = arr.some(function (currentValue) {
  if (currentValue > 5) return currentValue;
});
console.log(a); // 结果:true

《5》every()(检测数组中的所有元素是否满足条件)
查找到一个不符合要求的元素就终止程序,返回值为false

arr.every(function(currentValue,index,arr));
var arr = [3, 5, 6, 2];
var a = arr.every(function (currentValue) {
  if (currentValue > 5) {
    return currentValue;
  }
});
console.log(a); // 结果:false

《6》forEach和some的区别
如果查找数组中唯一的元素,使用some()效率更高

var arr = ["red", "green", "blue", "pink"];
//   1. forEach迭代 遍历
arr.forEach(function (value) {
  if (value == "green") {
    console.log("找到了该元素");
    return true; // 在forEach 里面 return 不会终止迭代
  }
  console.log(11);
});
// 结果: 11,找到该元素,11,11

// 2.some遍历
arr.some(function (value) {
  if (value == "green") {
    console.log("找到了该元素");
    return true; //  在some里面遇到return true就是终止遍历,迭代效率更高
  }
  console.log(11);
});
//   结果:11,找到了该元素

//   3.filter遍历
arr.filter(function (value) {
  if (value == "green") {
    console.log("找到了该元素");
    return true; //  // filter 里面 return 不会终止迭代
  }
  console.log(11);
});
//   结果:11,找了该元素,11,11

2、字符串方法

《1》trim()(删除字符串两端空白字符串)
返回一个新数组

str.trim();

3、对象方法

《1》Object.keys()(类似于for…in)
返回一个由属性名组成的数组

Object.keys(obj)

《2》Object.defineProperty()(定义新属性或修改原有属性)

Object.defineProperty(obj,prop,descriptor);

点击链接查看:Object.defineProperty()方法的详细具体使用

四、小案例(查询商品案例)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <style>
      input {
        width: 40px;
      }
      table {
        text-align: center;
        border-spacing: 0;
        border-top: 1px solid #ccc;
        border-left: 1px solid #ccc;
      }
      td {
        width: 120px;
        border-bottom: 1px solid #ccc;
        border-right: 1px solid #ccc;
      }
    </style>
  </head>

  <body>
    <div>
      <p>
        按照价格查询:<input type="text" class="min" /> -
        <input type="text" class="max" />
        <button class="btnNum">搜索</button>
        按照名称查询:<input type="text" class="nameSearch" />
        <button class="btnName">搜索</button>
      </p>
      <table>
        <thead>
          <tr>
            <td>id</td>
            <td>产品名称</td>
            <td>价格</td>
          </tr>
        </thead>
        <tbody>
          <!-- <tr>
            <td>1</td>
            <td>小米</td>
            <td>3999</td>
          </tr>
          <tr>
            <td>2</td>
            <td>oppo</td>
            <td>999</td>
          </tr>
          <tr>
            <td>3</td>
            <td>荣耀</td>
            <td>1299</td>
          </tr>
          <tr>
            <td>4</td>
            <td>华为</td>
            <td>2599</td>
          </tr> -->
        </tbody>
      </table>
    </div>
  </body>
  <script>
    var data = [
      {
        id: 1,
        pname: "小米",
        price: 3999,
      },
      {
        id: 2,
        pname: "oppo",
        price: 999,
      },
      {
        id: 3,
        pname: "荣耀",
        price: 1299,
      },
      {
        id: 4,
        pname: "华为",
        price: 1999,
      },
    ];
    var tbody = document.querySelector("tbody");
    var min = document.querySelector(".min");
    var max = document.querySelector(".max");
    var nameSearch = document.querySelector(".nameSearch");
    var btnNum = document.querySelector(".btnNum");
    var btnName = document.querySelector(".btnName");
    var tbody = document.querySelector("tbody");
    setData(data);

    function setData(data) {
      tbody.innerHTML = "";
      data.forEach(function (cValue, index, data) {
        var tr = `<tr><td>${cValue.id}</td><td>${cValue.pname}</td><td>${cValue.price}</td></tr>`;
        tbody.insertAdjacentHTML("beforeend", tr);
      });
    }

    btnNum.onclick = function () {
      var newData = data.filter(function (value, index, data) {
        return value.price >= min.value && value.price <= max.value;
      });
      setData(newData);
    };

    // 使用some方法
    btnName.onclick = function () {
      var newData = [];
      data.some(function (value, index, data) {
        if (nameSearch.value == value.pname) {
          newData.push(value);
          return true;
        }
      });
      setData(newData);
    };
  </script>

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值