(function(){ var x = y = 1; })(); var z; console.log(y); console.log(z); console.log(x);
答案:1,undefined,报错 Uncaught ReferenceError: x is not defined
解析:
var x = y = 1;从右向左执行,y = 1,因为没有声明变量所以是全局window上的变量。而var x = 1;是自执行函数内的局部变量。所以最后输出x报错,因为没有定义。
var a=3; function c(){ alert(a); } (function(){ var a=4; c(); })();
答案:3
解析:
js中变量的作用域链与定义时的环境有关,与执行时无关。执行环境只会改变this、传递的参数、全局变量等。
var a = 10; (function () { console.log(a); a = 5; console.log(window.a); console.log(a); var a = 20;// 如果var a=20改成let a = 20;打印什么 console.log(a); })()
答案:undefined、10、5、20;
如果改成 let a = 20;
答案:直接报错,let 声明 有暂时性死区(其实let声明提前了,但是不能用a,所以报错)。
// a function Foo () { getName = function () { console.log(1); } return this; } // b Foo.getName = function () { console.log(2); } // c Foo.prototype.getName = function () { console.log(3); } // d var getName = function () { console.log(4); } // e function getName () { console.log(5); } Foo.getName(); getName(); Foo().getName(); getName(); new Foo.getName(); new Foo().getName(); new new Foo().getName();
结果:2、4、1、1、2、3、3
解析:
- Foo.getName(), Foo为一个函数对象,对象都可以有属性,b 处定义Foo的getName属性为函数,输出2;
- getName(), 这里看d、e处,d为函数表达式,e为函数声明,两者区别在于变量提升,函数声明的 5 会被后边函数表达式的 4 覆盖;
- Foo().getName(), 这里要看a处,在Foo内部将全局的getName重新赋值为 console.log(1) 的函数,执行Foo()返回 this,这个this指向window,Foo().getName() 即为window.getName(),输出 1;
- getName(), 上面3中,全局的getName已经被重新赋值,所以这里依然输出 1;
- new Foo.getName(), 这里等价于 new (Foo.getName()),先执行 Foo.getName(),输出 2,然后new一个实例;
- new Foo().getName(), 这 里等价于 (new Foo()).getName(), 先new一个Foo的实例,再执行这个实例的getName方法,但是这个实例本身没有这个方法,所以去原型链__protot__上边找,实例.protot === Foo.prototype,所以输出 3;
- new new Foo().getName(), 这里等价于new (new Foo().getName()),如上述6,先输出 3,然后new 一个 new Foo().getName() 的实例。
var F = function() {}; Object.prototype.a = function() { console.log('a'); }; Function.prototype.b = function() { console.log('b'); } var f = new F(); f.a(); f.b(); F.a(); F.b()
结果:a,报错Uncaught TypeError: f.b is not a function,a,b
解析:
- f 是 对象,不是Function的实例
- F 为构造函数,可以访问原型上的b( )方法
function Parent() { this.a = 1; this.b = [1, 2, this.a]; this.c = { demo: 5 }; this.show = function () { console.log(this.a , this.b , this.c.demo ); } } function Child() { this.a = 2; this.change = function () { this.b.push(this.a); this.a = this.b.length; this.c.demo = this.a++; } } Child.prototype = new Parent(); var parent = new Parent(); var child1 = new Child(); var child2 = new Child(); child1.a = 11; child2.a = 12; parent.show(); child1.show(); child2.show(); child1.change(); child2.change(); parent.show(); child1.show(); child2.show();
结果:
parent.show(); // 1 [1,2,1] 5 child1.show(); // 11 [1,2,1] 5 child2.show(); // 12 [1,2,1] 5 parent.show(); // 1 [1,2,1] 5 child1.show(); // 5 [1,2,1,11,12] 5 child2.show(); // 6 [1,2,1,11,12] 5解析:
parent.show()
,可以直接获得所需的值;child1.show()
,Child
的构造函数原本是指向Child
的,题目显式将Child
类的原型对象指向了Parent
类的一个实例,需要注意Child.prototype
指向的是Parent
的实例,而不是指向Parent
这个类。所以调用show
方法,this.a
自身有,其他属性从原型上获取。child2.show()
,同上;parent.show()
,parent
是一个Parent
类的实例,Child.prorotype
指向的是Parent
类的另一个实例,两者在堆内存中互不影响,所以上述操作不影响parent
变量,所以输出结果不变;child1.show()
,child1
执行了change()
方法后,发生了怎样的变化呢?
this.b.push(this.a)
,由于this的动态指向特性,this.b会指向Child.prototype
上的b数组,this.a会指向child1
的a属性,所以Child.prototype.b
变成了[1,2,1,11]
;this.a = this.b.length
,这条语句中this.a
和this.b
的指向与上一句一致,故结果为child1.a
变为4;this.c.demo = this.a++
,由于child1
自身属性并没有c这个属性,所以此处的this.c
会指向Child.prototype.c
,this.a
值为4,为原始类型,故赋值操作时会直接赋值,Child.prototype.c.demo
的结果为4,而this.a
随后自增为5(4 + + = 5)。child2
执行了change()
方法, 而child2
和child1
均是Child
类的实例,所以他们的原型链指向同一个原型对象Child.prototype
,也就是同一个parent
实例,所以child2.change()
中所有影响到原型对象的语句都会影响child1
的最终输出结果。
- this.b.push(this.a),由于this的动态指向特性,this.b会指向
Child.prototype
上的b数组,this.a会指向child2
的a属性,所以Child.prototype.b
变成了[1,2,1,11,12]
;- this.a = this.b.length,这条语句中
this.a
和this.b
的指向与上一句一致,故结果为child2.a
变为5;- this.c.demo = this.a++,由于
child2
自身属性并没有这个属性,所以此处的this.c
会指向Child.prototype.c
,故执行结果为Child.prototype.c.demo
的值变为child2.a
的值5,而child2.a
最终自增为6(5 + 1 = 6)。