看代码写输出题目归纳(置顶)

loading 2022年11月11日 86次浏览

1.

function Foo(){
    getName = function(){
        console.log(1);
    };
    return this;
} 
Foo.getName = function(){
    console.log(2);
}
Foo.prototype.getName = function(){
    console.log(3);
}
var getName = function(){
    console.log(4);
}
function getName(){
    console.log(5);
}

Foo.getName();//2
getName();//4
Foo().getName();//1
getName();//1
new Foo.getName()//2
new Foo().getName();//3

逐行分析:

  1. Foo.getName() 直接调用定义在Foo()外部的静态方法,输出2。

  2. function getName()这种定义方式会导致变量提升 提前声明后会被var getName = function()这种方式覆盖,输出4。

  3. 运行Foo().getName()会覆盖掉之前的getName(),输出1。
    首先要知道Foo()中的getName前没有var,所以该getName()为全局变量。
    因此: 此时的Foo()内部的this指向window,而this.getName也就是输出1的这个getName,再return this使得window下的getName变成这个输出1的getName

  4. 由于第三步已经将window中的getName覆盖成了Foo()中的getName,因此继续输出1。

  5. new Foo.getName()相当于new (Foo.getName()),先输出2,再创建Foo.getName()这个方法的实例。

  6. new Foo().getName()相当于(new Foo()).getName(),先创建了一个Foo()的实例,实例中找不到对应方法(因为Foo()中是getName()而非this.getName())则到原型上去找,输出3。

2.

        window.name = 'Bytedance';
        function A(){
            this.name = 123;
        }
        A.prototype.getA = function(){
            return this.name+1;
        }
        let a = new A();
        let fnA = a.getA;
        console.log(fnA());//Bytedance1
        console.log(a.getA());//124

首先要记住的一个点:this永远指向最后调用它的那个对象。

那么
fnA = a.getA , fnA()
a.getA()
到底有什么区别呢?

a.getA的作用:从a里面找到getA这个方法。
a.getA()的作用:从a里面找到getA这个方法,并把a作为getA()的this,然后执行这个方法。
因此,fnA = a.getA , fnA()这种分开定义和执行两个步骤的写法会导致失去了指定this这个步骤。

因此题目中let fnA = a.getA 只是找到了return this.name+1这个方法,接下来调用时的this指向window,因此输出ByteDance1。
而a.getA()将this指向了A(),因此输出124。

3.

	window.name = 'Bytedance'
        class A {
            constructor(){
                this.name = 123;
            }
            getA(){
                console.log(this);
                return this.name+1;
            }
        }
        let a = new A();
        let fnA = a.getA;
        console.log(fnA());
	//undefined 
	//报错Cannot read properties of undefined (reading 'name')

在class A中,类内部的方法为严格模式,使得this的指向为undefined,因此在undefined上找name属性自然会报错

	window.name = 'Bytedance'
        var B = function(){
            this.name = 123;
        }
        B.prototype.getB = function(){
            console.log(this);
            return this.name+1;
        }
        let b = new B();
        let fnB = b.getB;
        console.log(fnB());
	//Window{...}
	//Bytedance1

在构造函数B中,同第二题中的例子,先let fnB = b.getB再执行fnB()使得函数的this指向window,因此输出Window和Bytedance1。

4.

    <script>
        function exeuctor(handler) {
            handler()
        }
        const obj = {
            count: 0,
            inc: function () {
                console.log(this) // window
                this.count++
            }
        }
        exeuctor(obj.inc)
        console.log(obj.count) //0
    </script>

如果直接执行obj.inc()那么是可以正确让this指向obj从而输出1的,但是现在在外面又套了一层函数,因此this就指向window了。

要想让this正确指向此处需要用bind(不能用call和apply,因为bind才能实现不让函数立即执行,而是被调用了才执行)

exeuctor(obj.inc.bind(obj))

5.

var a = 1;
function printA(){
  console.log(this.a);
}

var obj = {
  a: 2,
  foo: printA,
  bar: function() {
    printA();
  }
};

obj.foo(); // 2
obj.bar(); // 1
var foo = obj.foo;
foo(); // 1

6.

function foo(something) {
  this.a = something;
}

var obj1 = {
  foo: foo
};

var obj2 = {};

obj1.foo(2);
console.log(obj1.a); // 2

obj1.foo.call(obj2, 3);
console.log(obj2.a); // 3

var bar = new obj1.foo(4);
console.log(obj1.a); // 2
console.log(bar.a); // 4

谨记new绑定 > 显式绑定(call/apply/bind)> 隐式绑定(对象调用)>默认绑定(直接调用函数 指向window)

7.

(function(){
  var x = y = 1;
})();
var z;
console.log(y); // 1
console.log(z); // undefined
console.log(x); // Uncaught ReferenceError: x is not defined

这里的关键点是理解"var x = y = 1"这句代码是从右往左执行的,也就相当于先在全局作用域中声明了y = 1,然后再在局部作用域中声明了var x = y。

8.

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(); // 1 [1, 2, 1] 5
child1.show(); // 11 [1, 2, 1] 5
child2.show(); // 12 [1, 2, 1] 5

child1.change();
child2.change();

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.a重写成了11,因此把this.a改成11,但是this.b中的this.a初始化时就已经确定,所以不变。
  3. 同上,只是this.a改成了12。
  4. child1和child2的change影响不到parent,因此还是输出一样的内容。
  5. 重点两行来了,关键点在于记得this.b和this.c都是引用类型,内容在所有实例之间共享,先看child1.show():
    5.1 为什么被push进b的是11不是2?因为2是在new的时候被赋值的,之后被重写成11了,因此首先this.b变成[1, 2, 1, 11]
    5.2 this.a变成this.b.length = 4
    5.3 this.c.demo = this.a++,那么demo也变成4,而a++之后,this.a变成5
    5.4 因此只看child1.change()的话,此时child1.show应该是5 [1, 2, 1, 11] 4
  6. 再考虑上child2.change()
    6.1 this.b把12也push进去,因此最终this.b就是[1, 2, 1, 11, 12]
    6.2 this.a变成this.b.length = 5
    6.3 this.c.demo = this.a++,那么demo也变成5,而a++之后,this.a变成6

那么最终就得到了注释中的结果。