这里主要是弄清楚函数声明和变量声明会提升(即声明提到最近作用域的开头);变量声明、形参声明和函数声明的先后关系;全局作用域和局部作用域等问题
在js中,对于函数操作变量的问题需要注意以下几点:
1、js中,函数就相当于一个作用域。
2、在js中,函数声明和变量声明都有提升的作用,即提升到离他们最近的作用域的头部。变量声明,只是将变量声明在离他最近的作用域的头部,赋值在它正真定义的地方.
3、函数需要传递参数时,参数都是值传递的形式(即在函数内部改变一般变量的值,在函数问访问该变量,变量的值还是函数外定义的值)
4、在js中,通过var声明并且连续赋值,相当于除了第一个变量外,其他变量都定义成全局变量,例如
var a=b=c=10;
a为局部变量,b和c为全局变量
5、在js中,不通过var声明变量,直接给变量赋值,相当于将给变量定义成全局变量,如
a=10;
6、在一个作用域内,变量的声明顺序,变量声明 =》函数形参声明=》函数声明 ,声明是有先后顺序的
主要是理解全局变量和局部变量的区别,连续赋值的影响
代码1
var a = b = c = 10; //b和c是全局变量
(function(){
var a = b = c = 20; //重新定义变量,a局部变量,b和c全局变量,这里的b和c的值会覆盖外面b和c的值,a覆盖不了外面a的值
})();
console.log('a: '+a); //a: 10 ,访问的是外部的a的值
console.log('b: '+b); //b: 20
console.log('c: '+c); //c: 20
//代码2
var a = b = c = 10; //b和c全局变量
(function(){
a = b = c = 20; //这里相当于给a,b,c重新赋值
})();
console.log('a: '+a); //a: 20
console.log('b: '+b); //b: 20
console.log('c: '+c); //c: 20
代码3
(function(){
var a = b = c = 20; //a为局部变量,b和c为全局变量
})();
console.log('b: '+b); //b: 20
console.log('c: '+c); //c: 20
console.log('a: '+a); //ReferenceError: a is not defined 因为a为局部变量,在函数外没有声明a,所以报错
代码4
(function(){
a = b = c = 20; //a,b,c都是全局变量
})();
console.log('a: '+a); //a: 20
console.log('b: '+b); //b :20
console.log('c: '+c); //c: 20
变量声明会提升
// 代码1
var a = 10;
(function(){
console.log(a); //undefined
var a = 100; //变量声明会提升,即可写成代码2形式
})();
// 代码2
var a = 10;
(function(){
var a;
console.log(a); //undefined
a = 100;
})();
// 代码3
var a = 10;
(function(){
console.log(a); //undefined
var a =100; //a局部变量,覆盖不了外面a的值
})();
console.log(a); //10
// 代码4
var a = 10;
(function(){
console.log(a); //10
a =100; //对变量a重新赋值,覆盖外面a的值
})();
console.log(a); //100
连续赋值、函数声明会提升、变量声明和函数参数声明的顺序
// 代码1 函数声明和变量声明之后的形式如代码2
a(); //1,函数声明提升
var a = c = function() {
//函数表达式不会提升,在定义的地方开始执行,这里的a覆盖了函数声明中的a
console.log(2)
}
a(); //2 调用函数表达式中的a函数
function a() {
console.log(1)
}
a(); //2 调用函数表达式中的a函数
(function(b) {
b(), c(); // 2 2 b相当于a c调用函数表达式中的c
var b = c = function a() {
//函数表达式重新定义b,这里有连续赋值,这里的var预编译对c不起作用,相当于对最上面的c重新赋值
console.log(3)
}
b(); //3,调用行数表达式的b
})(a);
c() //3
// 代码2
var a;
function a() {
console.log(1)
}
a(); //1,函数声明提升
a = c = function() {
//函数表达式不会提升,在定义的地方开始执行,这里的a覆盖了函数声明中的a
console.log(2)
}
a(); //2 调用函数表达式中的a函数
a(); //2 调用函数表达式中的a函数
(function(b) {
b(), c(); // 2 2 b相当于a c调用函数表达式中的c
var b = c = function a() {
//函数表达式重新定义b,这里有连续赋值,这里的var预编译对c不起作用,相当于对最上面的c重新赋值
console.log(3)
}
b(); //3,调用行数表达式的b
})(a);
c() //3
var A = function() {
}
A.prototype.n = 1;
var b = new A()
A.prototype = {
n: 2, //重新定义A的原型链,A的原型链已经改变了,所以b和c所对应的原型对象不是一样的
m: 3
}
var c = new A()
console.log(b.n, b.m, c.n, c.m) //1 undefined 2 3
对比下面这个例子
var A = function() {
}
A.prototype.n = 1;
var b = new A()
A.prototype.n=2
A.prototype.m=3
var c = new A()
console.log(b.n, b.m, c.n, c.m) //2 3 2 3
函数声明提升,同名时,后面覆盖前面的定义
// 代码1
(function f() {
function f() {
return 1;
}
console.log(f()); //2
function f() {
return 2;
}
})();
// 代码2
(function f() {
function f() {
return 1;
}
function f() {
//这里的函数会覆盖前面定义的f函数
return 2;
}
console.log(f()); //2
})();
变量声明提升,相当于在if的前面写上var a;,而整个html文档的对象是window对象,所以a是在window对象里面的一个属性,所以输出为undefined
// 代码1 变量声明提升后的形式如代码2
if (!(a in window)) {
var a = 1;
}
console.log(a) //undefined
// 代码2
var a;
if (!(a in window)) {
a = 1;
}
console.log(a) //undefined
var声明的变量名和函数名一样,函数声明和变量声明会有提升,并且有顺序:变量声明 =》 函数声明
// 代码1 函数声明和变量声明提升之后的形式如代码2
function a() {
}
var a;
console.log(typeof a) //function 声明顺序:变量声明=》函数声明
a =10;
console.log(a); //10 ,这里的a是对函数a重新赋值,最后面a的类型为number
// 代码2
var a;
function a() {
}
console.log(typeof a) //function 声明顺序:变量声明=》函数声明
a =10;
console.log(a); //10 ,这里的a是对函数a重新赋值,最后面a的类型为number
// 代码3
var a;
function a() {
}
console.log(typeof a) //function 声明顺序:变量声明=》函数声明
a =10;
console.log(a); //10 ,这里的a是对函数a重新赋值,最后面a的类型为number
var声明的变量和函数参数名一样,声明顺序:变量声明 =》 函数参数
// 代码1 函数声明之后的代码如2所示
(function(b) {
console.log(b) //1
var b = c = 2
console.log(b) //2
})(1)
// 代码1
(function(b) {
var b;
console.log(b) //1
b = c = 2
console.log(b) //2
})(1)
var声明的变量、函数参数名、函数名一样,声明顺序:变量声明 =》参数声明 =》函数声明
// 代码1 提升后的形式如2
(function(b) {
console.log(b) //[Function: b]
var b = c = 2
console.log(b) // 2
function b() {
console.log('thie is b function');
}
console.log(b) //2
})(1)
// 代码2
(function(b) {
var b;
b = b; //右边的b相当于1,左边的b=1
function b() {
console.log('thie is b function');
}
console.log(b) //[Function: b]
b = c = 2;
console.log(b) // 2
console.log(b) //2
})(1)
传参和不传参的区别
// 代码1
var a = 1
function c(a, b) {
console.log(a) //undefined,因为c()执行的时候,并没有传参,函数中的形参值为undefined
a = 2
console.log(a) //2
}
c()
//代码2
var a = 1
function c(a, b) {
console.log(a) //undefined,因为c()执行的时候,并没有传参,函数中的形参值为undefined
a = 2
console.log(a) //2,这里的a是局部变量,不能影响到全局的同名的变量的值
}
c()
console.log(a) //1
// 代码3
var a = 1
function c(a, b) {
console.log(a) //1 //c(1)传进来一个参数1,作为a的值
a = 2
console.log(a) //2
}
c(1)
函数声明提升,同名,则后面的覆盖前面的
// 代码1 函数声明提升后的形式如代码2
function fn(){
function a(){
console.log(1)}
return a;
function a(){
console.log(2)}
}
fn()();//2
// 代码2
function fn(){
function a(){
console.log(1)}
function a(){
console.log(2)}
return a;
}
fn()();//2
变量声明提升
// 代码1 提升后的形式如代码2
var a=10;
function fn(){
//变量声明提升,a 赋值undefined,内部作用域存在a这个变量,所以这里 !a 就是 !undefined,就是true,进入函数a=20;
//但是后面的a怎么就是20呢,js没有块级作用域!! 不要小看js各种细节,够折腾的
if (!a) {
var a=20
}
console.log(a)// 这里是20 ,
}
fn() //20
// 代码2
var a=10;
function fn(){
//变量声明提升,a 赋值undefined,内部作用域存在a这个变量,所以这里 !a 就是 !undefined,就是true,进入函数a=20;
//但是后面的a怎么就是20呢,js没有块级作用域!! 不要小看js各种细节,够折腾的
var a;
if (!a) {
a=20;
}
console.log(a)// 这里是20 ,
}
fn() //20
如果将上面的改成
var a=10;
function fn(){
if (!a) {
a=20
}
console.log(a)// 这里是10 ,
}
fn() //10
理解逗号运算符,从左到右计算,返回最右边的结果
// 1
function count(){
return 1,2,3;
}
console.log(count()); //3
// 2
function count(){
return (1,2,3);
}
console.log(count()); //3
// 3
function count(){
return (1+1,2+3,3+1);
}
console.log(count()); //4
理解this的作用域;this始终指向程序当前正在使用的对象,this仅用于函数内
全局函数中的this:始终指向window对象
对象中的this:始终指向调用该方法的对象、
构造函数中的this:指向刚创建的新对象
全局函数中的this:始终指向window对象
// 代码1
// 全局方法中的this指向window
var a=100;
function fun(){
console.log(this.a);//window.a 100
}
fun();
// 代码2
var n=100;
function fun1(n){
// 函数传递的参数都是值传递的方式
n=10; //这里的赋值,只是对传递进来的参数进行赋值,并不影响外面的n的值
console.log(this.n);// this.n=window.n 100
}
fun1(n);
// 代码3
var n=100;
function fun1(n){
n++; // 同上解释
console.log(this.n); // this.n=window.n 100
}
fun1(n); //100
// 代码4
var n=100;
function fun1(n){
this.n++; // this.n=window.n
console.log(this.n);//this.n=window.n 101
}
fun1(n);
对象中的this:始终指向调用该方法的对象
// 代码1
var name="li ming";
var hmm ={
name:"zhang san",
show:function(){
console.log(this);//this=hmm这个对象,输出{name: "zhang san", show: ƒ}
}
};
hmm.show(); //{name: "zhang san", show: ƒ}
// 代码2
var name="li ming";
var hmm={
name:"zhang san",
show:function(){
console.log(this.name);//this.name = hmm.name,zhang san
}
};
hmm.show();//zhang san
// 代码3
var a=10;
var foo={
a:20,
bar:function(){
var a=30;
return this.a; //this.a=foo.a,20
}
}
console.log(foo.bar()); //20
构造函数中的this:指向刚创建的新对象
function Student(name){
this.name=name;
console.log(this); //this指向 new正在创建的对象 Student
}
var name1 = new Student("zhang san"); //Student {name: "zhang san"}
var name2 = new Student("li si"); //Student {name: "li si"}
var name="liming";
function Student(name){
this.name = name;
console.log(this.name); //this指向 new正在创建的对象 Student
}
var name1 = new Student("zhang san"); //zhang san
var name2 = new Student("li si"); //li si
理解下面这栗子
var a=10;
var foo = {
a: 20, //全局属性
bar: function () {
var a = 30; //局部变量,覆盖不了外面的a的值
return this.a; //this指向调用该方法的对象
}
};
console.log(
foo.bar(), // 20,foo调用bar函数,this指向foo,foo有一
//个对象a,所以this.a = foo.a
(foo.bar)(), // 20,foo.bar是匿名函数,用()调用该匿名函数
//,this指向这个匿名函数,但这个匿名函数仅限
//于foo里面的全局变量使用,此时this.a就是foo.a
(foo.bar = foo.bar)(), // 10 这里有'='赋值符号,赋值符号会改变this的指向,把右边的匿名函数重新赋值给左边的foo.bar,此时foo.bar整体相当于一个全局 变量,在用()调用,this指向全局的a,可以理解成var b= foo.bar;b()
(foo.bar, foo.bar)() // 10,逗号运算符,从左往右执行,返回最右边结果,同'=',会改变this的作用域,不太明白
);
代码1和代码2的区别,只是1中多了个与变量名同名的函数,由于函数声明会提升,所以函数a()的声明会在函数b()的最前面,然后才是给这个函数,重新赋值为10,在函数b()外面是访问不到b()里面定义的a
// 代码1
var a = 1;
function b(){
a = 10;
return;
function a(){
//函数声明会提升到b()的开头,提升后的代码形式如代码2
console.log(a);
}
}
b();
console.log(a); // 1
// 代码2
var a = 1;
function b(){
function a(){
console.log(a);
}
a = 10; //这里对a的赋值,其实是对b()函数里面声明的a赋值,
//因为,在执行赋值语句的时候,会由里向外查找,在
//函数b()里面找到a,所以就停止向函数b()外查找a,这里的a就相当于一个局部变量,函数b()外不能访问到该a
return;
}
b();
console.log(a); // 1,这里只能访问到函数b()外的a
// 代码3
var a = 1;
function b(){
a = 10; //在执行b()的时候,会由里向外查找a,结果在b()外找到a
//,所以这里是对函数b()外的a重新赋值
return;
// function a(){ //去掉函数a
// console.log(a);
// }
}
b();
console.log(a); //10
还是理解全局变量和局部变量
// 代码1
var a=b = 10; //b全局变量
(function(){
var b = 20; //重新定义一个b,b为局部变量,覆盖不了外面的b的值
})();
console.log(a); //10
console.log(b); //10 访问外部的b的值
// 代码2
var a=b = 10; //b全局变量
(function(){
b = 20; //对b重新赋值
})();
console.log(a); //10
console.log(b); //20
// 代码3
var b = 10;
(function(){
var a=b = 20; //a局部变量,b全局变量,这个b的值会覆盖外面b的值
})();
console.log(b); //20
console.log(a); //报错 ReferenceError: a is not defined
// 代码4
var a = 10;
var b =10;
(function(){
var a=20 //a b为局部变量,覆盖不了外面的a,b的值
var b = 20;
})();
console.log(b); //10 这里a和b都访问外部定义的值
console.log(a); //10
连续执行
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,b,c的输出分别是什么?
var a = fun(0); a.fun(1); a.fun(2); a.fun(3);
//undefined 0 0 0
var b = fun(0).fun(1).fun(2).fun(3);
//undefined 0 1 2
var c = fun(0).fun(1); c.fun(2); c.fun(3);
//undefined 0 1 1
理解js对象中的私有属性、公有属性、静态属性,this指向,函数声明和变量声明提升,运算符优先级
<!DOCTYPE html>
<html lang="en">
<body>
<script type="text/javascript">
var getName;
function Foo (){
getName = function (){
console.log(1);
};
return this;
}
function getName (){
console.log(5);
};
Foo.getName = function (){
console.log(2);
};
Foo.prototype.getName = function () {
console.log(3);
};
getName = function (){
console.log(4);
};
Foo.getName(); //2
getName(); //4
Foo().getName(); //1
getName(); //1
new Foo.getName(); //2
new Foo().getName(); //3
new new Foo().getName(); //3
</script>
</body>
</html>