1. JavaScript 闭包
1. 什么是闭包?
由一个函数和函数可以访问到的一个变量组成一个闭包
「函数」和「函数内部能访问到的变量」的总和,就是一个闭包
var quanju="全局变量";
function test1(){
var name="名字";
window.alert("quanju=="+quanju);
}
2. 闭包的作用
正常函数执行完毕后 , 声明的变量会被处理掉 , 但是闭包可以让作用域里的 变量 , 在函数执行完之后依旧不会被处理掉
1. JavaScript 作用域
全局变量的作用域可以在所有函数中 局部变量的作用域只能在当前函数中
1. JavaScript 全局变量
<script type="text/javascript">
var quanju="全局变量";
function test1(){
var name="名字";
//window.alert("name=="+name);
window.alert(quanju);
}
test1();
function test2(){
var name="mingzi";
//window.alert("name=="+name);
window.alert(quanju);
}
test2();
</script>
在函数外部定义变量,所有函数都可以访问
2. JavaScript 局部变量
在函数内部定义声明变量 ,只有本函数可以访问
3. JavaScript 函数内部未声明的变量
如果变量在函数内没有声明(没有使用 var 关键字),该变量为全局变量
<script type="text/javascript">
function test3(){
testname="没有声明的变量,为全局变量";
window.alert("testname=="+testname);
}
function test4(){
window.alert("testname=="+testname);
}
test3();
test4();
</script>
4. JavaScript 函数参数
函数参数只在函数内起作用,是局部变量
<script type="text/javascript">
function test5(myname){
window.alert("myname=="+myname);
}
function test6(){
window.alert("myname=="+myname);
}
test5("函数定义参数");
test6("函数未定义参数");
</script>
全局变量会覆盖window 对象的变量
局部变量,包括 window 对象可以覆盖全局变量和函数
<script type="text/javascript">
window.doxname="window 对象的变量";
var doxname="全局变量覆盖 window";
function test7(){
window.alert("doxname=="+doxname);
}
test7();
function test8(){
var doxname="局部变量覆盖全局变量和函数";
window.alert("doxname=="+doxname);
}
test8();
</script>
2. 闭包隐藏变量的改变方法
<script type="text/javascript">
function test1(){
var local="闭包隐藏变量的改变方法";
function foo(){
return local;
}
return foo;
}
function test2(){
var test3=test1();
var val=test3();
alert(val);
}
test2();
</script>
以上函数 test2 只可以调用 test1 中的变量 local ,而无法修改变量值
闭包不是函数套函数
闭包中的函数是可以没有return语句的
2. JavaScript 原型
我们先看俩个数组的对比 :
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script type="text/javascript">
var arr1 = [1, 0, 0, 8, 6];
var arr2 = [1, 0, 0, 8, 6, 1, 1];
//sort 排序方法
arr1.sort(function(n1, n2) {
return n1 - n2;
});
alert(arr1);//0,0,1,6,8
arr2.sort(function(n1, n2) {
return n1 - n2;
});
alert(arr2);//0,0,1,1,1,6,8
alert(arr1 === arr2);//false
alert(arr1.sort === arr2.sort);//true
</script>
</head>
<body>
</body>
</html>
我们可以看到俩个数组比较返回的布尔值是 false ,是因为arr1和arr2中的元素和元素个数不同
而俩个数组的 sort 方法比较时返回的布尔值是 true ,是因为arr1和arr2使用的是相同的sort方法,sort方法是arr1和arr2的公共方法
那么 JavaScript 是如何做到让sort方法是arr1和arr2的公共方法 ?
例子 :数组求和方法
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script type="text/javascript">
var arr1 = [1, 0, 0, 8, 6];
var arr2 = [1, 0, 0, 8, 6, 1, 1];
arr1.getSum = function(){
var sum = 0;
for(var i = 0; i <= this.length-1; i++){
sum += this[i];
}
return sum;
}
alert(arr1.getSum());//15
alert(arr2.getSum());//报错
</script>
</head>
<body>
</body>
</html>
可以看到 alert(arr2.getSum()); 报错 ,是因为变量 arr2 没有定义 getSum() 方法
那么有没有什么解决方案,函数只定义一次,然后提供给不同的变量使用呢?
答案是有的,我们可以将 getSum 定义为原型方法
Array.prototype.getSum = function() {......}将getSum()方法给了Array.prototype
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script type="text/javascript">
var arr1 = [1, 0, 0, 8, 6];
var arr2 = [1, 0, 0, 8, 6, 1, 1];
Array.prototype.getSum = function() {
var sum = 0;
for(var i = 0; i <= this.length-1; i++) {
sum += this[i];
}
return sum;
}
alert(arr1.getSum());//15
alert(arr2.getSum());//17
</script>
</head>
<body>
</body>
</html>
此时 getSum() 方法就成了原型方法,由此可知 Array.prototype 是数组的原型对象
得到的结果是对象 arr1 ,arr2 就可以共享 getSum 方法了
1. JavaScript 原型对象
在传统的 OOP 中,首先定义“类”,此后创建对象实例时,类中定义的所有属性和方法都被复制到实例中。在 JavaScript 中并不如此复制,而是在对象实例和它的构造器之间建立一个链接(它是__proto__属性,是从构造函数的prototype属性派生的),之后通过上溯原型链,在构造器中找到这些属性和方法。
1. 每个函数上面都有一个属性(prototype)指向了函数的原型对象(Person.prototype)
function Person() { } 函数
alert(Person.prototype);
即使你只定义了一个空函数,也存在一个prototype的属性。
尽管我们什么都不做,但是浏览器已经在内存中创建了两个对象:Person(函数)和Person.prototype,其中,我们称Person为构造函数,因为我们后面要用到这个函数来new对象,Person.prototype称为Person的原型对象,简称原型。
//构造函数 student
function student(){
this.name="小暖";
this.test1=function(){
}
}
var stu = new student();//实例
在对象实例 stu 和它的构造器 student(){} 之间建立一个链接 __proto__ 属性 ,
__proto__ 属性是从构造函数的 prototype 属性派生的
之后通过上溯原型链,在构造器中找到这些属性和方法
上溯原型链
stu → __proto__ → student.prototype → 构造函数 student 的属性
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script type="text/javascript">
//构造函数 student
function student(){
this.name="小暖";
this.test1=function(){
}
}
//创建对象(实例)
var stu = new student();
alert(stu.__proto__);//[object Object]
alert(student.prototype);//[object Object]
alert(stu.constructor);//构造函数 student
//创建新的对象(实例)
var stu1 = new student();
//使用构造函数设置新属性
student.prototype.age = "设置新属性";
//使用新对象输出新属性的值
alert(stu1.age);//设置新属性
//输出属性name的值
alert(stu.name);//小暖
//使用旧对象输出新属性的值
alert(stu.age);//设置新属性
</script>
</head>
<body>
</body>
</html>
2. JavaScript 原型链
对象在寻找某一属性时,如果自身属性没找到就去他对应的原型对象去找。若在原型上面找到对应的属性则停止,否则继续去原型的原型找对应的属性,这样构成了一条原型链。
console.log(student.prototype.__proto__ === Object.prototype); //true
这时候来了一个Object对象,它是JavaScript的顶级对象,同样也有自己的原型Object.protoype,这时候Person对象以及它的原型,Object对象已经对应的原型关系如下图所示
Object.prototype,它将两者打通,构成一个链式关系。同时你也看到Object的prototye也指向了Object.prototype,所以console.log(Person.prototype.__proto__ === Object.prototype)输出的额结果是 true
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script type="text/javascript">
Object.prototype.test1=function(){
alert("Object的原型对象中的方法");
}
function Person(name,age,address){
this.name=name;
this.age=age;
this.address=address;
}
var p1=new Person("小暖",21,"西安");
p1.test1();//Object的原型对象中的方法
alert(Object.prototype.__proto__);//null
var p2=new Person("小暖2",21,"西安2");
p2.test1();//Object的原型对象中的方法
alert(p2.address);
alert(Person.address);
</script>
</head>
<body>
</body>
</html>
对象在寻找某一属性时,如果自身属性没找到就去他对应的原型对象去找
若在原型上面找到对应的属性则停止,否则继续去原型的原型找对应的属性,这样构成了一条原型链
Object的原型对象是原型链的最顶端