递归和作用域
递归:指的就是函数内部直接或间接调用自己;
function test () {
test();
}
test();
这里值得一说的是递归主要有两个要素
自己调用自己 要有结束条件 (如果只是自己调用自己而没有结束条件,最后会内存泄露,程序崩溃掉);
递归主要用到的化归思想
化归思想:将一个问题由难化易,由繁化简,把一个困难的问题拆分成很多步来做
下面来几个用到递归的例子
例如:
求前n项和(1 - n)
前5 项和: 1 + 2 + 3 + 4 + 5 前4 项和 + 5
前4 项和: 1 + 2 + 3 + 4 前3 项和 + 4
前3 项和: 1 + 2 + 3 前2 项和 + 3
前2 项和: 1 + 2 前1 项和 + 2
前1 项和: 1 1
fn(n) = fn(n - 1 ) + n
结束条件:
当n=1 的时候直接返回1
function sum (n) {
if (n == 1 ){
return 1 ;
}
return sum(n - 1 ) + n;
}
console.log(sum(5 ));
再例如:
求n的m次方:
求 n的 m次方
求n的5 次方: n * n * n * n * n
求n的4 次方: n * n * n * n
求n的3 次方: n * n * n
求n的2 次方: n * n
求n的1 次方: n
由上面我们可以得出规律: fn(n,m) = fn(n,m-1 )*n;
所以申明函数如下:
function pow (n,m) {
if (m == 1 ){
return n
}
return pow(n,m-1 ) * n;
}
在斐波那契数列上更实用
1 1 2 3 5 8 13 21 34 55
仔细观察它的规律,我们能得出:fn(n) = fn(n-1 ) + fn(n-2 );
当n是1 或者n是2 的时候都可以直接return 1
function fib (n) {
if (n == 1 || n == 2 ){
return 1 ;
}
return fib(n - 1 ) + fn( n - 2 )
}
递归实现通过id名获取元素原理
function getChildren (ele) {
var list = [];
var children = ele.children;
for (var i = 0 ; i < children.length; i++ ){
var child = children[i];
list.push(child);
var temp = getChildren(child);
list = list.concat(temp);
}
return list;
}
function getElementById (id) {
var list = getChildren(document.body);
for (var i = 0 ; i < list.length; i++){
if (list[i].id == id){
return list[i];
}
}
return null ;
}
通过这个两个函数我们就实现通过id名获取元素的原理了;
```
## 作用域
### 作用域:顾名思义,变量起作用的范围,就是变量的作用域
* 在js当中有且只有函数可以创建作用域;
* 块级作用域 (通过代码块限定的作用域)(js中没有块级作用域)
* 变量的作用域只和函数的声明位置有关系,和函数的具体调用没任何关系,这种作用域就是:词法作用域(静态作用域),而js中的作用域就是这种作用域;
```js
var num = 123 ;
function f1 () {
console.log(num);
}
function f2 () {
var num = 456 ;
f1();
}
f2();
<div class ="se-preview-section-delimiter" ></div >
js作用域中的变量提升 (hoisting)
在js代码的与解析阶段,系统会将代码中所有的变量声明以及函数声明提升到其所在的作用域的最顶上!
func();
function func () {
var num = 123 ;
console.log(123 );
}
var a;
console.log(a);
a = 1 ;
function a () {
console.log(123 );
}
a();
<div class ="se-preview-section-delimiter" ></div >
js作用域变量提升六种特别情况
1. 函数同名的情况:同名的函数都会被提升,但是后面的函数声明会将前面的函数声明给覆盖掉
func();
function func () {
console.log("第一个func" );
}
function func () {
console.log("第二个func" );
}
<div class ="se-preview-section-delimiter" ></div >
2. 变量和函数同名的情况:只提升函数,忽略掉变量声明
console.log(typeof a);
console.log(typeof a);
var a = 123 ;
function a () {
console.log("我是一个函数" );
}
var a;
function a () {
console.log("我是一个函数" );
}
console.log(a);
a = 123 ;
<div class ="se-preview-section-delimiter" > </div >
3. 预解析是分作用域的
function func () {
console.log(f);
function f () {
console.log("我是在局部作用域中声明的函数" );
}
}
func();
<div class ="se-preview-section-delimiter" ></div >
4. 预解析是分段的 “段”指的script标签
func();
function func () {
console.log("我是第一个script标签中的函数" );
}
var b = 10 ;
<div class ="se-preview-section-delimiter" ></div >
5.条件式函数声明(不推荐使用) 在条件判断语句中声明的函数,可以将其当做是一个函数表达式来处理,只提升函数名,函数体不会被提升!
console.log(func);
if (false ){
function func () {
}
}
console.log(func);
if (false ){
var func = function () { };
}
var isDog = true ;
var bark = null ;
if (isDog){
bark = function () {
console.log("汪汪汪" );
}
}else {
bark = function () {
console.log("喵喵喵" );
}
}
bark();
<div class ="se-preview-section-delimiter" > </div >
6. 函数中存在形参和变量或者函数同名的时候的提升情况,函数中形参的声明和赋值过程优先于变量提升,并且不参与变量提升
function test (a) {
console.log(typeof a);
function a () {
}
}
test(100 );
<div class ="se-preview-section-delimiter" > </div >
最后又是我们的面试题环节了 @_@ ,下面的面试题基本都在上面六种情况内,我就只写答案,不做分析了哈~!
1.
function foo () {
var num = 123 ;
console.log(num);
}
foo();
console.log(num);
2.
var scope = "global" ;
function foo () {
console.log(scope);
var scope = "local" ;
console.log(scope);
}
foo();
3.
if ("a" in window){
var a = 10 ;
}
alert(a);
4.
if (!"a" in window){
var a = 10 ;
}
alert(a);
5.
var foo = 1 ;
function bar () {
if (!foo) {
var foo = 10 ;
}
alert(foo);
}
bar();
6.
var num = 123 ;
function f1 () {
console.log(num);
}
function f2 () {
var num = 456 ;
f1();
}
f2();
7.
var num = 123 ;
function f1 (num) {
console.log(num);
}
function f2 () {
var num = 456 ;
f1(num);
}
f2();
8.
var num = 123
function f1 () {
console.log(num);
}
f2();
function f2 () {
num = 456 ;
f1();
}
console.log(num);
9.
(function (a) {
console.log(a);
var a = 10 ;
function a () { }
}( 100 ));
10. 这个因为涉及到原型和对象,可能会有点难,但也不是太难,我们一步一步分析就能解决它;
function Foo () {
getName = function () { alert(1 ); };
return this ;
}
Foo.getName = function () { alert(2 ); };
Foo.prototype.getName = function () { alert(3 ); };
var getName = function () { alert(4 ); };
function getName () { alert(5 ); }
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
函数可以创建作用域,函数中又可以声明函数,这样就形成了作用域嵌套作用域的链式结构
首先在当前使用这个变量的作用域中进行查找,如果有该变量(有声明),就直接使用当前作用域中的这个变量,但是如果没有 就去上一级作用域中进行查找,如果有该变量(有声明),就直接使用当前作用域中的这个变量,但是如果没有 就继续沿着作用域链向上查找,直到找到全局为止
` ``js
1.
function foo () {
var num = 123 ;
console.log(num);
}
foo();
console.log(num);
2.
var scope = "global" ;
function foo () {
console.log(scope);
var scope = "local" ;
console.log(scope);
}
foo();
3.
if ("a" in window){
var a = 10 ;
}
alert(a);
4.
if (!"a" in window){
var a = 10 ;
}
alert(a);
5.
var foo = 1 ;
function bar () {
if (!foo) {
var foo = 10 ;
}
alert(foo);
}
bar();
6.
var num = 123 ;
function f1 () {
console.log(num);
}
function f2 () {
var num = 456 ;
f1();
}
f2();
7.
var num = 123 ;
function f1 (num) {
console.log(num);
}
function f2 () {
var num = 456 ;
f1(num);
}
f2();
8.
var num = 123
function f1 () {
console.log(num);
}
f2();
function f2 () {
num = 456 ;
f1();
}
console.log(num);
9.
(function (a) {
console.log(a);
var a = 10 ;
function a () { }
}( 100 ));
10. 这个因为涉及到原型和对象,可能会有点难,但也不是太难,我们一步一步分析就能解决它;
function Foo () {
getName = function () { alert(1 ); };
return this ;
}
Foo.getName = function () { alert(2 ); };
Foo.prototype.getName = function () { alert(3 ); };
var getName = function () { alert(4 ); };
function getName () { alert(5 ); }
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
函数可以创建作用域,函数中又可以声明函数,这样就形成了作用域嵌套作用域的链式结构
首先在当前使用这个变量的作用域中进行查找,如果有该变量(有声明),就直接使用当前作用域中的这个变量,但是如果没有 就去上一级作用域中进行查找,如果有该变量(有声明),就直接使用当前作用域中的这个变量,但是如果没有 就继续沿着作用域链向上查找,直到找到全局为止