作用域链
定义
变量在当前环境now、内部环境f1、内部深层环境f2/f3….都起作用的现象形成了一个链条,这个链条就称为变量的”作用域链”
<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
var title = '链条';
//title 在各个环境都起作用的现象形成了一个链条,称为“作用域链”
console.log('now:' + title);
function f1(){
console.log('f1:' + title);
function f2(){
console.log('f2:' + title);
function f3(){
console.log('f3:' + title);
}
f3();
}
f2();
}
f1();
</script>
</head>
<body>
<h2>作用域链</h2>
</body>
</html>
作用域链的作用
变量必须“先声明、后使用”
函数可以“先使用、后声明”,原因是函数有“预加载”过程(函数声明先于其他执行代码进入内存)。本质还是函数的声明在前,使用在后。
<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
//变量必须“先声明、后使用”
//函数可以“先使用、后声明”
/*
同名函数和变量的执行顺序问题,函数预先加载,之后代码一步步执行
*/
//var getInfo = 'school';//第二加载,会污染函数
getInfo();//第三加载,调用函数
var getInfo = 'school';//第四加载,没问题
function getInfo(){//预先加载
console.log('function info');
}
var getInfo = 'school';//第五加载,没问题
</script>
</head>
<body>
<h2>作用域链的作用</h2>
</body>
</html>
内部环境可以访问外部环境的变量,反之不然
<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
//内部环境可以访问外部环境的变量,反之不然
var age = 20;
function f1(){
var height = 170;
console.log('age:' + age);//内部可访问外部
function f2(){
console.log('height:' + height);//内部可访问外部
}
f2();
}
f1();
console.log('height:' + height);//外部不可访问内部
</script>
</head>
<body>
<h2>作用域链的作用</h2>
</body>
</html>
变量的作用域是声明时决定的,而不是运行时
这个没什么好说的
同名变量只在其作用域内有效,不同层级的同名变量互不干扰
AO活动对象
AO: Active Object 活动对象
执行环境:
- js代码执行是有环境的
- 该环境定义了其有权访问的其他数据
- 环境有一个与之关联的“活动对象AO”
- 环境中所有的变量和函数都是活动对象AO的属性
- 全局环境是最外围的执行环境,活动对象是window对象
- 执行环境中的代码执行完毕后就被销毁
<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
//AO活动对象
var mingzi = 'tom';
console.log('名字:' + mingzi);
function f1(){
var age = 20;
console.log('f1 名字:' + mingzi + ' 年龄:' + age);
function f2(){
var height = 170;
console.log('f2 名字:' + mingzi + ' 年龄:' + age + ' 身高:' + height);
function f3(){
var mingzi = 'zhaosi';
console.log('f3 名字:' + mingzi + ' 年龄:' +age + ' 身高:' + height);
}
f3();
}
f2();
}
f1();
/*
以上代码都可执行,具体看f3
f3获得变量顺序:
1、在本环境内部寻找
2、在其外部寻找f2
3、再在外部环境寻找
。。。
直到找到全局环境为止
以上寻找变量过程貌似是一个“动态过程”,其实本质是“固态过程”
f3环境里可访问什么信息,早早的已经固化为其AO“成员属性”信息
在每个环境里都有AO活动对象,内部存储着可以访问的成员信息
最外层AO对象为window,访问时一般都省略,比如 window.mingzi window.age
*/
</script>
</head>
<body>
<h2>AO活动对象</h2>
</body>
</html>
执行环境可以访问变量的类型及优先顺序
<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
//执行环境可以访问变量的类型及优先顺序
//优先级:内部环境变量 >>> 本环境函数 >>> 形参 >>> 外部环境变量
var height = 170;//1、外部环境变量
function f1(){
var height = 180;//外部环境变量、形参、函数
function f2(height){//2、形参
var height = 190;//4、内部环境变量
console.log(height);
function height(){//3、本环境函数
alert('00004');
}
}
f2(200);
}
f1();
</script>
</head>
<body>
<h2>执行环境可以访问变量的类型及优先顺序</h2>
</body>
</html>
重新认识全局变量和局部变量
全局变量:
- 声明:
- ① 在函数外部声明的变量
- ② 在函数内部不使用var声明的变量(前提是函数执行之后才起作用)
- 其是活动对象window的成员信息
- 在访问的时候,window是否设置都可以
- 声明:
局部变量:
声明:
- 在函数内部通过var声明的变量
其在一定范围内可以看做是“全局变量”。
- 其在本环境、内部环境、内部深层环境都可以被访问。
闭包
什么是闭包
闭包就是一个函数,两个函数彼此嵌套,内部函数就是闭包
形成闭包条件是内部函数需要通过return给返回出来。
<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
//简单的闭包
function f1(){
var age = 20;
var height = 170;
function f2(){
console.log('年龄:' + age + ' 身高:'+ height);
}
return f2;
}
//对象赋值,其实为引用传递,就是创建一个新的引用,但它们有共同的实体
var ff = f1();//ff就是一个闭包,它和f2共同指向同一个function
ff();
</script>
</head>
<body>
<h2>闭包</h2>
</body>
</html>
闭包特点
闭包有权利调用其上级环境的变量信息。
闭包使用规则
同一个闭包机制可以创建多个闭包函数出来,它们彼此没有联系,都是独立的。
并且每个闭包可以保存自己个性化的信息。
<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
//同一个闭包机制可以创建多个闭包函数出来,它们彼此没有联系,都是独立的。
//并且每个闭包可以保存自己个性化的信息。
function f1(n){
function f2(){
console.log(n);
}
return f2;
}
var fa = f1(100);
var fb = f1(200);
fa();
fb();
</script>
</head>
<body>
<h2>闭包</h2>
</body>
</html>
闭包案例
闭包生成数组元素
<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
var arr = new Array();
for(var i=0; i<4; i++){
arr[i] = function(){
console.log(i);
return i;
}
}
/*
以上代码可以确定的是,添加了4个数组元素出来,下标为0.1.2.3
每个元素都要访问i的信息,而内存中变量i只有一个
而下面的代码在访问的时候,它们都调用了同个变量i,且i===4
*/
arr[1]();
arr[3]();
</script>
</head>
<body>
<h2>闭包生成数组元素</h2>
</body>
</html>
<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
var arr = new Array();
for(var i=0; i<4; i++){
//闭包方式创建数组元素
arr[i] = f1(i);//f1被创建了4次,分别给4个元素赋值
}
function f1(n){
function f2(){
console.log(n);
}
return f2;
}
arr[1]();
arr[3]();
/*
利用闭包就可以创建符合要求的数组元素
每个元素都是函数 内部有访问的独特信息
*/
</script>
</head>
<body>
<h2>闭包生成数组元素</h2>
</body>
</html>
闭包事件操作
<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
window.onload = function(){
var lis = document.getElementsByTagName('li');
for(var i = 0; i<3;i++){
lis[i].onmouseover = over(i);
lis[i].onmouseout = out(i);
}
//闭包
var cols = ['red','blue','green'];
function over(i){
function f2(){
lis[i].style.backgroundColor=cols[i];
}
return f2;
}
function out(i){
function f2(){
lis[i].style.backgroundColor='';
}
return f2;
}
}
</script>
</head>
<body>
<h2>闭包事件操作</h2>
<ul>
<li>阿大</li>
<li>阿二</li>
<li>阿三</li>
</ul>
</body>
</html>
面向对象
PHP里边,是从一个类里边获得一个具体对象。
Javascript里边,没有类的概念,可以直接创建一个对象出来,对象可以有默认成员,后期也可以给对象丰富成员出来。
创建对象
- 字面量方式创建
- 构造函数创建对象
- Object方式创建对象
<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
/*① 字面量方式创建
var obj = {};//空对象
var obj = {name:'tom',swim:function(){console.log('在游泳')}}
给对象丰富成员
对象.成员名 = 值;
对象[成员名] = 值;
访问成员
对象.成员名
对象[成员名]()
*/
var obj = {};
console.log(obj);//Object {}
var dog = {name:'大黄',age:5,hobby:function(){
console.log('吃屎');
}};
//给已有对象丰富其他成员
dog.color = 'yellow';
dog['weight'] = 80;
dog.run = function(){
console.log('冲冲冲');
}
console.log(dog);//Object {name: "大黄", age: 5, color: "yellow", weight: 80}
//访问成员
console.log(dog.color);//yellow
dog.hobby();//吃屎
dog['run']();//冲冲冲
</script>
</head>
<body>
<h2>创建对象</h2>
</body>
</html>
<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
/*② 构造函数创建对象
var obj = new 函数();
*/
function Animal(){
//设置默认成员,通过this关键字声明
this.name = '大黄';
this.age = 5;
this.run = function(){
console.log('跑');
}
}
var dog = new Animal();
console.log(dog);
dog.run();
</script>
</head>
<body>
<h2>创建对象</h2>
</body>
</html>
<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
/*③ Object方式创建对象
var obj = new 函数();
*/
var dog = new Object();
dog.color = 'yellow';
console.log(dog);
console.log(dog.color);
</script>
</head>
<body>
<h2>创建对象</h2>
</body>
</html>
对象在内存分配
与对象有关系的内存区域:
- 栈空间
存放的数据大小比较小,一般固定大小的信息适合存放在该空间,例如 整型、boolean、对象的引用(名称)。 - 堆空间
该空间存储的数据比较多,空间较大,一般数据长度不固定的信息在该空间存放,例如: string、Array、对象实体 - 数据空间
该空间存放常量、类的静态属性 - 代码空间
存放函数体、方法体代码
对象调用其他函数/对象成员
<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
//3. 对象调用其他函数/对象成员
var mingzi = '李雷';
function say(){
console.log('我叫 ' + this.mingzi);
}
var dog = {mingzi:'大黄',eat:'便便'};
//使当前dog对象调用上边的say函数
//解决:给say函数创建一个别名赋给dog对象的成员即可
dog.wangwang = say;
dog.wangwang();
</script>
</head>
<body>
<h2>3. 对象调用其他函数/对象成员</h2>
</body>
</html>
构造函数 与 普通函数
构造函数和普通函数的区别:
没有区别,就看使用,new就是构造函数,函数()就是普通函数调用。
函数的各种执行方式
普通函数调用
- 构造函数执行new
- 作为对象的成员方法执行
- 通过call和apply执行
call和apply可以明显控制变量污染的风险。
这两个方法基本上是一个意思,区别在于 call 的第二个参数可以是任意类型,而apply的第二个参数必须是数组
<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
//call和apply可以明显控制变量污染的风险。
var mingzi = '李雷';
function say(){
console.log('我叫 ' + this.mingzi);
}
var dog = {mingzi:'大黄',eat:'便便',act:function(a,b){console.log(this.mingzi + '在' + a + ',吃' + b)}};
var lilei = {mingzi:'李雷雷'};
//使用call方法调用
say.call(dog);
dog.act.call(lilei,'卖当当','叉烧包');
</script>
</head>
<body>
<h2>call和apply可以明显控制变量污染的风险。</h2>
</body>
</html>
this都是谁
- 在函数/方法里边 代表调用该函数/方法的当前对象
- 在事件中,代表元素节点对象
divnode.onclick = function(){
alert(this.value);
} - 代表window
- 可以任意代表其他对象
在call和apply使用的时候,可以任意设置被执行函数内部this的代表
获取构造器
构造器:使用什么元素实例化的对象,元素就称为该对象的构造器
对象.constructor; //获得构造器
<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
//函数就是一个对象,函数的构造器是Function
function f1(){
alert('123');
}
console.log(f1.constructor);//Function() { [native code] }
var ff = new Function('name','age',"console.log(name + ':' + age)");
ff('梅梅','20');
</script>
</head>
<body>
<h2>获取构造器</h2>
</body>
</html>
return对实例化对象的影响
<html>
<head>
<meta charset='utf-8' />
<script type='text/javascript'>
//return对实例化对象的影响
//仍然实例化对象,不过return后边的代码不执行
function ff(){
this.xing = '李';
this.ming = '白';
return this;
this.zi ='吃';
}
var f1 = new ff();
console.log(f1);//ff {xing: "李", ming: "白"}
</script>
</head>
<body>
<h2>return对实例化对象的影响</h2>
</body>
</html>