一、作用域
- 作用域的概念:变量或函数或数据或功能,作用的区域,生效的区域,可被使用的区域
- 作用域的分类:
- 全局作用域:不属于任何函数的语句,叫全局作用域。全局作用域内的变量,叫全局变量。
- 局部作用域:属于某个函数内部的区域,叫局部作用域。局部作用域内的变量,叫局部变量。
- 作用域之间数据的访问规则(作用域嵌套后)
- 父作用域不能使用子作用域内部的数据(父不能拿子)
- 子作用域可以使用父作用域内部的数据(子可以拿父)
var a = 10; function fn(){ var b = 20; console.log(a); // 10 console.log(b); // 20 } fn(); console.log(a); // 10 console.log(b); // Error: b is not defined
- 多层作用域嵌套后,变量的读写规则
- 读(使用):
- 先在自身作用域查找,找到了就使用并停止查找
- 找不到,依次向上层作用域查找
- 任意一层找到了,都使用并停止查找
- 直到顶层都没找到,报错!
- 写(赋值):
- 先在自身作用域查找,找到了就修改并停止查找
- 找不到,依次向上层作用域查找
- 任意一层找到了,都修改并停止查找
- 直到顶层都没找到,直接声明成全局,再做修改!(非严格模式,严格模式下,报错)
- 读(使用):
- 不同作用域内变量的特点
- 全局变量:跟随全局环境,在内存中一直存在,占内存
- 局部变量:根据局部环境,局部作用域创建,变量存在,局部作用域结束,变量被删除,省内存
- 建议:尽量少用全局,可以使用匿名函数,生成一个大的新的作用域,将要使用的数据和多个功能再次包裹
;(function(){ var a = 10; function box1(){ console.log(a); // 10 } function box2(){ a = 20; console.log(a); // 20 } function box3(){ console.log(a); // 20 } box1(); box2(); box3(); })();
二、声明提升
- 所谓声明提升,就是js解析器在解析js代码时,会先将当前作用域内的变量声明和函数声明,都提前到作用域最开始的位置执行,这样做是为了在内存中提前创建好对应空间,以便将来程序可以直接进行使用。
- 但,这其实是js语言现存的一个bug,提前声明带来的优势非常小,反而在没有块级作用域概念时,会造成各种作用域空间的污染。所以在之后的版本ES6的新特性中,变量的声明提升被禁止了。
- 声明变量的提升
- var声明的变量,都会提前到作用域最开始的位置声明,原位赋值
console.log(a); // undefined var a = 10; console.log(a); // 10
- var声明的变量,都会提前到作用域最开始的位置声明,原位赋值
- 声明函数的提升
- function声明的函数,会整体提升到作用域最开始的位置
console.log(fn); // function fn(){...} function fn(){ console.log(1) } console.log(fn); // function fn(){...}
- function声明的函数,会整体提升到作用域最开始的位置
- 当var遇到function
- 赋值式创建函数:var fn = function(){}
- 提升的是变量,并没有提升函数
console.log(fun); // undefined var fun = function(){ console.log(2) } console.log(fun); // function(){...}
- 提升的是变量,并没有提升函数
- 当var的变量名和function的函数名重名时,var先提升,所以function生效
- 注意:重名,本来就是不被允许的!
console.log(b); // function b(){...} var b = 20; function b(){ console.log(3); } console.log(b); // 20
- 注意:重名,本来就是不被允许的!
- 赋值式创建函数:var fn = function(){}
三、递归
- 递归:在函数的内部,执行自身。滥用递归会造成类似于死循环的现象,造成程序崩溃,计算机死机,内存溢出
- 利用递归的思想,解决程序中的问题,要配合函数的结束语句,适当时跳出递归
- 就算是正常递归,也会消耗大量性能
- 必须使用时尽量少用,能少用时尽量不用
- 递:
- 不断执行自身的过程
- 归:
- 不断向外返回数据的过程
- 使用递归计算阶乘,如:5! = 54321
// 已知:
// 5! = 5 * 4 * 3 * 2 * 1
// 5! = 5 * 4!
// 4! = 4 * 3!
// 3! = 3 * 2!
// 2! = 2 * 1!
// 1
// 可推算计算任意数的阶乘公式为:
// fn(n) = n * fn(n-1);
// ...
// fn(5) = 5 * fn(4);
// fn(4) = 4 * fn(3);
// fn(3) = 3 * fn(2);
// fn(2) = 2 * fn(1);
// 计算最小数字1的阶乘为:
// fn(1) = 1;
function fn(n){
if(n === 1){
return 1;
}else{
return n * fn(n-1);
}
}
console.log( fn(5) ); // 120
四、对象介绍
-
对象:一种对客观事物的描述,将客观事物以逻辑数据的形式进行描述的过程
- 无序数据的打包,object对象,标志是
{}
- 有序数据的打包,array对象,标志是
[]
- 无序数据的打包,object对象,标志是
-
对象的本质(组成)
- 键值对。键(key)和值(value)一一对应,成对出现
- 键和值之间使用冒号连接,键值对之间使用逗号隔开。伪代码:
{ 颜色:红色, 身高:180, 体重:150, 发型:板寸 }
-
对象的意义(作用):存储数据,编程
-
对象的创建
- 字面量:
var obj = {}
- 构造函数:
var obj = new Object()
- 字面量:
-
任何情况下,两个对象都不相等;如果相等了,表示这俩就是一个对象。
var obj1 = { }; var obj2 = new Object(); var obj3 = { } var obj4 = obj1; console.log(obj1); // {} console.log(obj2); // {} console.log(obj1 === obj2); // false console.log(obj1 === obj3); // false console.log(obj4 === obj1); // true
-
对象的操作语法
- 点语法:当对象的属性是具体的值时
- 中括号语法:当对象的属性是变量时
var a = "world"; var obj = { title:"对象的操作", number:18, [a]:"啊哈哈哈" } var str = "number"; console.log( obj.str ); // undefined console.log( obj[str] ); // 18 console.log( obj.a ); // undefined console.log( obj.world ); // 啊哈哈哈 console.log( obj["title"] ); // 对象的操作 console.log( obj[title] ); // Error: title is not defined
-
对象的操作
var person = { name:"张三", age:18};
- 增:
person.sex = "男"; person["sex"] = "男";
- 删:
delete person.age; delete person["age"];
- 改
person.sex = "女"; person["sex"] = "女";
- 查
console.log(person.name) console.log(person["name"])
- 增:
-
对象的分类(了解)
- 宿主对象:
window
,document
- 本地对象:构造函数,需要new执行后才能得到具体的对象:
Object
,Number
,String
,Boolean
- 内置对象:官方提供的可直接使用的对象:
Math
- 宿主对象:
五、练习
- 编写函数,利用递归解决
- 当n为偶数时,调用函数,求1/2+1/4+…+1/n
- 当n为奇数时,调用函数,求1/1+1/3+…+1/n
- 利用递归求两个数字最大公约数
- 利用递归求斐波那契数列的第n位(1, 1, 2, 3, 5, 8, 13, 21, …)