题目:以下两种方式调用函数是,alert的结果是?
var foo=1;
function main(){
alert(foo);
var foo=2;
alert(this.foo);
this.foo=3;}
main(); //输出undefined,1
new main(); //输出undefined,undefined
先了解JavaScript的预编译:
function main(){ };
这种形式是声明一个函数,跟 var 一个变量的机制一样,脚本在解释执行之前会做预编译处理,
而 var main= function(){ };
这种形式是对一个变量赋值,虽然也做预编译,但仅仅只是给 main事先变量分配一个内存空间,而没有做初始化;
关于声明提升是指:js在解析的时候总是会将var, function这类关键词的声明语句提升至该作用域的最顶部(注意:这里只会提升声明部分)。
alert(typeof addB);
var addB = "variable";
function addB() {
alert("function addB");
}
alert(addB);
执行的结果是:function,variable;由此可看出,函数先被预编译处理,而后再是变量;
再了解下作用域链:
js在访问一个变量时会优先在该作用域(访问时的那个作用域)内寻找是否声明过该变量,如果该变量已经存在,则直接使用它的值,否则它会寻找该作用域的‘父作用域/上级作用域’,以此类推,直到找到全局作用域为止。
回到题目上,main函数中执行第一个alert(foo),先寻找foo,发现有定义变量foo,但是在alert之前并未赋值,所以代码等同如下:
var foo=1;
function main(){
var foo;
alert(foo);
foo=2;
alert(this.foo);
this.foo=3;}
main()
此时显而易见,第一个alert(foo)时,他会先在main函数中查找是否有foo变量,没有才回去上层的scope中查找。因为main函数中有声明foo变量,但是alert在他赋值之前,所以返回的是undefined;
第二个alert(this.foo),main()执行时,this指向的是window,所以this.foo是window的全局变量“1”;
关于this指向的问题:
-当函数作为一个对象属性被调用时,this指向的就是他的调用者,即那个对象;
-当函数直接被调用时,他的调用者其实就是window对象;
-当函数作为构造函数被执行时,他的this指向的是构造函数实例化出来的对象;
-我们还可以使用call/apply来改变默认的this指向。
故,执行 new main();
时,this.foo 为实例化对象实例的属性foo,而在构造函数main中,foo为定义成私有属性,故new main()并不能访问构造函数main的私有属性,所有返回undefined。
下面内容摘自豪情,个人感觉理解起来比较直接
//构造函数
//使自己的对象多次复制,同时实例根据设置的访问等级可以访问其内部的属性和方法
//当对象被实例化后,构造函数会立即执行它所包含的任何代码
function myObject(msg){
//特权属性(公有属性)
this.myMsg = msg; //只在被实例化后的实例中可调用
this.address = '上海';
//私有属性
var name = '豪情';
var age = 29;
var that = this;
//私有方法
function sayName(){
alert(that.name);
}
//特权方法(公有方法)
//能被外部公开访问
//这个方法每次实例化都要重新构造而prototype是原型共享,所有实例化后,都共同引用同一个
this.sayAge = function(){
alert(name); //在公有方法中可以访问私有成员
}
//私有和特权成员在函数的内部,在构造函数创建的每个实例中都会包含同样的私有和特权成员的副本,
//因而实例越多占用的内存越多
}
//公有方法
//适用于通过new关键字实例化的该对象的每个实例
//向prototype中添加成员将会把新方法添加到构造函数的底层中去
myObject.prototype.sayHello = function(){
alert('hello everyone!');
}
//静态属性
//适用于对象的特殊实例,就是作为Function对象实例的构造函数本身
myObject.name = 'china';
//静态方法
myObject.alertname = function(){
alert(this.name);
}
//实例化
var m1 = new myObject('111');
//---- 测试属性 ----//
//console.log(myObject.name); //china
//console.log(m1.name); //undefined, 静态属性不适用于一般实例
//console.log(m1.constructor.name); //china, 想访问类的静态属性,先访问该实例的构造函数,然后在访问该类静态属性
//console.log(myObject.address); //undefined, myObject中的this指的不是函数本身,而是调用address的对象,而且只能是对象
//console.log(m1.address); //上海 此时this指的是实例化后的m1
//---- 测试方法 ----//
//myObject.alertname(); //china,直接调用函数的类方法
//m1.alertname(); //FF: m1.alertname is not a function, alertname 是myObject类的方法,和实例对象没有直接关系
//m1.constructor.alertname(); //china, 调用该对象构造函数(类函数)的方法(函数)
//m1.sayHello(); //hello everyone, myObject类的prototype原型下的方法将会被实例继承
//myObject.sayHello(); //myObject.sayHello is not a function,sayHello是原型方法,不是类的方法
//---- 测试prototype ----//
//console.log(m1.prototype); //undefined, 实例对象没有prototype
//console.log(myObject.prototype); //Object
//alert(myObject.prototype.constructor); //console.log返回myObject(msg),此时alert()更清楚,相当于myObject
//console.log(myObject.prototype.constructor.name); //china, 相当于myObject.name;
相关案例:
a = 1 // 1
this.a // 1
window.a // 1
delete a // true
var b = 2// 2
this.b // 2
window.b // 2
delete b // false
从例子看以看出,未通过var声明的变量a和通过var声明的变量b,都可以通过this和window访问到,区别是一个可以被delete,一个不可以,下面分析下:
因为(在全局下)通过var 声明的变量是全局变量,而未通过var声明的变量是附在this,也就是window上的属性,而属性是可以被删除的。
在浏览器中,所以的变量都是存在于window下的,所以可以被window访问到;
而this是指向window的一个引用,未通过var声明的变量通过附在this上,达到可以全局访问的效果。