目录
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>