15_JS闭包、对象、原型
闭包
在程序语言中,所谓闭包,是指语法域位于某个特定的区域,具有持续参照(读写)位于该区域内自身范围之外的执行域上的非持久型变量值能力的段落。这些外部执行域的非持久型变量神奇地保留他们在闭包最初定义(或创建)时的值。
白话: 我们可以用一个函数 去访问 另外一个函数的内部变量的方式就是闭包。
- 变量作用域
function outFun() {
var num = 10; // outFun 的一个变量
function inFun() {
var key = 10;
console.log(num); // 外部函数的变量可以被内部函数所使用
}
console.log(key); // 这样是不可以的,内部函数的变量不可以被外部函数所使用
}
- 闭包常用写法
function outFun(){
var num = 10;// 让fun 函数以外的函数 的使用 num 的值 就可以创建闭包
function inFun(){
console.log(num);
}
return inFun;// 返回的是 inFun函数体 核心
}
// 使用
var demo = outFun();
console.log(demo);
demo();//输出10,这个10是另外一个函数的变量
- 闭包练习
function outerFun(){
var a = 0;
function innerFun(){
a++;
alert(a);
}
return innerFun;
}
var o1 = outerFun();
o1();//1
o1();//2
var o2 = outerFun();
o2();//1
o2();//2
- 简写
function outerFun(){
var a = 0;
function innerFun(){
a++;
alert(a);
}
return innerFun;
}
可以这样简写
function outerFun(){
var a = 0;
return function(){
a++;
alert(a);
}
}
- 闭包传参
function outerFun(x){
function innerFun(){
console.log(x);
}
return innerFun;//不能带括号
}
var obj = outerFun(4);
obj();
function outerFun(x){
function innerFun(y){
console.log(x+y);
}
return innerFun;//不能带括号
}
var obj = outerFun(4);
obj();//NaN
obj(2);//6
- **闭包的优缺点 : **
- 优点:不产生全局变量,实现属性私有化。
- 缺点:闭包中的数据会常驻内存,在不用的时候要删掉否则会导致内存溢出。
例:闭包案例,事件参数传递
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style type="text/css">
.box{
position: absolute;
left: 0;
width: 100px;
height: 100px;
background-color: red;
}
</style>
<script type="text/javascript">
window.onload = function () {
var btn1 = document.getElementById("btn1");
var btn2 = document.getElementById("btn2");
var box = document.getElementById("box");
//以前的传统写法
/*
var speed = 5;
btn1.onclick = function () {
box.style.left = box.offsetLeft+speed+"px";
}
btn2.onclick = function () {
box.style.left = (box.offsetLeft-speed)+"px";
}
*/
//封装函数的写法
/*
btn1.onclick = function () {
move(5);
}
btn2.onclick = function () {
move(-5);
}
function move(speed){
box.style.left = box.offsetLeft+speed+"px";
}
*/
//闭包传递参数的写法
function move(speed){
return function () {
box.style.left = box.offsetLeft+speed+"px";
}
}
btn1.onclick = move(5);
btn2.onclick = move(-5);
}
</script>
</head>
<body>
<button id="btn1">右走</button>
<button id="btn2">左走</button>
<div class="box" id="box"></div>
</body>
</html>
例:tab栏切换,使用闭包
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style type="text/css">
body,ul,li{
margin: 0;
padding: 0;
}
ul,li{
list-style: none;
}
.box{
width: 500px;
height: 430px;
margin: 15px auto;
}
.tab-btn{
width: 500px;
height: 30px;
}
.tab-btn li{
float: left;
width: 100px;
height: 30px;
line-height: 30px;
background-color: #ccc;
color: #333;
text-align: center;
}
.tab-btn .current{
background-color: red;
color:#fff;
}
.tab-con div{
width: 500px;
height: 400px;
background-color: red;
display: none;
}
.tab-con .show{
display: block;
}
</style>
<script type="text/javascript">
window.onload = function () {
function $(id){return document.getElementById(id);}
function tab(id){
var lis = $(id).getElementsByTagName("li");
var cons = $(id).getElementsByClassName("tab-con")[0].getElementsByTagName("div");
for(var i=0; i<lis.length; i++){
lis[i].index = i;
lis[i].onmouseover = tab(i);
}
//使用了闭包
function tab(num){
return function () {
for(var j=0; j<lis.length; j++){//清除所有
lis[j].className = "";
cons[j].className = "";
}
lis[num].className = "current";//留下当前的
cons[num].className = "show";
}
}
}
tab("one");//第一个tab栏
tab("two");//第二个tab栏
}
</script>
</head>
<body>
<div class="box" id="one">
<div class="tab-btn">
<ul>
<li class="current">首页</li>
<li>新闻时事</li>
<li>体育</li>
<li>购物</li>
<li>游戏</li>
</ul>
</div>
<div class="tab-con">
<div class="show">首页</div>
<div>新闻时事</div>
<div>体育</div>
<div>购物</div>
<div>游戏</div>
</div>
</div>
<div class="box" id="two">
<div class="tab-btn">
<ul>
<li class="current">首页</li>
<li>新闻时事</li>
<li>体育</li>
<li>购物</li>
<li>游戏</li>
</ul>
</div>
<div class="tab-con">
<div class="show">首页</div>
<div>新闻时事</div>
<div>体育</div>
<div>购物</div>
<div>游戏</div>
</div>
</div>
</body>
</html>
修改为闭包的立即执行,修改之后
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style type="text/css">
body,ul,li{
margin: 0;
padding: 0;
}
ul,li{
list-style: none;
}
.box{
width: 500px;
height: 430px;
margin: 15px auto;
}
.tab-btn{
width: 500px;
height: 30px;
}
.tab-btn li{
float: left;
width: 100px;
height: 30px;
line-height: 30px;
background-color: #ccc;
color: #333;
text-align: center;
}
.tab-btn .current{
background-color: red;
color:#fff;
}
.tab-con div{
width: 500px;
height: 400px;
background-color: red;
display: none;
}
.tab-con .show{
display: block;
}
</style>
<script type="text/javascript">
window.onload = function () {
function $(id){return document.getElementById(id);}
function tab(id){
var lis = $(id).getElementsByTagName("li");
var cons = $(id).getElementsByClassName("tab-con")[0].getElementsByTagName("div");
for(var i=0; i<lis.length; i++){
lis[i].index = i;
lis[i].onmouseover = function(num){//闭包的立即执行
return function () {
for(var j=0; j<lis.length; j++){//清除所有
lis[j].className = "";
cons[j].className = "";
}
lis[num].className = "current";//留下当前的
cons[num].className = "show";
}
}(i);
}
}
tab("one");//第一个tab栏
tab("two");//第二个tab栏
}
</script>
</head>
<body>
<div class="box" id="one">
<div class="tab-btn">
<ul>
<li class="current">首页</li>
<li>新闻时事</li>
<li>体育</li>
<li>购物</li>
<li>游戏</li>
</ul>
</div>
<div class="tab-con">
<div class="show">首页</div>
<div>新闻时事</div>
<div>体育</div>
<div>购物</div>
<div>游戏</div>
</div>
</div>
<div class="box" id="two">
<div class="tab-btn">
<ul>
<li class="current">首页</li>
<li>新闻时事</li>
<li>体育</li>
<li>购物</li>
<li>游戏</li>
</ul>
</div>
<div class="tab-con">
<div class="show">首页</div>
<div>新闻时事</div>
<div>体育</div>
<div>购物</div>
<div>游戏</div>
</div>
</div>
</body>
</html>
tab栏切换添加函数节流
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style type="text/css">
body,ul,li{
margin: 0;
padding: 0;
}
ul,li{
list-style: none;
}
.box{
width: 500px;
height: 430px;
margin: 15px auto;
}
.tab-btn{
width: 500px;
height: 30px;
}
.tab-btn li{
float: left;
width: 100px;
height: 30px;
line-height: 30px;
background-color: #ccc;
color: #333;
text-align: center;
}
.tab-btn .current{
background-color: red;
color:#fff;
}
.tab-con div{
width: 500px;
height: 400px;
background-color: red;
display: none;
}
.tab-con .show{
display: block;
}
</style>
<script type="text/javascript">
window.onload = function () {
function $(id){return document.getElementById(id);}
function tab(id){
var lis = $(id).getElementsByTagName("li");
var cons = $(id).getElementsByClassName("tab-con")[0].getElementsByTagName("div");
for(var i=0; i<lis.length; i++){
lis[i].index = i;
var timer = null;
lis[i].onmouseover = function(num){//闭包的立即执行+函数节流
return function () {
clearTimeout(timer);
timer = setTimeout(function () {
for(var j=0; j<lis.length; j++){//清除所有
lis[j].className = "";
cons[j].className = "";
}
lis[num].className = "current";//留下当前的
cons[num].className = "show";
},300);
}
}(i);
lis[i].onmouseout = function () {
clearTimeout(timer);
}
}
}
tab("one");//第一个tab栏
tab("two");//第二个tab栏
}
</script>
</head>
<body>
<div class="box" id="one">
<div class="tab-btn">
<ul>
<li class="current">首页</li>
<li>新闻时事</li>
<li>体育</li>
<li>购物</li>
<li>游戏</li>
</ul>
</div>
<div class="tab-con">
<div class="show">首页</div>
<div>新闻时事</div>
<div>体育</div>
<div>购物</div>
<div>游戏</div>
</div>
</div>
<div class="box" id="two">
<div class="tab-btn">
<ul>
<li class="current">首页</li>
<li>新闻时事</li>
<li>体育</li>
<li>购物</li>
<li>游戏</li>
</ul>
</div>
<div class="tab-con">
<div class="show">首页</div>
<div>新闻时事</div>
<div>体育</div>
<div>购物</div>
<div>游戏</div>
</div>
</div>
</body>
</html>
例:立即执行函数
<script type="text/javascript">
var fun = function(){}
//方式一
fun();//立即执行
//方式二
function(){}();//立即执行
</script>
案例:如上tab栏的修改之后
例:函数节流
window.onload = function () {
var box = document.getElementById("box");
var num = 0;
window.onresize = throttle(function () {
num++;
box.innerHTML = window.innerWidth||document.documentElement.clientWidth;
console.log(num);
},30);
function throttle(fn,delay){//闭包,节流
var timer = null;
return function () {
clearInterval(timer);
timer = setTimeout(fn,delay);
}
}
}
对象(object)
对象是什么?
基本数据类型 :string number boolean null undefined
Array对象
对象数据类型: 对象就是带有属性和方法的数据类型
var num = 10; // 变量
var arr = []; // 数组
arr.index = 10; // 数组arr 的 一个 index 属性
但是有个问题, 我们想要某些属性或者方法的时候,用数组不合适。arr.lenght
我们想要自己id属性和方法 。 要求这个一定是个对象才行。
声明对象
我们有两种声明对象的方式.
var obj = new Object();
但是我们更提倡用第二种方法: 字面量式声明对象
var obj = {};
var obj = {}; // 声明对象
obj.name = "刘德华"; // 属性
obj.age = 55;
obj.showName = function() { // 声明方法 方法一定带有 ()
alert("俺是刘德华");
}
obj.showAge = function() {
alert("俺今年18岁");
}
使用对象
console.log(obj.name); // 调用属性
console.log(obj.age);
obj.showName(); // 调用方法
obj.showAge();
this
<script type="text/javascript">
function fn(){
console.log(this);//this指向window对象
}
fn();
function fn1(){
this.x = 12;
}
fn1();
console.log(window.x);//打印12
new fn();//使用new的函数,this指向新的对象,而不是window
function person(){
this.x = 20;
}
var demo = new person();
console.log(demo.x);//打印20
console.log(window.x);//打印12
</script>
new
我们经常利用new 关键字 去声明新的对象
new运算符的作用是创建一个对象实例。这个对象可以是用户自定义的,也可以是带构造函数的一些系统自带的对象。
new 关键字可以让 this 指向新的对象
所谓"构造函数",其实就是一个普通函数,但是内部使用了this变量。对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例对象上。
prototype
共同的 相同的 部分
主要解决:函数因为使用非常非常多,重复执行效率太低。
Person.prototype.showName = function() { // 用的共同的父亲
alert("我的名字是"+ this.name);
}
类.prototype.方法 = function() {} 具体格式
可以把那些不变的属性和方法,直接定义在prototype对象上
使用方法:类名.prototype.方法
<script type="text/javascript">
Array.prototype.run = function () {
console.log(this);
console.log(this.length);
}
var demo1 = [1,2,3,5];
demo1.run();
var demo2 = [5];
demo2.run();
</script>
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.showName = function () {
alert("名称="+this.name);
}
var p1 = new Person("刘德华",24);
var p2 = new Person("张学友",23);
p1.showAge = function () {
alert("年龄="+this.age);
}
p2.showAge = function () {
alert("年龄="+this.age);
}
alert(p1.showAge == p2.showAge);//false
alert(p1.showName == p2.showName);//true
alert(p1.showName === p2.showName);//true
上面第一个打印false,第二个打印true,第三个打印true
例:下拉菜单,面向对象版
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>无标题文档</title>
<style type="text/css">
*{ padding:0; margin:0; list-style:none;}
.all{ width:330px; height:30px; background:url(img/bg.jpg) no-repeat; margin:100px auto; line-height:30px; text-align:center; padding-left:10px; margin-bottom:0;}
.all ul li{ width:100px; height:30px; background:url(img/libg.jpg); float:left; margin-right:10px; position:relative; cursor:pointer;}
.all ul ul{ position:absolute; left:0; top:30px; display:none;}
</style>
</head>
<body>
<div class="all" id="list">
<ul>
<li>一级菜单
<ul>
<li>二级菜单</li>
<li>二级菜单</li>
<li>二级菜单</li>
</ul>
</li>
<li>一级菜单
<ul>
<li>二级菜单</li>
<li>二级菜单</li>
<li>二级菜单</li>
</ul>
</li>
<li>一级菜单
<ul>
<li>二级菜单</li>
<li>二级菜单</li>
<li>二级菜单</li>
</ul>
</li>
</ul>
</div>
</body>
</html>
<script>
// 获取对象 遍历对象操作 显示模块 隐藏模块
function List(id) { // 获取对象
this.id = document.getElementById(id); // 取 id 值
this.lis = this.id.children[0].children; // 获取一级菜单所有的li
}
// init 初始化
List.prototype.init = function() { // 遍历所有的li 显示和隐藏
var that = this;
for(var i=0;i<this.lis.length;i++)
{
this.lis[i].index = i;
this.lis[i].onmouseover = function() {
that.show(this.children[0]); // 显示出来
}
this.lis[i].onmouseout = function() {
that.hide(this.children[0]); // 隐藏起来
}
}
}
// 显示模块
List.prototype.show = function(obj) {
obj.style.display = "block";
}
// 隐藏模块
List.prototype.hide = function(obj) {
obj.style.display = "none";
}
var list = new List("list"); // 实例化了一个对象 叫 list
list.init();
</script>