一.原型原型链
- 所有的类都是函数数据类型的(包含内置类)
所有的函数都天生自带一个属性:prototype原型
prototype的属性值默认是一个对象数据类型值【堆】
在对象中存储的是供实例能调用的公共属性和方法
并且在类的原型对象上,默认有一个属性constructor构造函数,属性值是当前类本身 - 所有对象数据类型值也天生自带一个属性:__proto__原型链
__proto__属性的属性值:当前实例所属类的原型prototype.实例.proto===类.prototype
都哪些值是对象数据类型的:
普通对象/数组对象/正则对象/日期对象/类数组对象/DOM对象…
大部分实例(除基本数据类型外)也是对象类型
类的原型prototype也是对象类型的
函数也是对象“函数的三种角色”
…万物皆对象,所有的值都是对象类型的 - Object是所有对象数据类型的“基类”,Object.prototype.__proto__如果要指向也是指向Object.prototype也就是自己,这样没有意义,所以Object.prototype.__proto__等于null.
<script>
function Fn(){
this.x=100;
this.y=200;
this.getX=function (){
console.log(this.x);
}
}
Fn.prototype.getX=function(){
console.log(this.x);
};
Fn.prototype.getY=function(){
console.log(this.y);
};
let f1=new Fn;
let f2=new Fn;
console.log(f1.getX===f2.getX); //f
console.log(f1.getY===f2.getY); //t
console.log(f1.__proto__.getY===Fn.prototype.getY); //t
console.log(f1.__proto__.getX===f2.getX); //f
console.log(f1.getX===Fn.prototype.getX); //f
console.log(f1.constructor); //Fn函数
console.log(Fn.prototype.__proto__.constructor); //Object
f1.getX(); //100
f1.__proto__.getX(); //undefined
f2.getY(); //200
Fn.prototype.getY(); //undefined
</script>
二.重定向
原型重定向,为了方便批量给原型上扩充属性和方法,但是新定向的对象上没有constructor属性,结构不完整;浏览器默认生成的原型对象因为缺少引用会被释放掉,可能导致原始加入的属性和方法丢失掉。
内置类的原型是不允许重定向的,怕把内置的方法整没了。
<script>
function fun(){
this.a=0;
this.b=function(){
alert(this.a);
}
}
fun.prototype={
b:function(){
this.a=20;
alert(this.a);
},
c:function(){
this.a=30;
alert(this.a)
}
}
var my_fun=new fun();
my_fun.b(); //'0'
my_fun.c(); //'30'
</script>
<script>
function Fn(){
this.x=100;
this.y=200;
}
//原始原型对象
Fn.prototype.getX=function (){};
//重定向的新对象 :没有constructor,也没有getX了...
Fn.prototype={
getY:function(){}
};
let f1=new Fn();
原始原型对象上如果不存在其他属性方法,我们只需要把constructor手动设置一下即可,如下:
Fn.prototype={
constructor:Fn,
getX(){},
getY(){},
getZ(){}
}
重定向之前,最好拿新原型对象和原始的原型对象合并一下
Object.assign(对象1,对象2):合并两个对象,重复的属性以对象2 为主,如下所示:
<script>
function Fn(){
this.x=10;
this.y=20;
}
Fn.prototype.z=300;
Fn.prototype=Object.assign(Fn.prototype,{
getX(){},
getY(){},
getZ(){}
});
</script>
三.重写内置new
- new Dog(…)
(1)创建一个Dog类的实例对象
对象(空)
对象.proto===Dog.prototype
(2)把Dog当作普通函数执行:私有上下文/作用域链/形参赋值/变量提升/代码执行…
(3)方法中的this指向创建的实例对象
(4)看方法的返回结果,如果没有return或者返回的是基本类型值,则返回的都是实例对象,否则以函数返回值为主。 - Object.create([对象A]):创建一个空对象,并且把对象A作为他的原型。 空对象.proto===对象A
实例:
<script>
function Dog(name){
tis.name=name;
}
Dog.prototype.bark=function (){
console.log('wangwang');
}
Dog.prototype.sayName=function(){
console.log('my name is'+this.name);
}
//Func[function]:想创造哪个类的实例,就传递这个类
//params[array]:基于剩余运算符获取其余传递的实参,这些值都是给类传递的实参
function _new(Func){
//创建一个实例对象
let obj=Object.create(Func.prototype);
//把类当作普通函数执行:基于call方法强制改变上下文中的this是创建的实例对象obj
let result=Func.call(obj,...params);
if(result!==null && /^(object|function)$/i.test(typeof result)) return result;
return obj;
}
let sanmao=_new(Dog,'三毛');
sanmao.bark();
sanmao.sayName();
console.log(sanmao instanceof Dog); //true
</script>
- Object.create如何实现
Object.create是ES2015(不是ES5,是ES6前身)提供的方法,不兼容低版本浏览器
Object.create=function create(obj){
if(obj===null||typeof obj!=="object"){
throw new TypeError('Object prototype may only be an Object');
}
function Anoymous(){}
Anoymous.prototype=obj;
return new Anoymous;
}