return o;
}
function Person(){}
var p1 = new Person()
console.log(p1.constructor)
Person.prototype = {
name:“小红”
}
var p2 = new Person()
console.log(p2.constructor)
答案:
function Person(){}
var p1 = new Person()
console.log(p1.constructor) // [Function: Person]
Person.prototype = {
name:“小红”
}
var p2 = new Person()
console.log(p2.constructor) [Function: Object]
无论什么时候,只要创建了一个新函数,就会根据一组特定的规则,为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor属性,这个属性指向了prototype所在的函数。
上面的体题中
打印p1.constructor
时,p1没有constructor
属性,于是就往原型链查找,就在Person.prototype上找到了有constructor
,而这个constructor
指向构造函数Person,因此,打印出[Function: Person]
。
而到p2时,Person.prototype
的值已经被修改成{
name:“小红”
},发现这个值里已经没有constructor,但是prototype上必须有constructor,所以它就自己创建了constructor,并且默认指向Object.,所以打印[Function: Object]
。
第四问:如果我修改prototype时,仍想constructor依旧指向Person,该怎么做呢?
如果想constructor依旧指向Person,可以在修改prototype的时候,添加上construtor属性
function Person(){}
var p1 = new Person()
console.log(p1.constructor) //[Function: Person]
Person.prototype = {
constructor: Person,
name:“小红”
}
var p2 = new Person()
console.log(p2.constructor) //[Function: Person]
但是这种添加constructor属性,会导致它的[[Enumerable]]特性的值被设置为true,所以可以用下面这种方式
function Person(){}
var p1 = new Person()
console.log(p1.constructor) //[Function: Person]
Person.prototype = {
name:“小红”
}
Object.defineProperty(Person.prototype,“constructor”,{
enumerable: false,
value: Person
})
var p2 = new Person()
console.log(p2.constructor) //[Function: Person]
// 实现原型链的一种基本模式
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){
this.subproperty = false;
}
// 继承,用 SuperType 类型的一个实例来重写 SubType 类型的原型对象
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function(){
return this.subproperty;
};
var instance = new SubType();
alert(instance.getSuperValue()); // true
其中,SubType 继承了 SuperType,而继承是通过创建 SuperType 的实例,并将该实例赋值给 SubType 的原型实现的。
实现的本质是重写子类型的原型对象,代之以一个新类型的实例。子类型的新原型对象中有一个内部属性 Prototype
指向了 SuperType 的原型,还有一个从 SuperType 原型中继承过来的属性 constructor 指向了 SuperType 构造函数。
最终的原型链是这样的:instance 指向 SubType 的原型,SubType 的原型又指向 SuperType 的原型,SuperType 的原型又指向 Object 的原型(所有函数的默认原型都是 Object 的实例,因此默认原型都会包含一个内部指针,指向 Object.prototype)
原型链继承的缺点:
1、在通过原型来实现继承时,原型实际上会变成另一个类型的实例。于是,原先的实例属性也就顺理成章地变成了现在的原型属性,并且会被所有的实例共享。这样理解:在超类型构造函数中定义的引用类型值的实例属性,会在子类型原型上变成原型属性被所有子类型实例所共享
2、在创建子类型的实例时,不能向超类型的构造函数中传递参数
有,就是借用构造函数继承
借用构造函数继承(也称伪造对象或经典继承)
// 在子类型构造函数的内部调用超类型构造函数;使用 apply() 或 call() 方法将父对象的构造函数绑定在子对象上
function SuperType(){
// 定义引用类型值属性
this.colors = [“red”,“green”,“blue”];
}
function SubType(){
// 继承 SuperType,在这里还可以给超类型构造函数传参
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push(“purple”);
alert(instance1.colors); // “red,green,blue,purple”
var instance2 = new SubType();
alert(instance2.colors); // “red,green,blue”
通过使用 apply() 或 call() 方法,我们实际上是在将要创建的 SubType 实例的环境下调用了 SuperType 构造函数。这样一来,就会在新 SubType 对象上执行 SuperType() 函数中定义的所有对象初始化代码。结果 SubType 的每个实例就都会具有自己的 colors 属性的副本了
。
借用构造函数的优点是解决了原型链实现继承存在的两个问题。
但是一波已平,一波又起
借用构造函数的缺点是方法都在构造函数中定义,因此函数复用就无法实现了。而且,在超类型的原型中定义的方法,对子类型而言也是不可见的,结果所有类型都只能使用构造函数模式。
既然两种方法都没有对方的缺点,那就可以把两者方法结合起来,就解决了,这种方法叫做组合继承
,
组合继承(也称伪经典继承)
将原型链和借用构造函数的技术组合到一块。使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又能够保证每个实例都有自己的属性。
function SuperType(name){
this.name = name;
this.colors = [“red”,“green”,“blue”];
}
SuperType.prototype.sayName = function(){
alert(this.name);
};
function SubType(name,age){
// 借用构造函数方式继承属性
SuperType.call(this,name);
this.age = age;
}
// 原型链方式继承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
alert(this.age);
};
最后
文章到这里就结束了,如果觉得对你有帮助可以点个赞哦
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
;
this.age = age;
}
// 原型链方式继承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
alert(this.age);
};
最后
文章到这里就结束了,如果觉得对你有帮助可以点个赞哦
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
[外链图片转存中…(img-keEI3LDO-1714205240268)]