作用域&变量提升&闭包&原型&继承__代码输出题

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

解析:

  1. Foo.getName(), Foo为一个函数对象,对象都可以有属性,b 处定义Foo的getName属性为函数,输出2;
  2. getName(), 这里看d、e处,d为函数表达式,e为函数声明,两者区别在于变量提升,函数声明的 5 会被后边函数表达式的 4 覆盖;
  3. Foo().getName(), 这里要看a处,在Foo内部将全局的getName重新赋值为 console.log(1) 的函数,执行Foo()返回 this,这个this指向window,Foo().getName() 即为window.getName(),输出 1;
  4. getName(), 上面3中,全局的getName已经被重新赋值,所以这里依然输出 1;
  5. new Foo.getName(), 这里等价于 new (Foo.getName()),先执行 Foo.getName(),输出 2,然后new一个实例;
  6. new Foo().getName(), 这 里等价于 (new Foo()).getName(), 先new一个Foo的实例,再执行这个实例的getName方法,但是这个实例本身没有这个方法,所以去原型链__protot__上边找,实例.protot === Foo.prototype,所以输出 3;
  7. 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

解析:

  1. f 是 对象,不是Function的实例
  2. 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

解析:

  1. parent.show(),可以直接获得所需的值;
  2. child1.show()Child的构造函数原本是指向Child的,题目显式将Child类的原型对象指向了Parent类的一个实例,需要注意Child.prototype指向的是Parent的实例,而不是指向Parent这个类。所以调用show方法,this.a自身有,其他属性从原型上获取。
  3. child2.show(),同上;
  4. parent.show()parent是一个Parent类的实例,Child.prorotype指向的是Parent类的另一个实例,两者在堆内存中互不影响,所以上述操作不影响parent变量,所以输出结果不变;
  5. child1.show()child1执行了change()方法后,发生了怎样的变化呢?
    • this.b.push(this.a),由于this的动态指向特性,this.b会指向Child.prototype上的b数组,this.a会指向child1a属性,所以Child.prototype.b变成了[1,2,1,11];
    • this.a = this.b.length,这条语句中this.athis.b的指向与上一句一致,故结果为child1.a变为4;
    • this.c.demo = this.a++,由于child1自身属性并没有c这个属性,所以此处的this.c会指向Child.prototype.cthis.a值为4,为原始类型,故赋值操作时会直接赋值,Child.prototype.c.demo的结果为4,而this.a随后自增为5(4 + + = 5)。
  6. child2执行了change()方法, 而child2child1均是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.athis.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)。
posted @ 2022-04-06 20:49  青柠i  阅读(27)  评论(0编辑  收藏  举报