闭包和继承
闭包
先不急着定义:
假设我们要做银行系统:
方案一:
var money =100;
money -=10;
console.log(money);
这样写有一个坏处:变量在全局是容易被覆盖的(全局污染);
改进:我们放在局部中
function fn(){
var money =100;
}
这种情况下我们消费不了(全局无法操作局部变量)。
改进:
function fn(){
var money = 100;
return money;
}
var m = fn();
console.log(m);//查询余额
查询和消费:
//消费和查询
function fn(handler,num){
var money =100;
if(handler=="消费"){
money -=num;
}
return money;
}
console.log(fn());
console.log(fn("消费",10));
console.log(fn());//消费完又变成100了
局部操作不行,每次查询都是最初始的值
改进:放在局部的局部
function fn(){
var money =100;
function fun(handler,num){
if(handler=="消费"){
num -=num;
}
return money;
}
return fun();
}
//查询余额
var m = fn();
console.log(m);
消费不了,调用不了局部函数(下面小改进,将局部返回值改为函数):
function fn(){
var money =100;
function fun(handler,num){
if(handler=="消费"){
num -=num;
}
return money;
}
return fun;
}
//改版后查询
var m =fn();
var yue = m();
console.log(yue);
//消费
m('消费',10);
console.log(yue);
//再查询
var yue = m();
console.log(yue);
90,没变成100;因为我们没有调用fn初始化money,一直调用fun
闭包:闭包就是能够读取其他函数内部变量的函数。
总结上面式子,得出闭包的形成条件:大函数返回了一个小函数,小函数使用了大函数的变量
闭包变量的特点:
//未闭包
function ax(){
var a =1;
a++;
return a;
}
console.log(ax());
console.log(ax());
console.log(ax());
console.log(ax());
//都是2,每次运行a都是1,并且a执行完就销毁
//闭包:
//闭包
function big(){
var a = 1;
function small(){
a++;
return a;
}
return small;
}
var f = big();
console.log(f());
console.log(f());
console.log(f());
console.log(f());
console.log(f());
//不断累加,a的空间没有销毁
闭包的特点:
- 变量生命周期变长了
- 内部的局部空间没有销毁(一直不销毁内存溢出)
- 变量私有化(保护变量不被全局污染)
- 全局可以访问并操作局部变量
闭包案例:星星评分
<!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>
<img src="./rank_3.gif" alt=""><img src="./rank_4.gif" alt="">
<img src="./rank_3.gif" alt=""><img src="./rank_4.gif" alt="">
<img src="./rank_3.gif" alt=""><img src="./rank_4.gif" alt="">
<img src="./rank_3.gif" alt=""><img src="./rank_4.gif" alt="">
<img src="./rank_3.gif" alt=""><img src="./rank_4.gif" alt="">
</body>
<script>
var imgs = document.querySelectorAll("img");
for (var i = 0; i < imgs.length; i++) {
imgs[i].onmouseover = fn(i); //第一层函数
//以前的写法
/* function (){imgs[i].οnmοuseοver=function(){console.log(i)}都是10
(function(i){
局部作用域中有i,和下面的闭包是一样的,变量不会被覆盖
imgs[i].onmouseover = function(){
console.log(i);
}
})
*/
}
function fn(i) {//相当于执行了很多次,每次都有自己的作用域,闭包的好处(i一直活着,不会像之前一样死掉)
return function () {//第二层函数,形成闭包
//console.log(i);
for (var j = 0; j <= i; j++) {
if (j % 2 == 0) {
imgs[j].src = "./rank_1.gif";
} else {
imgs[j].src = "./rank_2.gif";
}
}
for (var j = i+1; j < imgs.length; j++) {
if (j % 2 == 0) {
imgs[j].src = "./rank_3.gif";
} else {
imgs[j].src = "./rank_4.gif";
}
}
}
}
</script>
</html>
继承的概念
一个对象想要另一个对象的属性和方法,就继承另一个对象
俗称换爸爸。
function Animal(){
this.tui = "2条腿";
}
function Person(){
this.tedian="能思考";
}
var a = new Animal();
console.log(a);
var zhangsan = new Person();
console.log(zhangsan);
//希望张三属于动物,自动具有两条腿(张三拥有a的特点:属性)
console.log(zhangsan.tui);//undefined,对象里面没有就是undefined
//换原型(换爸爸,原型继承,给原型赋值)
zhangsan.__proto__ =a;
console.log(zhangsan);
console.log(zhangsan.tui);
原型继承
function ChengLong(){
this.money = '1000000000000';
}
function FanBingBing(){
this.meise ="美";
}
var cl = new ChengLong();
console.log(cl);
var fbb = new FanBingBing();
console.log(fbb);
//fbb想要cl的钱就需要认他做爸爸
fbb.__proto__ = cl;
原型继承:利用构造函数将原型改成想要的对象
FanBingBing.prototype=cl;
console.log(fbb);
再举个例子:
function Animal (name,age){
this.tui = '2条腿',
this.name = name;
this.age = age;
}
function Person(){
}
Perosn.prototype = new Animal();
var man = new Person();
console.log(man);
继承的缺点:无法动态的给父元素添加属性和方法
推荐的继承缺点链接:点击跳转
下面是引用上面链接的内容
- 引用值共享
因为使用的是同一个原型,只要一个修改了原型的值,
那么另一个引用原型的值也会随着更改
- 不能传参
Parent.prototype.grand = 'grand';
function Parent (name) {
this.name = name || 'parent';
this.color = ['red', 'orange', 'yellow'];
}
function Child () {
}
Child.prototype = new Parent();
var child1 = new Child('child1');
var child2 = new Child('child2');
上面传过去的child1,child2全部没用,都输出parent
上下文调用模式继承(借用函数继承)
function Animal (name,age){
this.tui = '2条腿',
this.name = name;
this.age = age;
}
function Person(name,age){
//animal相当于调用了,调用就会执行里面代码
/*
this.tui="两条腿",
this.name = name,
this.age = age,
中this被call给改了,改成当前构造函数中的this,man
*/
Animal.call(this,name,age);
}
Animal.prototype.eat =function(){
console.log(this,name,age);
}
//上下文调用模式解决原型继承的缺点
Person.prototype = new Animal();
var man = new Person("张三",12);
console.log(man);
console.log(man.eat())
解决了原型继承的引用值和不能传参问题
缺点:
- 继承不了原型的原型的方法
- 多执行了一次call
参考链接:点击参考好文链接,里面解释非常详细
解决方案:混合继承(原型继承+上下文调用模式继承)
function Animal(name,age){
this.tui = "2条腿",
this.name = name;
this.age = age
}
Animal.prototype.eat=function(){
console.log("动物方法:看到人看不到的东西");
}
function Person(name,age){
//再上下文调用模式继承
Animal.apply(this,[name,age]);
}
//先原型继承
Person.prototype = new Animal();
var p = new Person("张三",12);
console.log(p.eat())
还剩下几种没有介绍感兴趣点击链接查看:点击此处跳转
es6的class语法
类:抽象的对象
对象:万物皆对象
在初中还是高中生物里面说过:什么什么属于什么科,属之类的话(记不太清了)
拿豌豆举例子,豌豆是对象,属于豌豆属(类)。豌豆是具体的东西,但是豌豆属是将豌豆的特征集合起来的一个定义。你可以根据豌豆属知道豌豆大致具备什么信息。
老例子了:人是对象,但人类不是具体的事物,是人的概念(从人身上抽象出来的)
代码中:
对象 = new 构造函数;
构造函数又叫做类,es6中专门提供了一个语法,专门用来定义类,叫做class
//之前的面向对象写法
function Person() = {
this.name="张三"
}
var p = new Person();//new 操作叫做实例化
console.log(p);
//es6的面向对象
class Person {//定义好的类是专门用来实例化对象的
//如果要设置这个类中出来的属性----定义一个特殊固定方法:constructor(){}
constructor(name) {
//new 的时候就是在执行这个constructor
this.name =name;
//所有属性必须写在这个方法中
}
//定义方法
eat(){
console.log("吃月饼");
}
}
var p = new Person("张三");
console.log(p);
p.eat();
以后面向对象的格式:
class 类{
constructor(){
this.属性名=值
}
方法名(){
}
}
es6面向对象版轮播图
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>面向对象版轮播图</title>
</head>
<style>
ul,
ol {
list-style: none;
}
.box {
width: 500px;
height: 300px;
margin-left: 20px;
position: relative;
}
* {
margin: 0;
padding: 0;
}
ul {
width: 500px;
height: 300px;
border: 1px solid red;
}
ul li {
display: none;
}
ul li.active {
display: block;
}
ul li a img {
width: 500px;
height: 300px;
}
a {
text-decoration: none;
color: black;
font-size: 30px;
}
ol {
width: 200px;
height: 26px;
position: absolute;
background-color: green;
border-radius: 26px;
left: calc(50% - 100px);
bottom: 20px;
display: flex;
justify-content: space-evenly;
align-items: center;
}
ol li {
background-color: gray;
width: 26px;
height: 26px;
border-radius: 26px;
}
ol li.active {
background-color: red;
}
.left,
.right {
position: absolute;
left: 0;
top: calc(50% - 13px);
height: 50px;
width: 30px;
background-color: rgba(0, 0, 0, .5);
text-align: center;
line-height: 50px;
}
.right {
left: calc(100% - 28px);
}
</style>
<body>
<div class="box">
<ul>
<li class="active"><a href="#"><img
src="https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2493695704,2540399818&fm=26&gp=0.jpg"
alt=""></a></li>
<li><a href=""><img
src="https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=1242578728,2152118644&fm=26&gp=0.jpg"
alt=""></a></li>
<li><a href=""><img
src="https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3213912571,2486886761&fm=26&gp=0.jpg"
alt=""></a></li>
</ul>
<ol>
<li class="active"></li>
<li></li>
<li></li>
</ol>
<a href="javascript:;" class="left"><</a>
<a href="javascript:;" class="right">></a>
</div>
</body>
<script>
class Carousel {
constructor(classname) {
//在这里写的东西其实就是之前在构造函数中写的东西
this.box = document.querySelector("." + classname);
this.ulis = this.box.querySelectorAll("ul li ");
this.olis = this.box.querySelectorAll("ol li ");
this.leftBtn = this.box.querySelector("a.left");
this.rightBtn = this.box.querySelector("a.right");
this.index = 0;
this.timerId = null;
}
init(){
this.rightBtn.onclick = function () {
this.rightMove();
};
this.rightBtn.onclick = () => {
this.rightMove();
};
this.leftBtn.onclick = () => {
this.leftMove();
};
for (let i = 0; i < this.olis.length; i++) {
this.olis[i].onclick = () => {
this.dotMove(i);
}
};
this.autoMove();
this.box.onmouseover=()=>{
clearInterval(this.timerId);
};
this.box.onmouseout=()=>{
this.autoMove();
};
}
autoMove() {
this.timerId = setInterval(() => {
this.rightMove()
}, 2000)
};
rightMove() {
this.index++;
if (this.index == this.ulis.length) {
this.index = 0;
}
this.move();
}
leftMove() {
this.index--;
if (this.index < 0) {
this.index = this.ulis.length - 1;
}
this.move();
}
dotMove(i) {
this.index = i;
this.move();
}
move() {
for (var i = 0; i < this.ulis.length; i++) {
this.ulis[i].style.display = "none";
this.olis[i].style.backgroundColor = "gray";
}
this.ulis[this.index].style.display = "block";
this.olis[this.index].style.backgroundColor = "red";
}
}
var c = new Carousel("box");
c.init();
</script>
</html>
init不是强求这么写的,但是为了显得专业还是这么写吧。
ES6的继承
class Animal{
constructor(name,age){
this.name=name;
this.age=age;
}
eat(){
console.log("吃月饼");
}
}
//es6中的继承使用extends关键字
/*
子类 extends 父类
*/
class Person extends Animal{
//se6中继承,子类里面的 constructor的第一行必须调用一个函数叫super
constructor(name,age){
super(name,age);//super其实就是在调用父类animal的constructor
}
}
var p = new Person("张三",12);//new 其实就是在执行constructor
console.log(p);