一、作用域及作用域链
作用域:作用的范围
作用域链:在子级作用域中使用一个变量,如果当前作用域没有,会向上一级作用域寻找,如果还没有,一次向上一级的上一级寻找,这个过程就叫做作用域链。(一级一级向上查找的过程就叫做作用域链)
script : 全局作用域
- 声明在全局作用域中的变量,叫做全局变量,同时也是window对的属性,在全局范围中有效;生存时间,从程运行开始开辟空间,到退出程序释放空间。
- 声明在函数中的函数,叫做全局函数,同时也是window对象的方法。
function :函数作用域(局部作用域)
- 声明在函数中的变量或形参,叫做局部变量,只在所在的函数中有效;生存时间,从调用函数开始开辟空间,到函数调用结束释放空间。
一旦进入作用域,浏览器就会启动js解析器,就会执行下面的两步
第一步 预解析 (寻找var function 形参)
- 如果在作用域中找到了var 或形参,会将后面的变量名放到内存中,并且给它初始化一个值为undefined。
- 如果在作用域中找到了function,会将函数名提取到内容中,并将整个函数块赋值给这个变量。
- 如果变量与函数名同名时,丢变量,保函数。(同类型覆盖,不同类型选择)
- 一个页面中有多个script作用域时,从上到下依次解析script,所以上面script中声明的东西,下面可以使用。下面声明的,上面的使用报错。建议所有声明的东西放到第一个script中。(声明的东西会都放在内存中,所以上面声明的,下面在内存中能找见。)
第二步 逐行解析代码(如果遇到的是声明函,则直接跳过)
- 执行表达式
- 函数调用(函数也是一个作用域)
一、预解析(寻找var function 形参)
二、逐行解析代码(如果遇到的是声明函数,则直接跳过)
1. 执行表达式
2. 函数调用(函数也是一个作用域)
<script>
/*
一、预解析 var 形参 function
如果在作用域中找到了var或形参,会将后面的变量名放到内存中,并赋一个初值为undefined
a = undefined
如果在作用域中找到了function,会将函数名提取到内存中,并赋值为整个函数块
fn = function fn(){}
二、逐行解读代码
alert(a) a=undefined
a=1
alert(a) a=1
如果遇到函数声明,则跳过
*/
alert(a);//undefined
var a = 1;
alert(a); //1
function fn(){
alert(2);
}
</script>
<script>
/*
一、预解析
a=function a(){}
如果函数名和变量名相同,丢变量,保函数。(同类型覆盖。不同类型选择)
二、逐行解析
*/
alert(a); //a=function a(){alert(4);}
var a = 1;
alert(a); //a=1
function a(){
alert(2);
}
var a = 3;
alert(a); //a=3
function a(){
alert(4);
}
alert(a); //a=3
</script>
<script>
/*
第一个script
一、预解析
fn = function fn(){}
二、逐行解析
第二个script
一、预解析
a = undefined
二、逐行解析
第一个script预解析完进行逐行解析的时候,内存中还没有a,会报错
第二个script预解析完进行逐行解析的时候,调用第一个script中的fn(){},因为fn()已经在内存中了。
*/
alert(a); //报错
function fn(){
alert(2);
}
</script>
<script>
var a = 1; //a=1
fn(); //a= 2
</script>
<script>
/*
一、预解析
a = undefined
fn = function fn(){}
二、逐行解析
函数作用域
预解析
逐行解析
在子级作用域使用一个变量,如果当前作用域没有,就依次向上级寻找。找到就使用。
同样在子级作用域给一个变量赋值,如果当前作用域没有,就依次向上级寻找,找到就修改。
*/
alert(a); //a = undefined
var a = 1;
alert(a); //a = 1
function fn(){
alert(a); //a = 1
a = 2;
}
fn(); //a = 1 a = 2
alert(a); //a=2
</script>
<script>
/*
一、预解析
a = undefined
fn = function fn(a){}
二、逐行解析
函数作用域
a=undefined
*/
var a = 1;
alert(a);//a=1
function fn(a){
alert(a); //a = undefined
a = 2;//a = 2
}
fn();
alert(a); //a = 1 这里是全局的a
</script>
<script>
/*
一、预解析
a = undefined
fn = function fn(){}
二、逐行解析
函数作用域
预解析
a = 1 实参传递过来的值
逐行解析
*/
var a = 1;
alert(a); //1
function fn(a){
alert(a); //1
a = 2;
}
fn(a);
alert(a); // 1
</script>
<script>
/*
一、预解析
a = undefined
fn = function fn(){}
二、逐行解析
函数作用域
预解析
a = 1
*/
var a = 1;
alert(a); //1
function fn(a){
alert(a); //1
var a = 3;
alert(a); //3
a = 2;
alert(window.a); //1 全局的a
}
fn(a);
alert(a); //1
</script>
<script>
/*
一、预解析
fn = function(){}
二、逐行解析
预解析
a = 3
隐式声明的变量会在全局作用域中自动生效
*/
function fn(){
a = 3;
alert(a);//3
a = 2;
alert(window.a); //2
}
fn();
alert(a); //2
</script>
二、递归 : 自己调用自己。
本质 : 循环(初始值,条件,步长)
<script>
//利用递归实现 输出10次helloworld
function fnRe(n){
if( n === 0){
return 0;
}else{
console.log('HelloWorld!');
fnRe(n-1);
}
}
//递归 1-100的和
function fnSum(n){
if(n===0){
return 0;
}else{
return n + fnSum(n-1);
}
}
//递归 n的阶乘
function fnMulti(n){
if(n===1){
return 1;
}else{
return n * fnMulti(n-1);
}
}
</script>
三、匿名函数
匿名函数,是没有名字函数
- 将匿名函数赋值给一个变量
- 通过事件绑定
- 函数的自我执行
- 匿名函数传参
- 可以有返回值
<script>
/*
匿名函数,是没有名字的函数
function(){
alert('匿名函数');
}
*/
// 1.将匿名函数赋值给一个变量
// var fn = function(){
// alert('匿名函数');
// alert('将匿名函数赋值给一个变量');
// }
//2.通过事件绑定
//点击页面即可触发
// document.onclick = function(){
// alert('匿名函数');
// alert('通过事件绑定');
// }
//3.函数自我执行
// (function(){
// alert('匿名函数');
// alert('函数的自我执行');
// })()
//4.匿名函数传参
// (function(a,b){
// alert('匿名函数');
// alert('匿名函数传参');
// alert(a);
// })(3,4)
//5.可以有返回值
var fn = (function(a,b){
alert('匿名函数');
alert('可以有返回值');
return a + b;
})(3,5)
console.log(typeof fn);//number
// console.log(fn());
// console.log(fn);
</script>
三、如何创建对象?
- 字面量的方式: { key : value,key : value} 当只需要一个对象的时候。
- new 内置构造函数() : new Object();
- new 自定义构造函数() : new 函数名(); 批量创建对象时
构造函数语法:
function 函数名([参数]){
this.属性 = 值;
this.方法 = function(){
。。。。
}
}
<script>
/*
如何创建对象
*/
//1.字面量的方式:
// {key : value,key : value,……}
var obj = {
name : '张三',
age : 18,
showName : function(){
return this.name;
}
}
//工厂模式,批量生产
//2.new 内置构造函数() :
//new object();
function creatObj(){
//原材料
var obj = new Object();
//加工
obj.name = name;
obj.age = age;
obj.showName = function(){
return this.name;
}
//出厂
return obj;
}
var obj1 = new Object('张三',18);
var obj2 = new Object('二哈',7);
console.log(typeof obj1,typeof obj2)
//工厂模式的缺点
//1. 无法分辨对象具体属于哪一类的对象。
//2.不符合程序员创建对象的习惯
//3.new 自定义构造函数()
// new 函数名(); 批量创建对象时
function Person(){
this.name = '张三';
this.age = 18;
this.shouName = function(){
this.name;
}
}
function Dog(){
this.name = '二哈';
this.age = 7;
this.shouName = function(){
this.name;
}
}
//构造函数中的this代表:new出来的对象
//new做了哪些是(面试)1.在构造函数中会自动创建一个对象 2.会自动返回这个对象 所以 new内置构造函数中要var 对象,并且要return 对象;而自定义构造函数中不需要。
var per1 = Person('张三',18);
var dog1 = Dog('二哈',7);
console.log(typeof per1,typeof dog1);
//instanceof : 判断一个对象是否属于某个类
console.log(per1 instanceof Person , dog1 instanceof Dog);
</script>
四、如何访问对象中的属性和方法
1. 对象.属性 对象.方法()
2. 对象['属性'] 对象['方法']()
<script>
function Dog(name,age){
this.name = name;
this.age = age;
this.showAge = function(){
return this.age;
}
this.showName = function(){
return this.name;
}
}
var dog1 = new Dog('小黄',5);
// var dog = new Dog('大黄',7);
// console.log(dog.name,dog.showName());
console.log(dog1.age,dog1.showName(),this.age);
</script>