- 首先我们上图
如图所示就是我们的原型链视图,想要更贴切的了解原型链的模式,并且想要玩儿好原型链,接下来慢慢看
先从构造函数讲起
ES6语法中,'new 函数()' =>这种方式就是基于构造函数的方式来执行的
- Func不在作为普通函数,而是叫做构造函数(也就是我们所谓的自定义类)
- 返回的结果也不再基于return来判断返回值,返回的结果是当前类的一个实例
function Func(name,age){
//代码执行之前,先创建一个实例对象(堆内存),让this指向这个堆
this.name = name;
this.age = age;
//this.xxx = xxx 这样的形式都是在给实例对象是指私有的属性和方法,存放于创建的堆中
};
//如果函数没有return,会默认把创建的实例对象return出去
let f1 = new Func('xiaoming',10);
let f2 = new Func('xiaohong',10);
console.log(f1===f2);//false 因为每次创建的都是新实例,每一个实例和其它实例都是一个单独的,互相不冲突
- 构造函数执行的时候,函数体中:
- 没有return,默认会返回当前类的实例对象(对象数据类型)
- 有return,并且return的是基本类型值,最后的返回结果还是类的实例,不会影响构造函数本身默认return的实例对象
- 如果return的是引用数据类型,则会把默认返回的实例给覆盖掉(真是项目中不建议手动return,防止实例被覆盖)
- 构造函数执行和普通函数执行的区别(初始化作用域链、形参赋值、变量提升都一样)
- 首先会在当前上下文中创建一个对象(这个对象就是当前类的实例)
- 让当前上下文中的this指向新创建的实例对象
- 代码执行只要是this.xxx=xxx就是给当前实例添加私有属性
- 代码执行完,如果设置了return,return基本值不起作用,return引用值会覆盖默认返回的实例对象
原型模式
构造函数解决了实例的私有属性
原型模式解决的是实例的公有属性
let ary = [1,2,3,4,5];
let ary1 = [3,4,5,6];
// sort、push
ary.push('s');
ary1.push('s');
// push肯定不是数组的私有属性
console.log(ary,ary1)
function Fn(name,age){
this.name = name;
this.age = age
};
Fn.prototype.say = function(){
console.log('哈哈')
}
let f1 = new Fn('chengyunkai', 18); // {name:'chengyunkai', age:18}
let f2 = new Fn('panhong', 24);// {name:'panhong', age:24}
console.log(f1,f2)
f1.say()
由以上的题可以看出:
- 每个函数天生自带一个prototype属性,其属性值是一个对象,这个对象里存储的是供实例调取的公有属性(这个对象也叫原型)
- 每个原型天生自带constructor属性,其属性值指向当前的类
- 每一个对象天生自带__proto__属性,其属性值指向当前所属类的原型(IE浏览器不允许我们操作__proto__属性)
- 对象数据类型
- 实例也是对象类型(出基本值外)
- 类的原型(prototype)属性值也是对象
- 函数也具备对象的特征(它有一重身份就是对象类型)
- 所有对象数据类型都是Object内置类的实例(Object是所有对象的基类)
下面我们用一个小测试来展示原型
function Fn(name, age) {
this.name = name;
this.age = age
}
Fn.prototype.say = function () {
console.log('哈哈')
}
let f1 = new Fn('xiaohong', 18); // {name:'xiaohong',age:18}
let f2 = new Fn('xiaoming', 24); // {name:'xiaoming', age:24}
f1.say()
f2.say()
console.log(Fn.prototype.constructor === Fn);
console.log(f1.__proto__.__proto__.__proto__)
//我想增加一个属性,让Fn类的每一个实例都可以使用
say:function(){
console.log('哈哈')
}
- 解题过程:
- 原型链:
在对象里去查找一个属性或者方法,先看自己私有的有没有,如果自己没有,那就通过__proto__找到当前所属类的原型上,如果原型上有就直接使用,如果原型上也没有,那就继续通过__proto__继续向上查找,直到找到Object的原型为止,如果还没有,那就是null,这种一级一级向上查找的机制就是原型链
原型的重定向
- 重定向之后的原型没有constructor
- 真实项目中,为了保证结构的严谨,我们需要手动在重定向中添加constructor属性
- 要注意在项目中如果想重定向原型,要注意之前原型上的方法不要丢失,要添加进重定向原型中
- 内置类的原型不允许重定向(为了保护内置类原型上的方法)
- 真实项目中为了重定向之后会丢失原来原型上的方法,用Fn.prototype=Object.assign(合并对象),这样也就可以省去手动在重定向的原型上添加constructor属性了
function Fn(){
this.x = 10
};
Fn.prototype.say = function(){
console.log('哈哈')
};
Fn.prototype = {};//原型重定向,原型指向了一个空对象
let f1 = new Fn;
f1.say();//报错