原型&继承题目及内容解答

javascript 文章 2022-07-15 14:28 0 全屏看文

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

1. 代码输出结果

function Person(name) {    this.name = name}var p2 = new Person('king');console.log(p2.__proto__) //Person.prototypeconsole.log(p2.__proto__.__proto__) //Object.prototypeconsole.log(p2.__proto__.__proto__.__proto__) // nullconsole.log(p2.__proto__.__proto__.__proto__.__proto__)/ull后面没有了,报错console.log(p2.__proto__.__proto__.__proto__.__proto__.__proto__)/ull后面没有了,报错console.log(p2.constructor)//Personconsole.log(p2.prototype)//undefined p2是实例,没有prototype属性console.log(Person.constructor)//Function 一个空函数console.log(Person.prototype)//打印出Person.prototype这个对象里所有的方法和属性console.log(Person.prototype.constructor)//Personconsole.log(Person.prototype.__proto__)// Object.prototypeconsole.log(Person.__proto__) //Function.prototypeconsole.log(Function.prototype.__proto__)//Object.prototypeconsole.log(Function.__proto__)//Function.prototypeconsole.log(Object.__proto__)//Function.prototypeconsole.log(Object.prototype.__proto__)/ull

这道义题目考察原型、原型链的基础,记住就可以了。

2. 代码输出结果

// afunction Foo () { getName = function () {   console.log(1); } return this;}// bFoo.getName = function () { console.log(2);}// cFoo.prototype.getName = function () { console.log(3);}// dvar getName = function () { console.log(4);}// efunction getName () { console.log(5);}Foo.getName();           // 2getName();  // 4Foo().getName();         // 1getName();  // 1 new Foo.getName();       // 2new Foo().getName();     // 3new new Foo().getName(); // 3

输出结果: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() 的实例。

3. 代码输出结果

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()

输出结果:

aUncaught TypeError: f.b is not a functionab

解析:

  1. f 并不是 Function 的实例,因为它本来就不是构造函数,调用的是 Function 原型链上的相关属性和方法,只能访问到 Object 原型链。所以 f.a() 输出 a  ,而 f.b() 就报错了。
  2. F 是个构造函数,而 F 是构造函数 Function 的一个实例。因为 F instanceof  Object === true,F instanceof Function === true,由此可以得出结论:F 是 Object 和 Function 两个的实例,即 F 能访问到 a, 也能访问到 b。所以 F.a() 输出 a ,F.b() 输出 b。

4. 代码输出结果

function Foo(){    Foo.a = function(){        console.log(1);    }    this.a = function(){        console.log(2)    }}Foo.prototype.a = function(){    console.log(3);}Foo.a = function(){    console.log(4);}Foo.a();let obj = new Foo();obj.a();Foo.a();

输出结果:4 2 1

解析:

  1. Foo.a() 这个是调用 Foo 函数的静态方法 a,虽然 Foo 中有优先级更高的属性方法 a,但 Foo 此时没有被调用,所以此时输出 Foo 的静态方法 a 的结果:4
  2. let obj = new Foo(); 使用了 new 方法调用了函数,返回了函数实例对象,此时 Foo 函数内部的属性方法初始化,原型链建立。
  3.  obj.a() ; 调用 obj 实例上的方法 a,该实例上目前有两个 a 方法:一个是内部属性方法,另一个是原型上的方法。当这两者都存在时,首先查找 ownProperty ,如果没有才去原型链上找,所以调用实例上的 a 输出:2
  4. Foo.a() ; 根据第2步可知 Foo 函数内部的属性方法已初始化,覆盖了同名的静态方法,所以输出:1

5. 代码输出结果

function Dog() {  this.name = 'puppy'}Dog.prototype.bark = () => {  console.log('woof!woof!')}const dog = new Dog()console.log(Dog.prototype.constructor === Dog && dog.constructor === Dog && dog instanceof Dog)

输出结果:true

解析:

因为constructor是prototype上的属性,所以dog.constructor实际上就是指向Dog.prototype.constructor;constructor属性指向构造函数。instanceof而实际检测的是类型是否在实例的原型链上。

constructor是prototype上的属性,这一点很容易被忽略掉。constructor和instanceof 的作用是不同的,感性地来说,constructor的限制比较严格,它只能严格对比对象的构造函数是不是指定的值;而instanceof比较松散,只要检测的类型在原型链上,就会返回true。

6. 代码输出结果

var A = {n: 4399};var B =  function(){this.n = 9999};var C =  function(){var n = 8888};B.prototype = A;C.prototype = A;var b = new B();var c = new C();A.n++console.log(b.n);console.log(c.n);

输出结果:9999 4400

 

解析:

  1. console.log(b.n),在查找b.n是首先查找 b 对象自身有没有 n 属性,如果没有会去原型(prototype)上查找,当执行var b = new B()时,函数内部this.n=9999(此时this指向 b) 返回b对象,b对象有自身的n属性,所以返回 9999。
  2. console.log(c.n),同理,当执行var c = new C()时,c对象没有自身的n属性,向上查找,找到原型 (prototype)上的 n 属性,因为 A.n++(此时对象A中的n为4400), 所以返回4400。

7. 代码输出问题

function A(){}function B(a){  this.a = a;}function C(a){  if(a){this.a = a;  }}A.prototype.a = 1;B.prototype.a = 1;C.prototype.a = 1; console.log(new A().a);console.log(new B().a);console.log(new C(2).a);

输出结果:1 undefined 2

解析:

  1. console.log(new A().a),new A()为构造函数创建的对象,本身没有a属性,所以向它的原型去找,发现原型的a属性的属性值为1,故该输出值为1;
  2. console.log(new B().a),ew B()为构造函数创建的对象,该构造函数有参数a,但该对象没有传参,故该输出值为undefined;
  3. console.log(new C(2).a),new C()为构造函数创建的对象,该构造函数有参数a,且传的实参为2,执行函数内部,发现if为真,执行this.a = 2,故属性a的值为2。

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();child1.show();child2.show();child1.change();child2.change();parent.show();child1.show();child2.show();

输出结果:

parent.show(); // 1  [1,2,1] 5child1.show(); // 11 [1,2,1] 5child2.show(); // 12 [1,2,1] 5parent.show(); // 1 [1,2,1] 5child1.show(); // 5 [1,2,1,11,12] 5child2.show(); // 6 [1,2,1,11,12] 5

这道题目值得神帝,他涉及到的知识点很多,例如this的指向、原型、原型链、类的继承、数据类型等。

解析

  1. parent.show(),可以直接获得所需的值,没啥好说的;
  2. child1.show(),Child的构造函数原本是指向Child的,题目显式将Child类的原型对象指向了Parent类的一个实例,需要注意Child.prototype指向的是Parent的实例parent,而不是指向Parent这个类。
  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 + 1 = 5)。
  1. child2执行了change()方法, 而child2child1均是Child类的实例,所以他们的原型链指向同一个原型对象Child.prototype,也就是同一个parent实例,所以child2.change()中所有影响到原型对象的语句都会影响child1的最终输出结果。
  • this.b.push(this.a),由于this的动态指向特性,this.b会指向Child.prototype上的b数组,this.a会指向child2a属性,所以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自身属性并没有c这个属性,所以此处的this.c会指向Child.prototype.c,故执行结果为Child.prototype.c.demo的值变为child2.a的值5,而child2.a最终自增为6(5 + 1 = 6)。

9. 代码输出结果

function SuperType(){    this.property = true;}SuperType.prototype.getSuperValue = function(){    return this.property;};function SubType(){    this.subproperty = false;}SubType.prototype = new SuperType();SubType.prototype.getSubValue = function (){    return this.subproperty;};var instance = new SubType();console.log(instance.getSuperValue());

输出结果:true

实际上,这段代码就是在实现原型链继承,SubType继承了SuperType,本质是重写了SubType的原型对象,代之以一个新类型的实例。SubType的原型被重写了,所以instance.constructor指向的是SuperType。具体如下:

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 

-EOF-