一道面试题,像是最锋利的剃刀,比时间杀猪刀更恐怖,它是让你的头发在不知不觉间就无痛落下了。
function A() {
this.name = 'apple';
}
A.prototype.getName = function () {
return this.name;
}
// 补全,使下面能运行
// ...
var B = A.extend({
initialize: function () {
this.superclass.initialize.call(this);
this.total = 3;
},
say: function () {
return '我有' + this.total + '个' + this.getName();
}
})
var b = new B();
console.log(b.say());// 我有3个apple
这道题看上去,第一印象是继承。但是又有个 initilize 在搞事。感觉不处理这个 initilize 搞屎棍,没办法顺利进行。不过可以得出一部分代码,如:
Function.prototype.extend = function (obj) {
// ...
}
如果先不管 initilize ,当做简单继承来处理,还能继续写下去
Function.prototype.extend = function (obj) {
var F = function() {};
F.prototype = new this();
F.prototype.constructor = F;
for (let key in obj) {
F.prototype[key] = obj[key];
}
return F;
}
我们来看一下答案,这里已经实现了继承了,所以答案是:
// 我有undefined个apple
很好,果然是对的,如我所料,不处理 initilize 这个搞屎棍,是不可能革命成功的。
仔细分析一下代码,假如运行 initilize ,total 这个属性要挂载到 A 上面去,要不然,结果还是 我有undefined个apple 。那么 this.superclass.initialize.call(this) 应该是关键的提示。如果 this 要指向 A 那么 A === this.superclass.initialize ,这样就有希望看到革命成功了。那么 initilize 的运行环境是 obj ?还是extend ? 其实都不是,最后是 b.say() 所以 initialize 应该挂载在 extend 里面的 F 上 。那么,需要在 extend 那部分代码进行一些修改:
Function.prototype.extend = function (obj) {
var self = this;
var F = function() {
// 这样在F的环境下,this.superclass.initialize === self === A
this.superclass = {initialize: self};
if (obj.initialize) {
obj.initialize.call(this);
}
};
F.prototype = new self();
F.prototype.constructor = F;
for (let key in obj) {
// 用过了,不必要放进去了
if (key !== 'initialize') {
F.prototype[key] = obj[key];
}
}
return F;
}
function A() {
this.name = 'apple';
}
A.prototype.getName = function () {
return this.name;
}
var B = A.extend({
initialize: function () {
// this.superclass === {initialize: A},那么
// this.superclass.initialize.call(this) === A.call(this)
this.superclass.initialize.call(this);
this.total = 3;
},
say: function () {
return '我有' + this.total + '个' + this.getName();
}
})
var b = new B();
console.log(b.say());// 我有3个apple