面向对象
模块化
模块依赖于 <script> 标签中的 type="module"
`导出`
1.export default 默认导出(仅导出一个文件)
2.export 导出指定的类名或者方法名 as起别名
import A,{obj,arr as arr1,abc} from "./js/A.js"
3. 没有export 文件没有导出任何内容的js,可以直接使用import加载进来就会执行
import "./js/Main.js";
`导入`
1.import fn from "./js/a.js" fn()
// 在一个js文件中 只能只有一个默认导出
//所以从"./js/a.js" 文件中导入 fn 这个默认模块 fn是自己起的名字,和模块中的名称可以不同
import lyf from "./js/a.js" lyf() 与上面的作用一样
2.import {obj,arr,fn} from "./js/b.js"
起别名(防止重名) // 从"./js/b.js"中导入obj,arr,fn1
import { obj, arr as arr1,fn1} from "./js/b.js"
整体起别名 // 从"./js/b.js"中导入obj,arr,fn1
import * as o from "./js/b.js";
或 import o from "./js/c.js"; 本质与1方式一样
3.import fn,{obj,arr} from "./js/d.js";
既有默认导出模块,又有多个导出模块
导入时,需要将默认导出模块写在前面,后面使用,再导出多个模块
import "./js/a.js"; //这样导入的js,所有的变量和函数都是私有的,全局是不能调用
a.js
function fn() {
console.log("aaa")
}
export default fn;
b.js
// 这个js要导出多个模块 每个模块的名字必须要起名
export var obj = { a: 1, b: 2 };
export var arr = [1, 2, 3];
export function fn1() {
console.log("bbb")
}
c.js
var obj={a:1,b:2};
var arr=[1,2,3];
function fn1(){
console.log("bbb");
}
export default {obj,arr,fn1}; //这里的本质与a.js一样
面向对象
基础知识点
实例化 通过new构造函数的方式产生
1.类(抽象对于某一群体共性,方法总的集合):class创造的类型
class 类名(第一个字母大写)
2.基类:所有类的最基础类别 js的基类是Object
3.父类和子类:(B类 继承 A类) (C类 继承 B类)
A类和B类 就是C类的父类 B类和C就是A类的子类
A类是B类的父类 B类是C类的父类
4.超类:子类的父类叫超类 A类就是B类的超类 B类就是C类的超类
5.实例化对象:通过new构造函数的方式产生这个类别的对象
new 类名()
重要知识点
`属性`:
1.静态属性:(只属于类 不属于个体)
static 开头
静态属性中this指向当前类
只能被类本身调用,实例化对象无法调用
2.实例化属性:
this指向实例化对象 即会调用编程,this就指向谁
`方法`:
1.静态方法:
在静态方法中无法直接调用实例化方法和实例化属性
只能被类本身调用
在静态方法中this指向当前类
调用静态方法: 类名.方法()
2.实例化方法:
实例化方法中this指向实例化对象
调用实例化方法: this.方法()
`构造函数`:
constructor(a,b){
console.log("aa")
构造函数里面的this指向实例化对象
}
1) js中所有类的构造函数都叫做constructor 但是constructor指向类名
2) 构造函数中不能使用return,并且只有一个构造函数
3) 在js中是没有私有、受保护的概念,所以构造函数或者其他地方的属性方法都是暴漏在外,公有的属性和方法
`extends继承`
1.可以将被继承的类的所有实例化属性和方法全部都继承给新的类
2.静态方法和属性也会继承,修改父类的静态属性,子类的静态属性也会改变
3.继承一个类之后,必须在构造函数中最上面写入super()
3.1 super:超类的构造函数,执行超类的构造函数
3.2 原因:从当前函数中传递进去,通过super传递到父类的构造方法,重新赋值
4.重写父类的方法会覆盖父类中原本的方法
5.执行父类方法的同时 执行自己重写的方法
class 女性 extends 人类 {
化妆品=10;
constructor(name,age){
// super就是人类的构造函数constructor 继承时,必须在构造函数的第一句使用super调用其超类的构造函数
super(name,age,"女")
}
生孩子(){
console.log(this.姓名+"生孩子")
}
// 覆盖超类的当前方法 override
走路(){
// console.log("穿高跟鞋");
// }
走路(){
console.log("穿高跟鞋");
//有super就是执行超类的走路方法
super.走路();
}
}
补充知识点
判断一个对象的构造类型 instanceof
A->B->C
c1=new C();
c1 instanceof C 判断c1是不是C的类 实例化对象
c1 instanceof B 判断c1是不是A和B的子类实例化对象
c1 instanceof A
案例 小球移动
1.过程式编程
var div = document.querySelector(".div1>div");
var x = 0;
var y = 0;
//设置小球在 x y轴上的移动速率
var speedX = 10;
var speedY = 10;
setInterval(function () {
//x y是小球每次移动到的位置
x += speedX;
y += speedY;
// console.log(x,"x")
// console.log(y,"y")
//设置小球的范围
//当小球要超出范围时 就让小球往反方向走
if (y <= 0 || y > 450) speedY = - speedY;
if (x <= 0 || x >= 1150) speedX = -speedX;
//设置小球的水平和纵向样式
//x y 一起就是小球移动的距离
div.style.left = x + "px";
div.style.top = y + "px"
}, 16)
2.函数式编程
var divs
init();
function init() {
//获取到所有的div
divs = document.querySelectorAll(".div1>div");
//遍历每一个div 给每一个dive设置数据
for (var i = 0; i < divs.length; i++) {
//(小球开始运动的位置)让小球开始运动的位置随机
//不让小球在同一位置开始移动
divs[i].x = Math.random() * 1150;
divs[i].y = Math.random() * 450;
//让小球开始移动的方向随机
//若是给固定值 所有小球会向同一方向移动
divs[i].speedX = ~~(Math.random() * 7 - 3) || 1;
divs[i].speedY = ~~(Math.random() * 7 - 3) || 1;
}
setInterval(animation, 16);
}
function animation() {
for (var i = 0; i < divs.length; i++) {
//x y是小球每次移动到的位置
divs[i].x += divs[i].speedX;
divs[i].y += divs[i].speedY;
//设置小球的范围
//当小球要超出范围时 就让小球往反方向走
if (divs[i].y <= 0 || divs[i].y > 450) divs[i].speedY = -divs[i].speedY;
if (divs[i].x <= 0 || divs[i].x > 1150) divs[i].speedX = -divs[i].speedX;
//设置当前小球的水平和纵向样式
divs[i].style.left = divs[i].x + "px";
divs[i].style.top = divs[i].y + "px"
}
}
3.伪面向对象编程
class Ball {
x = 0;
y = 0;
speedX = ~~(Math.random() * 7) - 3 || 1;
speedY = ~~(Math.random() * 7) - 3 || 1;
elem;
constructor() {
//构造函数中this指向实例化对象
this.elem = document.createElement("div");
var div1 = document.querySelector(".div1")
//把获取的当前div添加到div1中
div1.appendChild(this.elem);
//获取父元素的getBoundingClientRect方法
//用来获取父元素的大小、位置信息
var rect = div1.getBoundingClientRect();
//让小球开始运动的位置随机
//rect.width,rect.height 分别是div1的宽和高
//this.elem.offsetWidth,this.elem.offsetHeight 分别是当前div自身的宽高
this.x = Math.random() * (rect.width - this.elem.offsetWidth);
this.y = Math.random() * (rect.height - this.elem.offsetHeight);
//若使用setInterval的正常形式形成回调函数 会让其中的this指向window
//所以要使用箭头函数 会让this指向函数外的this 即该类
setInterval(() => this.animation(), 16);
}
animation() {
//x y是小球每次移动到的位置
this.x += this.speedX;
this.y += this.speedY;
var rect = document.querySelector(".div1").getBoundingClientRect();
if (this.x < 0 || this.x > rect.width - this.elem.offsetWidth) this.speedX = -this.speedX;
if (this.y < 0 || this.y > rect.height - this.elem.offsetHeight) this.speedY = -this.speedY;
this.elem.style.left = this.x + "px";
this.elem.style.top = this.y + "px"
}
}
for (var i = 0; i < 150; i++) {
var b = new Ball()
}
4.面向对象编程
class Ball {
x = 0;
y = 0;
speedX = ~~(Math.random() * 7) - 3 || 1;
speedY = ~~(Math.random() * 7) - 3 || 1;
elem;
rect;
//设置一个初始值 小div的宽高
w = 50;
//设置一个set集合 让其中的值是唯一的
static list = new Set();
constructor() {
this.elem = document.createElement("div");
//在内部设置div的样式
Object.assign(this.elem.style, {
width: this.w + "px",
height: this.w + "px",
backgroundColor: "red",
position: "absolute",
borderRadius: "50%",
});
//把创建好的实例化对象放入集合中
//即放入的是每一个创建好的div
Ball.list.add(this)
}
appendTo(parent) {
//判断传入的参数是不是字符串 若是字符串则获取
if (typeof parent === "string") parent = document.querySelector(parent);
//判断parent是否存在 且是否为HTML标签
if (parent && parent instanceof HTMLElement) {
//把div添加到div1中
parent.appendChild(this.elem);
//this.rect dom属性方式设置一个变量rect
this.rect = parent.getBoundingClientRect();
//让小球开始运动的位置随机
this.x = Math.random() * (this.rect.width - this.w);
this.y = Math.random() * (this.rect.height - this.w)
//设置当前小球所在位置
Object.assign(this.elem.style, {
left: this.x + "px",
top: this.y + "px",
})
}
}
update() {
//x y是小球每次移动到的位置
this.x += this.speedX;
this.y += this.speedY;
//如果父元素没有大小,位置就跳出
if (!this.rect) return;
if (this.x < 0 || this.x > this.rect.width - this.w) this.speedX = -this.speedX;
if (this.y < 0 || this.y > this.rect.height - this.w) this.speedY = -this.speedY;
this.elem.style.left = this.x + "px";
this.elem.style.top = this.y + "px";
}
static update() {
Ball.list.forEach(item => {
//item 是每一个创建好的div
//遍历 让每一个div调用实例化的update方法
item.update();
})
}
}
//小球数量
for (var i = 0; i < 30; i++) {
//向appendTo函数中传入值
//实例化的对象调用appendTo方法传入parent需要的值
new Ball().appendTo(".div1")
}
setInterval(function () {
//调用静态update方法
Ball.update();
}, 16)
多选框
<div class="div1"></div>
<script type="module">
import CheckBox from "./js/CheckBox1.js";
var arr = ["游泳", "跑步", "唱歌", "跳舞"];
arr.forEach(item => {
//实例化对象
//把数组组中的值传入到 构造函数中 即把获取的值给label
var ck = new CheckBox(item);
ck.appendTo(".div1");
})
</script>
export default class CheckBox {
//需要的文本
label;
//需要的块
elem;
_checked = false;
constructor(label) {
//this指实例化的对象
this.label = label;
//创建块元素
this.elem = document.createElement("div")
//把文本和需要设置背景图的div放入块中
this.elem.innerHTML = `<div class='icon'></div>
<span class='title'>${label}</span>
`
//设置 背景图样式
this.setStyle();
//若是使用普通函数 会形成回调函数 导致this指向window
//要让this指向实例化对象 所以使用箭头函数 让函数中this指向跟随父元素
//设置 点击侦听事件
this.elem.addEventListener("click", () => this.clickHandler());
//设置 鼠标按下侦听事件
this.elem.addEventListener("mousedown", e => e.preventDefault())
}
//实例化方法
appendTo(parent) {
//判断传入的参数是不是字符串
if (typeof parent === "string")
//是字符串 则通过字符串的内容获取
parent = document.querySelector(parent)
//判断parent是否为真且是否是HTML元素
if (parent && parent instanceof HTMLElement) parent.appendChild(this.elem)
}
//点击事件(开关的作用)
clickHandler() {
//_checked为false 点击后为true
//把点击后的值做为参数 放入setChecked函数
this.setChecked(!this._checked);
//让当前实例化的对象 调用dispatch方法
this.dispatch();
}
//设置点击切换时的样式
setChecked(value) {
//把value赋值给this._checked
//为true时被选中 为false时不被选中
this._checked = value;
this.setCheckedStyle();
}
//该函数作用:获取点击时背景图切换的样式
setCheckedStyle() {
Object.assign(this.elem.firstElementChild.style, {
// 若this._checked 为真 则显示被选中的图片样式
//否则显示未被选中的图片样式
backgroundPositionX: this._checked ? "-128px" : "-238px",
backgroundPositionY: this._checked ? "-126px" : "-37px"
})
}
dispatch() {
//创建一个事件对象
var evt = new Event("change");
evt.checked = this._checked;
evt.ck = this;
//将该事件 抛发给doucment(触发该事件)
document.dispatchEvent(evt);
}
//设置 初始时的样式
setStyle() {
this.elem.style.position = "relative";
Object.assign(this.elem.firstElementChild.style, {
width: "14px",
height: "14px",
backgroundImage: "url(./img/new_icon.png)",
backgroundPositionX: "-238px",
backgroundPositionY: "-37px",
float: "left",
marginRight: "5px",
position: "relative",
top: "3px"
})
}
}
多选框(继承版)
<div class="div1"></div>
<script type="module">
import CheckBox from "./js/CheckBox.js";
var arr = ["游泳", "跑步", "唱歌", "跳舞"];
arr.forEach(item => {
var ck = new CheckBox(item);
ck.appendTo(".div1");
//设置侦听事件 观察实例化对象的变化
ck.addEventListener("change", changeHandler)
})
function changeHandler(e) {
console.log(e);
}
</script>
//父类
export default class Component extends EventTarget {
elem;
constructor(type, className) {
super();
//从子类 接收type 此处为"div"
this.elem = document.createElement(type);
//如果有类名 则设置类名
if (className) this.elem.className = className;
}
appendTo(parent) {
if (typeof parent === "string") parent = document.querySelector(parent);
if (parent && parent instanceof HTMLElement) parent.appendChild(this.elem);
return parent;
}
}
//子类
import Component from "./Component.js";
export default class CheckBox extends Component {
label;
_checked = false;
constructor(label) {
super("div");
this.label = label;
//this.elem 在父类中创建的div
this.elem.innerHTML = `
<div class='icon'></div>
<span class='title'>${label}</span>
`
// console.log(this.elem)
this.setStyle();
this.elem.addEventListener("click", () => this.clickHandler());
this.elem.addEventListener("mousedown", e => e.preventDefault())
}
clickHandler() {
//开始时this._checked为false
//点击之后this._checked为true 并让它作为setChecked函数的参数
this.setChecked(!this._checked);
//点击时 调用dispatch函数
this.dispatch();
}
setChecked(value) {
//第一次点击后 value 从上面获取参数为true 并重新赋值给this._checked
this._checked = value;
//调用setCheckedStyle 函数
this.setCheckedStyle();
}
//设置点击‘按钮’的样式
setCheckedStyle() {
//this.elem.firstElementChild 在div中添加的类名为 icon的div
Object.assign(this.elem.firstElementChild.style, {
//第一次点击后this._checked 为true 显示为被选中的样式
//当再次点击后this._checked 为false 显示为未被选中的样式
backgroundPositionX: this._checked ? "-128px" : "-238px",
backgroundPositionY: this._checked ? "-126px" : "-37px"
})
}
//创建事件函数 用于外部观察实例化对象的变化
dispatch() {
var evt = new Event("change");
evt.checked = this._checked;
this.dispatchEvent(evt);
}
setStyle() {
//给创建的div加定位
this.elem.style.position = "relative";
//设置类名为 icon的div的初始样式
Object.assign(this.elem.firstElementChild.style, {
width: "14px",
height: "14px",
backgroundImage: "url(./img/new_icon.png)",
backgroundPositionX: "-238px",
backgroundPositionY: "-37px",
float: "left",
marginRight: "5px",
position: "relative",
top: "3px"
})
}
}
单选框
<div class="div1"></div>
<script type="module">
import Radio from "./js/Radio.js";
var arr = ["男", "女"];
arr.forEach(item => {
//item -->label
//"sex" -->name
var ck = new Radio(item, "sex");
ck.appendTo(".div1");
ck.addEventListener("change", changeHandler);
})
function changeHandler(e) {
console.log(e)
}
var arr = ["0-3000", "3001-6000", "6001-10000", "10001-20000"];
arr.forEach(item => {
//item -->label
//"wage" -->name
var ck = new Radio(item, "wage");
ck.appendTo(".div1");
ck.addEventListener("change", changeHandler);
})
</script>
//CheckBox 就是上面的CheckBox
import CheckBox from "./CheckBox.js";
export default class Radio extends CheckBox {
name;
static list = new Map();
constructor(lable, name) {
super(lable)
this.name = name;
//判断list中有没有键name
if (!Radio.list.has(name)) {
//没有则把name设置为键,让当前的实例化对象为值
Radio.list.set(name, this);
//调用父类中的setChecked,并传入参数true
this.setChecked(true);
}
}
//设置 单选
//当一个被选中时 让另外一个不被选中
clickHandler() {
//如果list中有当前实例化对象(说明已经被选中了)
if (Radio.list.has(this.name)) {
//让其不被选中
Radio.list.get(this.name).setChecked(false)
}
//如果此时该实例化对象没有被选中
if (!this._checked) {
//让其被选中
this.setChecked(true);
//并且把它加入到集合中
Radio.list.set(this.name, this);
}
}
//获取点击时背景图切换的样式
setCheckedStyle() {
Object.assign(this.elem.firstElementChild.style, {
//在精灵图中 选中和未选中样式在同一行 所以不需要使用Y轴
backgroundPositionX: this._checked ? "-175px" : "-195px"
})
}
//设置初始样式
setStyle() {
this.elem.style.position = "relative";
Object.assign(this.elem.firstElementChild.style, {
width: "18px",
height: "18px",
backgroundImage: "url(./img/new_icon.png)",
backgroundPositionX: "-195px",
backgroundPositionY: "-104px",
float: "left",
marginRight: "5px",
position: "relative",
top: "1px"
})
}
}