前言
JavaScript 中的原型链是理解继承和对象行为的关键概念。本篇文章通过大量题目,帮助你逐步消除对原型链的恐惧,深入理解其原理和应用。题目难度交错,适合初学者和有一定基础的开发者。目标是读完并答对全部问题,像闯关一样逐步攻克原型链难题。
题目解析
题目 1
function Parent() {}
const p1 = new Parent();
console.log(p1.__proto__ === Parent.prototype); // true
解析:p1 是通过 Parent 构造函数创建的实例,p1.__proto__ 指向 Parent.prototype。
题目 2
function Parent() {}
const p1 = new Parent();
console.log(Parent.prototype.__proto__ === Object.prototype); // true
解析:Parent.prototype 是一个对象,它的 __proto__ 指向 Object.prototype。
题目 3
function Parent() {}
const p1 = new Parent();
console.log(p1.constructor === Parent); // true
解析:p1 的构造函数是 Parent,p1.constructor 指向 Parent。
题目 4
function Parent() {}
Parent.prototype.name = "jjq";
const p1 = new Parent();
console.log(p1.name); // jjq
解析:p1 可以访问 Parent.prototype 上的属性 name。
题目 5
function Parent() {}
Parent.prototype.name = "jjq";
const p1 = new Parent();
p1.name = "jiangjianqing";
console.log(p1.name); // jiangjianqing
解析:p1 自身的属性 name 会覆盖原型链上的同名属性。
题目 6
function Parent() {}
Parent.prototype.getName = function () {
return "jjq";
};
const p1 = new Parent();
console.log(p1.getName()); // jjq
解析:p1 可以访问 Parent.prototype 上的方法 getName。
题目 7
function Parent() {}
Parent.prototype.getName = function () {
return "jjq";
};
const p1 = new Parent();
p1.getName = function () {
return "jiangjianqing";
};
console.log(p1.getName()); // jiangjianqing
解析:p1 自身的方法 getName 会覆盖原型链上的同名方法。
题目 8
function Parent() {}
Parent.prototype.getName = function () {
return "jjq";
};
const p1 = new Parent();
console.log(p1.getName === Parent.prototype.getName); // true
解析:p1.getName 和 Parent.prototype.getName 指向同一个方法。
题目 9
function Parent() {}
Parent.prototype.getName = function () {
return "jjq";
};
const p1 = new Parent();
p1.getName = function () {
return "jiangjianqing";
};
console.log(p1.getName === Parent.prototype.getName); // false
解析:p1.getName 和 Parent.prototype.getName 指向不同的方法。
题目 10
function Parent() {}
Parent.prototype.getName = function () {
return "jjq";
};
const p1 = new Parent();
console.log(p1.hasOwnProperty("getName")); // false
解析:p1 自身没有 getName 属性,hasOwnProperty 返回 false。
题目 11
function Parent() {}
Parent.prototype.getName = function () {
return "jjq";
};
const p1 = new Parent();
p1.getName = function () {
return "jiangjianqing";
};
console.log(p1.hasOwnProperty("getName")); // true
解析:p1 自身有 getName 属性,hasOwnProperty 返回 true。
题目 12
function Parent() {}
Parent.prototype.getName = function () {
return "jjq";
};
const p1 = new Parent();
console.log(p1.__proto__.getName === Parent.prototype.getName); // true
解析:p1.__proto__ 指向 Parent.prototype,p1.__proto__.getName 和 Parent.prototype.getName 指向同一个方法。
题目 13
function Parent() {}
Parent.prototype.getName = function () {
return "jjq";
};
const p1 = new Parent();
console.log(p1.__proto__.constructor === Parent); // true
解析:p1.__proto__ 指向 Parent.prototype,Parent.prototype.constructor 指向 Parent。
题目 14
function Parent() {}
Parent.prototype.getName = function () {
return "jjq";
};
const p1 = new Parent();
console.log(p1.__proto__.constructor === Function); // false
解析:p1.__proto__.constructor 指向 Parent,而不是 Function。
题目 15
function Parent() {}
Parent.prototype.getName = function () {
return "jjq";
};
const p1 = new Parent();
console.log(p1.__proto__.__proto__ === Object.prototype); // true
解析:p1.__proto__ 指向 Parent.prototype,Parent.prototype.__proto__ 指向 Object.prototype。
题目 16
function Parent() {}
Parent.prototype.getName = function () {
return "jjq";
};
const p1 = new Parent();
console.log(p1.__proto__.__proto__.constructor === Function); // false
解析:p1.__proto__.__proto__.constructor 指向 Object,而 Object !== Function,因此结果为 false。
题目 17
function Parent() {}
Parent.prototype.getName = function () {
return "jjq";
};
const p1 = new Parent();
console.log(p1.__proto__.__proto__.__proto__); // null
解析:Object.prototype.__proto__ 是 null,表示原型链的终点。
题目 18
function Parent() {}
Parent.prototype.getName = function () {
return "jjq";
};
function Child() {}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
const c1 = new Child();
console.log(c1.__proto__.hasOwnProperty("getName")); // false
console.log(c1.__proto__.__proto__.hasOwnProperty("getName")); // true
解析:c1.__proto__ 指向 Child.prototype,而 Child.prototype 是通过 Object.create(Parent.prototype) 创建的,它自身并没有 getName 属性,所以 c1.__proto__.hasOwnProperty("getName") 返回 false。
但是 c1.__proto__.__proto__ 指向 Parent.prototype,而 Parent.prototype 自身有 getName 属性,所以 c1.__proto__.__proto__.hasOwnProperty("getName") 返回 true。
题目 19
function Parent() {}
Parent.prototype.getName = function () {
return "jjq";
};
const p1 = new Parent();
console.log(p1.__proto__.hasOwnProperty("constructor")); // true
解析:p1.__proto__ 指向 Parent.prototype,Parent.prototype 自身有 constructor 属性。
题目 20
function Parent() {}
Parent.prototype.getName = function () {
return "jjq";
};
const p1 = new Parent();
console.log(p1.__proto__.__proto__.hasOwnProperty("toString")); // true
解析:p1.__proto__.__proto__ 指向 Object.prototype,Object.prototype 自身有 toString 方法。
题目 21
function Parent() {}
Parent.prototype.getName = function () {
return "jjq";
};
const p1 = new Parent();
console.log(p1.toString()); // [object Object]
解析:p1 没有自身的 toString 方法,会沿着原型链找到 Object.prototype.toString。
题目 22
function Parent() {}
Parent.prototype.getName = function () {
return "jjq";
};
const p1 = new Parent();
console.log(p1.__proto__.__proto__.toString === Object.prototype.toString); // true
解析:p1.__proto__.__proto__ 指向 Object.prototype,Object.prototype.toString 是 toString 方法的来源。
题目 23
function Parent() {}
Parent.prototype.getName = function () {
return "jjq";
};
const p1 = new Parent();
console.log(p1.__proto__.__proto__.hasOwnProperty("hasOwnProperty")); // true
解析:p1.__proto__.__proto__ 指向 Object.prototype,Object.prototype 自身有 hasOwnProperty 方法。
题目 24
function Parent() {}
Parent.prototype.getName = function () {
return "jjq";
};
function Child() {}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
Child.prototype.getName = function () {
return "child";
};
const c1 = new Child();
console.log(c1.getName()); // "child"
console.log(c1.__proto__.hasOwnProperty("getName")); // true
console.log(c1.__proto__.__proto__.hasOwnProperty("getName")); // true
解析:
c1.getName()调用时,会先在c1自身查找getName属性,没有找到,然后沿着原型链查找,先找到Child.prototype上的getName方法,所以返回"child"。c1.__proto__.hasOwnProperty("getName")返回true,因为c1.__proto__指向Child.prototype,而Child.prototype自身有getName属性。c1.__proto__.__proto__.hasOwnProperty("getName")也返回true,因为c1.__proto__.__proto__指向Parent.prototype,而Parent.prototype自身也有getName属性。
题目 25
function Parent() {}
Parent.prototype.name = "Parent";
function Child() {}
Child.prototype = Object.create(Parent.prototype);
const c1 = new Child();
console.log(c1.constructor === Parent); // true
console.log(c1.constructor === Child); // false
解析:因为 Child.prototype 是通过 Object.create(Parent.prototype) 创建的,所以它继承了 Parent.prototype 的属性,包括 constructor,因此 c1.constructor 默认指向 Parent,而不是 Child。
题目 26
function Parent() {}
const p1 = new Parent();
console.log(Parent.prototype.__proto__ === Object.prototype); // true
解析:Parent.prototype 是一个对象,它的 __proto__ 指向 Object.prototype。
题目 27
function Parent() {}
const p1 = new Parent();
console.log(p1.constructor === Parent); // true
解析:p1 的构造函数是 Parent,p1.constructor 指向 Parent。
相信自己的判断即可,不去思考题目的难易
题目 28
function Parent() {}
Parent.prototype.name = "jjq";
const p1 = new Parent();
p1.name = "jiangjianqing";
console.log(p1.name); // jiangjianqing
解析:p1 自身的属性 name 会覆盖原型链上的同名属性。
相信自己的判断即可,不去思考题目的难易
题目 29
function Parent() {}
Parent.prototype.getName = function () {
return "jjq";
};
const p1 = new Parent();
console.log(p1.getName()); // jjq
解析:p1 可以访问 Parent.prototype 上的方法 getName。
题目 30
function Parent() {
this.name = "Parent";
}
Parent.prototype.getName = function () {
return this.name;
};
function Child() {
this.name = "Child";
}
Child.prototype = new Parent();
const child = new Child();
console.log(child.getName()); // 输出结果1
console.log(child.name); // 输出结果2
Child.prototype.name = "Prototype";
console.log(child.getName()); // 输出结果3
console.log(child.name); // 输出结果4
解析:
-
创建 Parent 和 Child 构造函数:
Parent构造函数有一个属性name和一个方法getName。Child构造函数有一个属性name。Child.prototype被设置为new Parent(),这意味着Child的原型对象是一个Parent实例。
-
创建 child 实例:
-
child是通过new Child()创建的实例。 -
child自身有一个属性name,值为"Child"。 -
child的原型链是:child->Child.prototype(一个Parent实例)->Parent.prototype->Object.prototype->null。
-
-
第一次调用
child.getName():child.getName()会先在child自身查找getName方法,没有找到。- 然后沿着原型链查找,在
Child.prototype(一个Parent实例)中找到了getName方法。 getName方法内部返回this.name,此时this指向child,所以返回child.name,即"Child"。
-
第一次调用
child.name:child.name直接在child自身找到了属性name,值为"Child"。
-
修改
Child.prototype.name:Child.prototype.name被设置为"Prototype"。- 这个修改不会影响
child自身的属性,因为child自身已经有一个name属性。
-
第二次调用
child.getName():- 仍然沿着原型链查找
getName方法,找到的是Child.prototype中的getName。 getName方法内部返回this.name,此时this仍然指向child,所以返回child.name,即"Child"。
- 仍然沿着原型链查找
-
第二次调用
child.name:child.name仍然直接在child自身找到,值为"Child"。
最后
收摊!!