作用域&变量提升&闭包&原型&继承__代码输出题
作者:互联网
1. 局部作用域中的意外全局变量
(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报错,因为没有定义。
2. 变量的作用域链
var a=3;
function c(){
alert(a);
}
(function(){
var a=4;
c();
})();
答案:3
解析:
js中变量的作用域链与定义时的环境有关,与执行时无关。执行环境只会改变this、传递的参数、全局变量等。
3. 变量提升/作用域
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,所以报错)。
4. 原型
// 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() 的实例。
5. 原型上的方法
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( )方法
6. this的指向、原型、原型链、类的继承、数据类型
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)。
标签:闭包,__,show,作用域,getName,Child,new,function,Foo 来源: https://www.cnblogs.com/fuct/p/16109068.html