小练习1:N的阶乘
//n的阶乘一般求法:
function mul(n){
var num = 1;
for(var i = 1;i<=n; i++){
num *=i;
}
return num;
}
mul(5)
//递归:
//规律n! = n * (n - 1);
function mul(n){
if(n == 1 || n==0){
return 1;
}
return n * mul(n-1);
}
mul(5);
递归需要注意的两点:1.找规律、2.找出口。没有出口的话递归就会无限死循环,所以必须用一个已知的条件当做它的出口。递归只有一个好处让代码变得简洁,除此之外无任何好处。递归的速度特别慢(因为每一个结果都要等待下一次递归执行的结果), 所以特别复杂的程序不能用递归。
小练习2:菲波那切数列
//规律 fb(n) = fb(n-1) + fb(n- 2)
function fb(n){
if(n ==1 || n ==2){
return 1;
}
return fb(n) = fb(n-1) + fb(n- 2);
}
递归最典型的就是这两个:阶乘和菲波那切数列。有规律有出口就可以大胆的使用递归。
作用域初探
1、作用域定义:变量(变量作用域又称上下文)和函数生效(能被访问)的区域。
function test(){
var a = 123;
function demo(){
var b = 234;
document.write(a);
}
demo();
document.write(b);
}
test();
结果:123、undefined
互相嵌套的函数,外层的函数不能访问里层的变量,但里层的函数可以访问外层的变量,也就是越往里它的权限就越大。
2、全局、局部变量
定义在全局的变量称为全局变量,定义在函数内部的变量称为局部变量。
3、作用域的访问顺序
js运行三部曲
1、语法分析
javascript的两大特点,单线程和解释性语言。js在执行的时候是解释一行执行一行而不是通篇编译再执行,但在执行之前会先通篇扫描一下有没有低级的语法错误,比如多个大括号,有中文符号等等,但不执行。这个扫描过程就叫语法分析的过程。 然后进行预编译,最后在再解释执行。
2、预编译
对于预编译,记住两句话:
- 函数声明整体提升,也就是只要你写了一个函数声明,无论写在那里,系统都会把函数提到逻辑的最前面、所以不管在那里调用它,本质上都是在函数下面调用;
- 变量 声明提升,变量一旦声明,无论是否赋值,变量的声明都会被提升到逻辑的最前面,但仅仅是声明被提升了,变量的赋值并不被提升。
但是这两句话解决不了所有的问题,来看一下下面这个:
console.log(a);
function a(a){
var a = 234;
var a = function(){
}
a()
}
var a = 123;
函数名和变量名重了要怎么办呢?这就是那两句话不能解决的问题。学完预编译,所有的问题都会解决。
预编译前奏
- 1.imply global 暗示全局变量:即任何变量,如果变量未经声明及赋值,此变量就为全局对象(window)所有。全局上的任何变量,即使声明了也归window所有。
- eg: a = 123;
- eg: var a = b = 123; (注意连续 赋值是自右向左的)
//window就是全局的域
var a = 123;
//相当于在window里添加
//window{
// a:123;
//}
//如果变量定义在全局上,那就相当于在全局上挂了一个这样的属性。
//下一次在访问这个a的时候,会在windows里面去找是否有这个a。
-
2.一切声明的全局变量,全是window的属性。
- eg: var a = 123; ===> window.a = 123;
3.window就是全局。
function fn(a){
console.log(a);
var a = 123;
console.log(a);
function a(){};
console.log(a);
var b = function(){};
console.log(b);
function d(){}
}
fn(1);
既有变量又有函数,都存在提升,也会存在一个覆盖的问题,那么这里任何提升?谁覆盖谁呢?这就是预编译发生的奇妙的过程。
预编译发生在函数执行的前一刻。预编译经过如下四个过程:
预编译四部曲
- 1.创建AO对象:Activation Object(执行期上下文)
AO{
//AO对象
}
- 2.找形参和变量声明,将变量和形参名作为AO属性名,值为undefined。这里讲的是函数的,全局比较简单。
AO{
a: undefined;
b: undefined;
}
- 3.将实参和形参统一
AO{
a: 1;
b: undefined;
}
- 4.在函数体里面找函数声明,值赋予函数体(注意是函数声明而不是函数表达式)
AO{
a: function d(){};
b: undefined;
d: function d(){}
}
预编译四部曲的第四部优先级最高。到这里预编译基本完成,开始解释执行。执行期上下文相当于作用域。
3、解释执行
解释执行时,变量的值是在AO里面找的,如上面第四步所示。
function fn(a){
console.log(a); //function a(){}
var a = 123; //预编译时,变量的声明已经被提升,
//所以这里未执行的只有a=123这个赋值语句,
//也就是相当于重新给a赋值,覆盖了AO里面的原始的a的值,
//到这里,a的值为123,而不是function...
console.log(a); //123
function a(){}; //解释执行时,预编译看过的地方不用再看。
console.log(a); //123
var b = function(){}; //b变成function(){} ==> b: function(){}
console.log(b); //function(){}
function d(){}
}
fn(1);
所以最后的执行结果是:
function a(){}
123
123
function (){}
再看一个例子:
function test(a, b){
document.write(a);
c = 0;
var c;
a = 3;
b = 2;
document.write(b);
function b(){};
function d(){};
document.write(b);
}
test(1);
经过预编译四部曲后的AO:
//第一、二步:
AO{
a: undefined;
b: undefined;
c: undefined;
}
//第三步:
AO{
a: 1;
b: undefined;
c: undefined;
}
//第四步:
AO{
a: 1;
b: function b(){};
c: undefined;
d: function(){};
}
//最后执行后的结果为:
//1
//2
//2
再举几个栗子:
function test(a, b){
console.log(a);
console.log(b);
var b = 234;
console.log(b);
a = 123;
console.log(a);
function a(){};
var a;
b = 234;
var b = function(){};
console.log(a);
console.log(b);
}
test(1)
//AO
AO{
a: function a(){};
b: undefined;
}
结果:
function a(){}
undefined
234
123
123
function(){}
预编译不止发生在函数里,还发生在全局。来看一下全局的预编译:
- 1.生成一个GO 对象 Global Object。GO===window。
其他是一样的,不过全局没有参数。
console.log(window.a) === console.log(GO.a)
也就相当于console.log(a)
function test(){
var a = b = 123;
console.log(window.b); //123
console.log(window.a); //undefined
}
test();
GO{
b: 123; //未经声明的变量归GO所有。
}
AO{
a: undefined;
}
AO和GO,先生成GO;
最后看一个特别恶心的栗子:
GO{
test: function(){
//...
}
}
console.log(test); // 结果 ==> function test(){...}
function test(test){
console.log(test); // 结果 ==> function test(){//函数内部这个 }
//这里GO和AO里面都有test,先找AO里面的值,
//也就是遵循一个我自己有就用自己的,我没有才用别人的原则。
var test = 234;
console.log(test); //234
function test(){
}
}
AO{
// 先是 test: undefiend;
test: function test(){
},
}
test(1);
var test = 123;
再看一个:
//Go{
// a: undefiend;
// c: 234; 暗示全局变量
//}
function test(){
console.log(b); // undefined
if(a){ //注意,找变量声明和形参的时候不执行if或for语句,直接把里面的东西拿出来
var b = 100;
}
console.log(b); // undefined
c = 234;
console.log(c); //234
}
var a;
test();
//AO{
// b: undefiend;
//}
a = 10;
console.log(a); // 10
console.log(c); //234
13年百度面试题1:
function bar(){
return foo;
foo = 10;
function foo(){
}
var foo = 11;
}
console.log(bar()); // function foo(){}
13年百度面试题2:
console.log(bar()); //11
function bar(){
foo = 10;
function foo(){
}
var foo = 11;
return foo;
}
练习1:
a = 100;
function demo(e){
function e(){ };
arguments[0] = 2;
console.log(e); //2
if(a){
var b = 123;
function c(){
//以前可以在if里面定义function,现在不允许了
}
}
var c;
a = 10;
var a;
console.log(b); //undefined
f = 123;
console.log(c); //function (){ }
console.log(a); //10
}
var a ;
demo(1);
console.log(a); //100
console.log(f); //123
练习2:
var str = false + 1;
console.log(str); // 1
var demo = false == 1;
conso.log(demo); // false
if(typeof(a) &&-true + (+undefined) + ""){
// typeof(a) ==> 字符串型的undefined
//-true + (+undefined) + "" ==> NaN
console.log("基础扎实");
}
if(11 + "11" * 2 == 33){
console.log("基础扎实);
}
!!" " + !!"" - false||console.log("你觉得能打印,你就是猪");
// 空格字符串是ture,!!就能把它变成布尔值
//空字符串是false,!!就能把它变成布尔值
typeof()是唯一一个使用未定义的变量不报错,返回undefined的方法。typeof(null)返回对象。一般数学符号的隐式转换,都是转换为number,转换不了的一般都是NaN。
练习3:css中display的属性有几种?
none 隐藏元素
block 块级元素
inline 行内元素
inline-block
练习4:window.foo的值为?
(window.foo || (window.foo = 'bar'));
// ||的优先级高于=
// bar(先读括号)