目录
ES5构造函数
- 面向对象是一种思想 —考虑的是怎么去调用它,不需要关注内部的实现
- 面向过程也是一种思想
面向对象分为两个层次:
-
使用对象 – 属性和方法
- Array
push() - String
- Math
- Number
- Date
- Reg
- Window
- Document
- location
- Array
-
构造对象 --系统对象没有提供的方法,需要自己构造
- 对象的构成:属性和方法
-
面向对象的三大特性:
- 抽象: 抽:抽离出核心的属性构造对象
- 封装: 封装:封装一个具体特定功能的代码模块—复用强
- 继承: 继承:就是继承父亲的优秀基因(方法等),然后直接在提升自己的能力(也就是你的就是我的,我的还是我的)
- 多态: 组件化开发(并列关系)
eg:就是一个办公系统
姓名 性别 年龄 地址 工号 等等 这些可以可以抽离到一个对象身上,再进行eg:多态是什么?
就是有着公共的代码,但是也是有着独一无二的功能,比如选项卡- 功能1: 普通版本
- 功能2:拖拽
- 功能3:可以增加栏目
使用对象
- 变量与属性,函数与方法的区别
属性的本质是变量,只不过这个变量是挂载在一个对象身上,我们为了区分它们,也就称之为对象的属性
方法的本质是函数,只不过这个函数是挂载在一个对象身上,我们为了区分它们,也就称之为对象的方法
let num = 90; //变量
let arr = [1, 3, 5] //数组
arr.num1 = 20; //属性:num1本质是一个变量,但是挂载在arr对象身上,也就称之为属性
function a() { //函数
console.log(a);
}
a()
arr.bb = function () { //方法:本质上是一个函数,但是挂载在arr对象身上,我们称之为方法
console.log(bb);
}
arr.bb()
arr.push(); //调用方法
注意点:自己构造一个对象的方法的时候,不要挂载在原本对象身上,避免方法的重名,这需要使用一个空对象,来挂载
也就是 let obj = new Object();
构造对象
- 工厂模式构造函数
- 构造函数结合原型对象实现方法的共享!
注意点:原型对象之内定义的是公共的属性和方法!
new 的作用:
- 1:构造函数都有着自己的原型对象
- 2:构造函数的原型对象有一个属性:constructor属性,它指回自己的构造函数
- 3:new 一个实例,也就是创建一个实例对象,可以同时创建多个不同的实例对象
- 4:实例对象和原型之间存在一条连接,也就是原型链
原型用法: 构造函数(对象).原型.方法名 = function(){}
- 每一个构造函数下都有一个原型
- 每一个对象和自己的原型都有着一条连接,这个链接就是原型链!
注意点:
- 每一个对象(构造函数实例化的对象)都会从原型对象中“继承”属性
- proto :这是每个对象(除null外)都会有的属性,叫做__proto__,这个属性会指向该对象的原型
构造函数结合原型对象实现方法的共享
/*
需求:
需要创建两个对象,小黑,小白,年龄分别为20,30,居住地为傻屌之村,
小黑喜欢吃辣,小白喜欢吃素,小黑是乡村歌手,小白是歌王
按照上面要求:
+ 01:需要定义一个构造函数,里面还定义者name和age(不同的姓名和年龄,定义在构造函数内)
+ 02:而居住地方是同一个地方,也就是公共的属性,那么需要定义在原型对象内
+ 03:而小黑和小白都有着公共的eat和song方法
*/
function Peasonl(stuName, stuAge) {
this.name = stuName;
this.age = stuAge;
}
Peasonl.prototype.adr = "傻屌之村"
Peasonl.prototype.eat = function (food) {
console.log(this.name + food);
}
Peasonl.prototype.song = function (song) {
console.log(this.name + song);
}
let xiaohei = new Peasonl("小黑", 20);
let xiaobai = new Peasonl("小白", 30);
console.log(xiaohei); //Peasonl {name: "小黑", age: 20}
xiaohei.eat("喜欢吃辣"); //小黑喜欢吃辣
xiaohei.song("-乡村歌手") //小黑-乡村歌手
console.log(xiaohei.name + xiaohei.adr); //小白-傻屌之村
xiaobai.eat("喜欢吃素") //小白喜欢吃素
xiaobai.song("-歌王"); //小白-歌王
console.log(xiaobai.name + xiaobai.adr);
/*
构造函数:属性和方法
* 属性:定义在构造函数之内
* 方法:定义在原型对象之内 --- 这样可以实现,每一实例对象都共享这个方法!
*/
原型链
原型链的查找机制:
- 1:读取实例对象,在实例对象身上查找是否含有该属性,若是有就是直接使用,否者到2
- 2:没有找到的话,就去实例对象的原型对象中查找,有就返回这个属性的值,否则到3
- 3:没有找到haul,就到原型对象中的原型对象查找,若是没有,直接查找到Object这个对象(顶级对象)
- 4:若是Object对象还是没有查找到这个属性,就返回undefined
prototype和__proto__的区别:
prototype:原型对象
- 每一个构造函数都有着一个原型对象
- 原型对象内有着一个属性:constructor —它指回构造函数
proto:对象的原型
- 每一个实例对象都有着一个对象的原型
- 这个对象的原型指向原型对象
function Person(name, age) {
this.name = name; //在构造函数中定义的属性---私有的属性
this.age = age;
}
Person.prototype.adr = "小山村"; //在原型对象上定义的属性---共享属性
Person.prototype.say = function (say0) { //在原型对象上定义的方法---共享方法
console.log(this.name + say0);
}
let p1 = new Person("小黑");
let p2 = new Person("小白");
console.log(Person.prototype === p1.__proto__);
//true 证明两者都指向同一个原型对象,也就是说他们是等价的
class
- 概念:class 关键字表明类的声明;
- 作用 :非常明确 : 面向对象编程的工具;
- 组成:定义一个类,类里面是由构造函数 和 原型方法!
- 构造函数:一般用于存储数据
- 原型方法:用于存储功能的!
class Factory {
// 构造函数变成了统一的名称 : constructor
constructor() {
this.a = "这是一条数据";
}
// 定义任意名称的构造函数方法名;
init() {
console.log("这是一个功能")
}
}
// 调用 :
// class 的调用 和 构造函数 + 原型的调用是一致的;
var fac = new Factory()
fac.init();
需求:实现一个简单的拖拽功能
- 01:文档(也就是光标)按下的时候,获取到初始值的位置,
- 02: 文档移动的时候,盒子粘连在鼠标之上,跟着移动,也就是需要 把当前的值 => 赋值为盒子 (可视区 - 最初),使得盒子向鼠标光标居中(左上走)
- 03:文档抬起的时候,就取消拖拽效果!
class Drag {
constructor(selector) {
this.ele = document.querySelector(selector)
this.bindEvent();
}
bindEvent() {
let self = this;
//01:鼠标按下的时候,获取到当前的位置,赋值为盒子,使得盒子向鼠标光标居中(左上走)
document.onmousedown = function (evt) {
let e = evt || event;
let {
offsetX: x,
offsetY: y
} = e;
//02: 鼠标移动的时候,盒子粘连在鼠标之上,跟着移动,也就是需要 把当前的值 => 赋值为盒子 (可视区 - 最初),使得盒子向鼠标光标居中(左上走)
document.onmousemove = function (evt) {
let e = evt || event;
self.eleMove(e.clientX - x, e.clientY - y);
}
}
//03:鼠标抬起的时候,就取消拖拽效果!
document.onmouseup = function () {
document.onmousemove = null;
}
}
eleMove(x, y) {
this.ele.style.left = x + "px";
this.ele.style.top = y + "px";
}
}
new Drag("#box");
实现一个可继承父类
//需求:
// * 01:文档(也就是光标)按下的时候,获取到初始值的位置,
// * 02: 文档移动的时候,盒子粘连在鼠标之上,跟着移动,也就是需要 把当前的值 => 赋值为盒子 (可视区 - 最初),使得盒子向鼠标光标居中(左上走)
// * 03:文档抬起的时候,就取消拖拽效果!
// * 04:实现一个继承的拖拽效果,就是边界判断! 在子类之中,覆盖父类的元素移动方法
// * 05:继承的话,调用的时候是调用子类的!
class Drag {
constructor(selector) {
this.ele = document.querySelector(selector);
this.bindEvent()
}
bindEvent() {
let self = this;
// 01:文档(也就是光标)按下的时候,获取到初始值的位置,
document.onmousedown = function (evt) {
let e = evt || event;
let {
offsetX: x,
offsetY: y
} = e;
// 02: 文档移动的时候, 盒子粘连在鼠标之上, 跟着移动, 也就是需要 把当前的值 => 赋值为盒子(可视区 - 最初), 使得盒子向鼠标光标居中( 左上走) document.onmousemove =
document.onmousemove = function (evt) {
let e = evt || event;
//调用移动元素函数
self.eleMove(e.clientX - x, e.clientY - y);
}
}
// 03:文档抬起的时候,就取消拖拽效果!
document.onmouseup = function () {
document.onmousemove = null;
}
}
//元素移动
eleMove(x, y) {
this.ele.style.left = x + "px";
this.ele.style.top = y + "px";
}
}
//继承父元素中带有边界的!
class boundaryDrag extends Drag {
constructor(selector) {
console.log("带有边界");
super(selector)
}
eleMove(x, y) {
//最下值判断
x = x < 0 ? 0 : x;
y = y < 0 ? 0 : y;
//最大值的判断
this.ele.style.left = x + "px";
this.ele.style.top = y + "px";
}
}
let drg1 = new boundaryDrag("#box");
ES5的继承
- 概念:
- 01:构造函数中的属性拿出来
- 02:原型对象上的方法拿出来
- 03:继承构造数里面功能和调用;
ES5之中普通的继承
function Father() {
this.a = "你好";
this.b = "世界";
}
new Father()
function Son() {
var son_this = this;
Father.call(son_this)
}
var son = new Son();
console.log(son); //Son {a: "你好", b: "世界"}
//通过call的方法,在son里面调用父函数,然后把里面的this转接到son之上,于是也就继承了Father的属性
带有参数的继承
//一个son自带参数的继承
function Father(a, b) {
this.a = a
this.b = b;
}
new Father()
function Son(son_a, son_b) {
var son_this = this;
Father.call(son_this, son_a, son_b)
}
var son = new Son("我是", "你是")
console.log(son); //Son {a: "我是", b: "你是"}
原型方法的继承
//原型方法的继承
function Father() {}
Father.prototype.init = function () {
console.log("我是father上原型方法");
}
new Father();
function Son() {}
//遍历父构造函数的原型,然后把原型对象身上的方法,赋值给子函数!
for (var key in Father.prototype) {
console.log(Father.prototype[key]);
Son.prototype[key] = Father.prototype[key]
}
var son = new Son();
son.init() //我是father上原型方法
需求:既需要传递参数,也需要继承父构造函数的方法!
function Drag(selector) {
this.ele = document.querySelector(selector);
this.bindEvent()
}
Drag.prototype.bindEvent = function () {
let self = this;
// 01:文档(也就是光标)按下的时候,获取到初始值的位置,
document.onmousedown = function (evt) {
let e = evt || event;
let {
offsetX: x,
offsetY: y
} = e;
// 02: 文档移动的时候, 盒子粘连在鼠标之上, 跟着移动, 也就是需要 把当前的值 => 赋值为盒子(可视区 - 最初), 使得盒子向鼠标光标居中( 左上走) document.onmousemove =
document.onmousemove = function (evt) {
let e = evt || event;
//调用移动元素函数
self.eleMove(e.clientX - x, e.clientY - y);
}
}
// 03:文档抬起的时候,就取消拖拽效果!
document.onmouseup = function () {
document.onmousemove = null;
}
}
//元素移动
Drag.prototype.eleMove = function (x, y) {
this.ele.style.left = x + "px";
this.ele.style.top = y + "px";
}
function boundaryDrag(selector) {
var son_this = this;
Drag.call(son_this, selector)
}
//继承父元素中带有边界的!
for (var key in Drag.prototype) {
boundaryDrag.prototype[key] = Drag.prototype[key];
}
boundaryDrag.prototype.eleMove = function (x, y) {
//最下值判断
x = x < 0 ? 0 : x;
y = y < 0 ? 0 : y;
this.ele.style.left = x + "px";
this.ele.style.top = y + "px";
}
let drg1 = new boundaryDrag("#box");
原型链的继承
- 原理:就是把父元素的实例对象(指向的是父构造函数的原型对象) 赋值给Son的原型对象,这样的话,Son原型对象指向的是 父构造函数的原型对象 而原型对象存储这方法! 可用于继承!
function Father() {}
Father.prototype.init = function () {
console.log("我是父的初始化");
}
Father.prototype.show = function () {
console.log("我是父的显示");
}
function Son() {}
//把父元素的实例对象(指向的是父构造函数的原型对象) 赋值给Son的原型对象 这样的话 Son原型对象指向的是 父构造函数中的原型对象!
Son.prototype = new Father()
// console.log(Son); //Father{}
Son.prototype.show = function () {
console.log("我是子的显示");
}
var son = new Son();
son.init() //我是父的初始化
son.show() // 我是子的显示