通常分析作用域链,要结合变量声明提升,看看下面几个题,把你绕晕没?
其中问题7最经典,如果你能明白,说明原型链你掌握的差不多了。
// --------------------1-----------------
function foo() {
var num = 123;
console.log(num); //?
}
foo();
console.log(num); //?
//提升后的代码
function foo() {
var num;
num = 123;
console.log(num); //?123
}
foo();
console.log(num); //?num is not defined
//is not defined 没有定义
//undefined 定义了没有赋值
// --------------------2-----------------
var scope = "global";
foo();
function foo() {
console.log(scope); //?
var scope = "local";
console.log(scope); //?
}
//提升后的代码
var scope;
function foo(){
var scope;
console.log(scope); //?undefined
scope = "local";
console.log(scope); //?local
}
scope = "global";
foo();
// --------------------3-----------------
//in 关键字 判断某个对象中是否有某个属性
function f1(){
if("a" in window){
var a = 10;
}
alert(a); //undefined
}
f1();
// --------------------4-----------------
if("a" in window){
var a = 10;
}
alert(a);
//预解析
var a;
if("a" in window){
a = 10;
}
alert(a); // 10
// --------------------5-----------------
if(!"a" in window){
var a = 10;
}
alert(a);
//预解析
var a;
if(!"a" in window){
a = 10;
}
alert(a); // ?undefined
// --------------------6-----------------
var foo = 1;
function bar() {
if(!foo) {
var foo = 10;
}
alert(foo); //??10
}
bar();
//提升后的代码
var foo;
function bar(){
var foo;
if(!foo) {
foo = 10;
}
alert(foo); //??10
}
foo = 1;
bar();
// --------------------7-----------------
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(); // ? alert 2
getName(); // ? 4
Foo().getName(); // ? 1
getName(); // ? 4 //1
new Foo.getName(); // ? 2 //先执行Foo.getName(),再new。new 2,还是2
(new Foo).getName(); //
new Foo().getName(); // ? 3 //先创建Foo对象,在调用函数
new new Foo().getName(); // ? 3 //先创建对象,调用函数,再new
//等价于
function Foo() {
getName = function(){ alert(1); };
return this;
}
var getName;
function getName(){ alert(5); }
Foo.getName = function() { alert(2); };
Foo.prototype.getName = function(){ alert(3); };
getName = function() { alert(4); };
Foo.getName(); // ? alert 2
getName(); // ? 4
Foo().getName(); // ? 1
getName(); // ? 4 //1
new Foo.getName(); // ? 2 //先执行Foo.getName(),再new。new 2,还是2
(new Foo).getName(); //3
new Foo().getName(); // ? 3 //先创建Foo对象,在调用函数
new new Foo().getName(); // ? 3 //先创建对象,调用函数,再new
知识点:局部变量声明提前后不会覆盖原来的全局变量
例1 :参数是引用参数
var foo={n:1};
(function (foo) {
console.log(foo.n);
foo.n=3;
var foo={n:2};
console.log(foo.n);
})(foo);
console.log(foo.n);
解析:变量提升以后:(局部变量声明提前后不会覆盖原来的全局变量)
var foo = {n:1};
(function(foo){ //形参foo同实参foo一样指向同一片内存空间,这个空间里的n的值为1
var foo; //局部变量声明提前后不会覆盖原来的全局变量,赋值才会覆盖
console.log(foo.n); //输出1
foo.n = 3; //形参与实参foo指向的内存空间里的n的值被改为3
foo = {n:2}; //形参foo指向了新的内存空间,里面n的值为2.
console.log(foo.n); //输出新的内存空间的n的值
})(foo);
console.log(foo.n); //实参foo的指向还是原来的内存空间,里面的n的值为3.
//所以答案为: 1 2 3
例2: 参数是传值参数
var foo=1;
(function (foo) {
console.log(foo);
foo=3;
var foo=2;
console.log(foo);
})(foo);
console.log(foo);
//答案为1 2 1