原型相关名词概念解释
名词 | 用途 | 描述 | 其他 | [[Construct]] | 对象的一个内置方法,用于创建对象,通过new或者super关键字调用。 | 该内置方法的第一个参数是传入的参数列表。第二个参数是new所初始化的对象。最后返回一个Obejct。 | constructor调用实现该内置方法 | constructor对象 | 用来创建和初始化一个对象。 | 是个具有prototype 属性的function对象。(这个prototype 属性保存的是一个指针,指向了prototype 对象所在的位置)。 | 通过new创建对象的时候会调用constructor方法,不使用new调用constructor的话,根据具体实现可能会得到不同结果。比如:new Date()得到的是当前日期Date对象,Date()则是当前时间字符串。 | constructor属性 | 指向constructor对象 | 保存的是引用 | 无 | prototype属性 | 指向一个prototype对象所在的位置 | 每个函数都有该属性,这个属性中保存的是引用,而不是具体的对象 | 无 | prototype对象 | 对象的用途是包含可以由特定类型的所有实例共享的属性和方法。 | 该对象的 __proto____属性指向了Object的原型 | 无 | proto_ | 实例的属性,指向实例的原型对象 | 保存的也是对象的引用 | 无 |
---|
ecma262/#constructor
function Parent(){
this.name = 'parent';
}
var a = new Parent();
console.log(Parent.prototype);
console.log(Parent.constructor);
console.log(Parent.prototype.constructor);
console.log(a.prototype);
console.log(a.__proto__);
console.log(a.constructor);
根据上述代码,我们来分析每个函数对应的prototype和constructor
对象 | 取值 | Parent的prototype对象 | 一个具有constructor属性的Object(图:prototype 1-1 ) | Parent.prototype属性 | Parent的prototype对象的引用 | Parent.constructor | ƒ Function() { [native code] } | Parent.prototype.constructor | ƒ Parent(){this.name = 'parent';} | a.prototype | undefined | a.proto__ | 与Parent.prototype相同 | a.constructor | ƒ Parent(){this.name = 'parent';} |
---|
把这些关系抽象成关系图就是如下:(非常建议大家自己写段代码,然后自己画一遍这个关系图,就会非常清晰)
- 抽象的原型构造函数关系图
Function prototype reference
new具体的过程
- 一个继承自
*Foo*.prototype
的新对象被创建。 - 使用指定的参数调用构造函数
Foo
,并将 this 绑定到新创建的对象。new *Foo*
等同于new Foo
()
,也就是没有指定参数列表,Foo
不带任何参数调用的情况。 - 由构造函数返回的对象就是
new
表达式的结果。如果构造函数没有显式返回一个对象,则使用步骤1创建的对象。(一般情况下,构造函数不返回值,但是用户可以选择主动返回对象,来覆盖正常的对象创建步骤)
new Parent() = {
var obj = {};
obj.__proto__ = Parent.prototype; // 此时便建立了obj对象的原型链:
// obj->Parent.prototype->Object.prototype->null
var result = Person.call(obj); // 相当于obj.Person("John")
return typeof result === 'object' ? result : obj; // 如果无返回值或者返回一个非对象值,则将obj返回作为新对象
}
手写一个new方法
function _new(fn,...arg){
const obj = Object.create(fn.prototype);
const ret = fn.apply(obj,arg);
return ret instanceof Object?ret:obj;//考虑到基本数据类型
}
mozilla.org-new
alexzhong22c js-new-happen
Object.getPrototypeOf
var o = new C();
//o是C的实例,所以
Object.getPrototypeOf(o) === C.prototype
Object.getPrototypeOf 返回的是实例对象的原型
因为Object是构造函数
Object.getPrototypeOf(Object) 得到的是// f(){[native code]}
Object.getPrototypeOf( Object ) === Function.prototype; // true
因为把Object看作对象,Object是个函数
所以Object.getPrototypeOf( Object )返回的就是函数对象的原型Function.prototype
Object.prototype是构造出来的对象的原型。
继承的实现方式
构造函数直接实现
function SuperType(){
this.property =true;
}
function SubType(){
SuperType.call(this);
}
//修改父类上的原型内容
SuperType.prototype.getType = function(){
return 'add';
}
var instance = new SubType();
console.log(instance.property);
//true
console.log(instance.getType());
//报错:Uncaught TypeError: instance.getType is not a function
- 问题:通过构造方法继承的子类,可以获取到父类构造函数当中的所有属性。
- 子类就无法获取到父类
prototype
上变化的属性和方法。 - 不好进行函数复用
- 子类就无法获取到父类
原型继承
function SuperType(){
this.property =true;
this.colors = ['red','green'];
}
SuperType.prototype.getSuperValue = function(){
return this.property;
}
var parent = new SuperType();
function SubType(){
this.property = false;//子类重写父类属性
}
//把子类的原型设置为父类的一个新的实例对象
//父类的实例的__proto__中指向它自己的原型对象
//所以这样子类也可以成功访问到
SubType.prototype = new SuperType();
SubType.prototype.getSubType = function(){
return this.property;
}
var instance = new SubType();
console.log(instance.getSuperValue());//false
instance.colors.push('blue');
console.log(parent.colors);//'red','green',
var instance2 = new SubType();
console.log(instance2.colors);//'red','green','blue'
- 问题:
- 不同子类实例会共享同一个引用类型数据,所以如果有一个修改了它,其他实例访问到的也是修改之后的。
- 创建子类实例的时候不能向构造参数传递参数。
组合继承:构造函数+原型继承
function SuperType(){
this.property =true;
this.colors = ['red','green'];
}
SuperType.prototype.getSuperValue = function(){
return this.property;
}
var parent = new SuperType();
function SubType(arg){
SuperType.call(this,arg);
this.property = false;//子类重写父类属性
}
SubType.prototype = new SuperType();
SubType.prototype.getSubType = function(){
return this.property;
}
var instance = new SubType();
console.log(instance.getSuperValue());//false
instance.colors.push('blue');
console.log(instance.colors);//'red','green','blue'
console.log(parent.colors);//'red','green',
var instance2 = new SubType();
console.log(instance2.colors);//'red','green'
- 组合继承,具有了两种集成方式的有点,同时因为借用了父类的构造函数,所以每个子类实例获得了父类的属性。
- 不足之处:每次都会调用两次超类的构造函数。一次是创建子类原型的时候,另一次是在子类构造函数内部。
寄生继承
- 调用函数创建对象+增强该对象的属性和方法
function createAnother(original){
var clone = object(original);
clone.sayHi = function(){
console.log('hi');
}
return clone;
}
var person = {
name:"sherry",
friends:['lisa']
}
var p = createAnother(person);
最佳实践:寄生组合继承
function inheritPrototype(subType,superType){
var prototype = object(superType.prototype);
prototype.contructor = subType;
subType.prototype = prototype;
}
另一种实现方式:
function extend(Child, Parent) {
var F = function(){};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
Child.uber = Parent.prototype;
}
- 只调用一次superType的构造函数
- 原型链也没有改变
参考:《Javascript高级教程》
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!