1、考察实参argument、函数运行时的作用域
<script>
function sum() {
if (arguments.length == 2) {
return arguments[0] + arguments[1];
} else if (arguments.length == 1) {
var first = arguments[0];//2
return function(second) {
return first + second;
};
}
}
console.log(sum(2,3));//5
console.log(sum(2)(3));//5
/*
解析:
argument代表实参类数组
当sum(2,3)时,arguments.length == 2,返回arguments[0] + arguments[1]=>2+3=5
当sum(2)(3)时,调用sum函数后返回的函数再调用;
sum(2)=>function(second){return first + second};
再调用返回的函数,传入3,second=3,first在函数中没有,到外部作用域查找;
因为函数是写在函数中的,函数调用时的作用域是看函数写在那个位置(此处考察函数运行时的作用域)
所以first=2 first+second=2+3=5
*/
</script>
2、考察this
<script>
var User = {
count: 1,
getCount: function() {
return this.count
}
}
var fn = User.getCount //让fn引用User.getCount
console.log(fn()); //fn()可写为window.fn(),调用者为window
//调用返回结果为window.count=>indefined
/*
易错:
认为getCount函数的调用者为User,内部的this代表User,但是只是这里并未调用,
只是把User的属性让fn引用,this是看是谁调用。
真正的调用者是是隐式的window,fn()可写为window.fn()
*/
</script>
3、关于作用域
<script>
function fun(n, o) {
console.log(o)
return {
fun: function(m) {
return fun(m, n);
}
};
}
var a = fun(0);
a.fun(1);
a.fun(2);
a.fun(3);
var b = fun(0).fun(1).fun(2).fun(3);
var c = fun(0).fun(1);
c.fun(2);
c.fun(3);
/*
解析:
关于a:
fun(0)=>
执行过程:fun(n,o){
var n,o
n=0
o=undefined
console.log(o)//打印undefined
return { fun: function(m) {return fun(m, n);}};
}
a={ fun: function(m) {return fun(m, n);}};
a.fun(1)=>
function(m) {
var m = 1;
return fun(m, n);先调用函数,再返回值(补充: fun: function()这种形式是无法直接通过fun()调用,要通过对象.fun());
此时调用外部的fun函数,fun(m,n)=>m,n先取值再传入,m=1,n在函数内未存在。到上一级作用域查找n=0;
}
fun(n, o) {
var n,o
n=1;
o=0
console.log(o)//打印0
return {fun: function(m) {return fun(m, n);}};
}
a.fun(1)= {fun: function(m) {return fun(m, n);}};
a.fun(2)=>
function(m) {
var m = 2;
return fun(m, n);先调用函数,再返回值
此时调用外部的fun函数,fun(m,n)=>m,n先取值再传入,m=2,n在函数内未存在。到上一级作用域查找n=0;
}
fun(n, o) {
var n,o
n=2;
o=0
console.log(o)//打印0
return {fun: function(m) {return fun(m, n);}};
}
a.fun(2)= {fun: function(m) {return fun(m, n);}};
a.fun(3)=>
function(m) {
var m = 3;
return fun(m, n);先调用函数,再返回值
此时调用外部的fun函数,fun(m,n)=>m,n先取值再传入,m=3,n在函数内未存在。到上一级作用域查找n=0;
}
fun(n, o) {
var n,o
n=3;
o=0
console.log(o)//打印0
return {fun: function(m) {return fun(m, n);}};
}
a.fun(3)= {fun: function(m) {return fun(m, n);}};
关于b:
fun(0).fun(1).fun(2).fun(3)=>
全局作用域:[fun(0)]{
function fun(n, o) {
var n,o;
n=0;
o=undefined;
console.log(o) //打印undefined
return { //fun(0)返回值
fun: function(m) {
return fun(m, n);
}
};
[fun(0).fun(1)]调用这个返回值:{
function(m) {
var m;
m=1
return fun(m, n); ==>调用全局作用域下的fun,m、n先取值,m=1,n=0
得到:{
fun: function(m) {
return fun(m, n);
}
};
[fun(0).fun(1).fun(2)]调用这个返回值:{
function(m) {
var m;
m=2
return fun(m, n); ==>调用全局作用域下的fun,m、n先取值,m=2,n=1
得到:{
fun: function(m) {
return fun(m, n);
}
};
[fun(0).fun(1).fun(2).fun(3)]调用这个返回值:{
function(m) {
var 3;
m=3
return fun(m, n); ==>调用全局作用域下的fun,m、n先取值,m=3,n=2
得到:{
fun: function(m) {
return fun(m, n);
}
};
}
}
}
m=1时调用的 function fun(n, o) {
var n,o;
n=1;
o=0;
console.log(o) //打印0
return { //fun(0).fun(1)返回值
fun: function(m) {
return fun(m, n);
}
};
m=2时调用的 function fun(n, o) {
var n,o;
n=2;
o=1;
console.log(o) //打印1
return { //fun(0).fun(1).fun(2)返回值
fun: function(m) {
return fun(m, n);
}
};
m=3时调用的 function fun(n, o) {
var n,o;
n=3;
o=2;
console.log(o) //打印2
return { //fun(0).fun(1).fun(2).fun(3)返回值
fun: function(m) {
return fun(m, n);
}
};
b={ fun: function(m) {return fun(m, n);}
关于c:
全局作用域:[fun(0)]{
function fun(n, o) {
var n,o;
n=0;
o=undefined;
console.log(o) //打印undefined
return { //fun(0)返回值
fun: function(m) {
return fun(m, n);
}
};
[fun(0).fun(1)]调用这个返回值:{
function(m) {
var m;
m=1
return fun(m, n); ==>调用全局作用域下的fun,m、n先取值,m=1,n=0
得到:{
fun: function(m) {
return fun(m, n);
}
};
}
}
}
}
m=1时调用的 function fun(n, o) {
var n,o;
n=1;
o=0;
console.log(o) //打印0
return { //fun(0).fun(1)返回值
fun: function(m) {
return fun(m, n);
}
};
c={ fun: function(m) {return fun(m, n);}
c.fun(2)=>
此时function fun(n, o) {
var n,o;
n=1;
o=0;
console.log(o) //打印0
return { //fun(0).fun(1)返回值
fun: function(m) {
return fun(m, n);
}
};
运行 c.fun(2):
function(m) {
var m = 2;
return fun(m, n);先调用函数,再返回值
此时调用外部的fun函数,fun(m,n)=>m,n先取值再传入,m=2,n在函数内未存在。到上一级作用域查找n=1;
}
function fun(n, o) {
var n,o;
n=2;
o=1;
console.log(o) //打印1
return { //c.fun(2)返回值
fun: function(m) {
return fun(m, n);
}
};
运行 c.fun(3):
function(m) {
var m = 3;
return fun(m, n);先调用函数,再返回值
此时调用外部的fun函数,fun(m,n)=>m,n先取值再传入,m=3,n在函数内未存在。到上一级作用域查找n=1;
}
function fun(n, o) {
var n,o;
n=3;
o=1;
console.log(o) //打印1
return { //c.fun(3)返回值
fun: function(m) {
return fun(m, n);
}
};
*/
</script>
4、标识符的隐式提升和作用域
<script>
var num = 5;
function func1() {
var num = 3;
var age = 4;// 调用func3后,age=6
function func2() {
console.log(num);//unf
var num = 'ivan';
function func3() {
age = 6;
}
func3();
console.log(num);// 'ivan'
console.log(age);//6
}
func2();
}
func1();
/*
解析:
1、函数不调用则不运行,先声明一个num赋值为5,再调用func1函数
2、声明局部变量num和age分别赋值为3和4,再调用func2
3、func2内部情况
function func2() {
var num; 标识符隐式提升
function func3() { 函数提前声明
age = 6;
}
console.log(num);在这个函数中,此时num只是声明了,但未赋值,打印undefined
num = 'ivan'; //为num赋值为'ivan'
func3();
调用func3,{age = 6;为age赋值,func3函数无age,到上级作用域查找}
console.log(num);// 'ivan'
console.log(age);// func2中无age,到fun1中寻找,上面经过调用func3,将age 的修改为6
//打印结果为6
}
*/
</script>
5、对象的知识
<script>
function changeObjectProperty(o) {
o.siteUrl = "http://www.csser.com/";
o = new Object();
o.siteUrl = "http://www.popcg.com/";
return o
}
var CSSer = new Object();
//CSSer引用一个新创建的空对象
var re=changeObjectProperty(CSSer);
//调用changeObjectProperty函数,把这个对象当实参传入函数,并赋值给re
/*
changeObjectProperty函数执行过程:
changeObjectProperty(o) {
var o=CSSer //让o也引用CSSer这个对象引用的对象
o.siteUrl = "http://www.csser.com/";
//为这个对象添加一个siteUrl属性,并赋值为"http://www.csser.com/"
o = new Object();
o引用一个新的对象;
o.siteUrl = "http://www.popcg.com/";
为o的新对象添加一个siteUrl属性,并赋值为"http://www.csser.com/"
return o //返回的是新创建的这个属性
}
*/
console.log(CSSer.siteUrl);//"http://www.csser.com/"
console.log(re.siteUrl)//"http://www.csser.com/"
re=null;//防止内存泄漏
</script>
6、对象知识
<script>
var x = 8;
var objA = {
x: 'good',
y: 32 //=>5
}
function add(x, y) {
console.log(x.y + y);//8
}
function fn(x, y) {
x.y = 5;
y(x, 3);
}
fn(objA, add);
console.log(objA);//{x:"good",y:5}
/*
解析:
调用fn函数,将objA对象和add函数作为实参传入。
fn函数执行过程:
function fn(x, y) {
var x,y
x={
x: 'good',
y: 32
};
y=function(x, y){
console.log(x.y + y);//8
}
x.y = 5;
//将y属性的值改为5,因为x引用的是objA对象,所以外部的objA对象也发生了改变
y(x, 3);
//调用函数,先取x的值=>y(8,3)
function(x, y){
var x,y
x=8;
y=3;
console.log(x.y + y)//这里的x是对象,到上级作用域查找
//5+3=8
}
最后打印objA//{x:"good",y:5}
*/
</script>
7、对象的知识
<script>
var obj1 = new Object();
//声明obj1标识符引用{}
obj1.name = 'zjzhome';
//为obj1引用的对象添加一个name属性,赋值为'zjzhome'
var obj2 = obj1;
//让obj2也引用这个对象
console.log(obj2.name);//因为是同一个对象 打印'zjzhome'
obj1.name = 'zjz'; //修改name的值
console.log(obj2.name);//'zjz'
</script>
8、运算符
<script>
var a = '' + 3;
//字符串与数字相加转换为字符串拼接
var b = 4;
console.log(typeof a); //"string"
console.log(a + b); //"34"
console.log(a - b); //-1 数值类型的字符串可以跟数字减法运算
console.log({} - b) //NaN
//number: NAN 200 100 10.2 Infinity
var foo = "11" + 2 + "1";
console.log(foo); //"1121"
console.log(typeof foo); //"string"
var foo1 = "11" + 2 + 1;
//只要有一个是字符串全部都会进行字符串拼接
console.log(foo1)//1121
var foo2 = "11" + 2 * 2;
//先进行乘法运算,它的优先级高
console.log(foo2)//114
</script>
9、考察函数运行时作用域
<script>
var name = 'laruence';
function echo() {
console.log(name);
}
function env() {
var name = 'eve';
echo();//'laruence';
}
env();
//调用env函数,
function env() {
var name = 'eve';//声明局部变量name,赋值为'eve'
echo();//调用echo函数,打印name,'laruence'
//echo是写在全局作用域下的,它的作用域不在env内,所以打印的是全局变量name
}
</script>
10、考察细心
<script>
a = new Array(2, 3, 4, 5, 6);
sum = 0;
for (i = 1; i < a.length; i++) //i是从1开始的,取不到2
sum += a[i]; //结果为3+4+5+6
console.log(sum);//18
</script>
11、考察作用域
<script>
var f = true;
if (f === true) {
var a = 10;
}
function fn() {
var b = 20;
c = 30;
}
fn();
console.log(a); //10
console.log(b); //b为局部变量,全局作用域下无法直接访问,代码报错
console.log(c); //代码报错后,后面的代码不再执行
</script>
12、考察 this 作用域 隐式声明提升 原型 符号优先级
<script>
function Foo() {
getName = function() {
console.log(1);
}
return this;
}
Foo.getName = function() {console.log(2)}
Foo.prototype.getName = function() {console.log(3)}
var getName = function() {console.log(4)}
function getName() {console.log(5)}
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
/*
预编译:
var getName
function Foo() {
getName = function() {
console.log(1);
}
return this;
}
function getName() {console.log(5)}
Foo.getName = function() {console.log(2)}
//为Foo添加一个属性getName,值为function() {console.log(2)}
Foo.prototype.getName = function() {console.log(3)}
getName = function() {console.log(4)}
Foo.getName(); // 2
//取Foo的getName属性,然后再调用
getName();// 4 调用全局作用域下的getName函数,从下往上找就近优先
// getName = function() {console.log(4)}调用这个函数
Foo().getName(); //1
//先调用Foo函数,为getName赋值,本函数内无getName,到上一级作用域寻找,为getName从新赋值,此时getName = function() {console.log(4)} =>getName = function() {console.log(1)}
//然后返回this对象,代表window,Foo().getName()=>window.getName()
//调用function() {console.log(1)}
getName(); //1
//调用全局作用域下的getName函数,此时getName = function() {console.log(1)}
new Foo.getName(); //2
//根据优先级
//先访问Foo的成员,再new一个函数(new带参优先级大于函数调用优先级)
//new (Foo.getName)()=>1、创建一个空对象 2、调用Foo.getName函数 3、返回这个空对象
new Foo().getName(); // 1
//根据优先级 从左往右
//((new Foo()).getName)() (访问成员优先级大于函数调用)
//1、创建一个空对象 2、调用Foo函数,将getName = function() {console.log(1);}
//3、函数然回创建的这个空对象
//4、此时表达式为{__proto__:{getName:function() {console.log(3)}} }.getName() 此时对象内无这个函数到原型链上查找
//5、调用function() {console.log(3)}
new new Foo().getName();//3
//根据优先级
//new ((new Foo()).getName)()
//1、创建一个对象{} 2、调用Foo函数,将getName = function() {console.log(1);}
//3、函数然回创建的空对象
//4、此时表达式为new ({__proto__:{getName:function() {console.log(3)}} }.getName)()
//5、创建一个对象{} 6、调用{__proto__:{getName:function() {console.log(3)}} }.getName函数 打印3
//7、返回一个空函数
*/
</script>