JS原型链

JS函数有双重性质:new实例时是类型,直接获取属性时是对象(如:son.name)
JS函数为对象时,有3属性prototype__proto__,自己定义的静态属性和方法(多个),不存在自身属性
JS函数的原型链就一个,不分是对象时还是类型时

未命名文件 (3).png
function parent(name) {
  let funName = "变量name";
}
parent.staticName = "我叫A";
parent.staticAge = "最少21岁";
parent.prototype.wife = "父亲的女人";

function son(name) {
  this.info = "son属性";
  this.name = name;
}
son.prototype.girl = "儿子的女朋友";

console.log("自身属性:", parent.funName); //undefined 不存在自身属性
console.log("静态属性:", parent.staticName); //我叫A
console.log("prototype:", parent.prototype); //{wife: '父亲的女人', constructor: ƒ} constructor包含了静态属性staticName
console.log("1:", parent.prototype.prototype); //undefined
console.log("__proto__:", parent.__proto__); //对象祖先类:ƒ () { [native code] }
console.log("prototype.__proto__:", parent.prototype.__proto__);
//{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ,...}
image.png

proto代表上一层对象空间
对象空间上的__proto__可以修改
原型空间上的__proto__也可以修改

console.log("parent.__proto__:", parent.__proto__); //ƒ () { [native code] }
parent.__proto__ = {};
console.log("parent.__proto__-edit:", parent.__proto__); //{}
parent.__proto__ = [];
console.log("parent.__proto__-edit:", parent.__proto__); //[]

//原型空间上的__proto__也可以修改
console.log("parent.prototype.__proto__-1:", parent.prototype.__proto__);
//{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, ...}
parent.prototype.__proto__ = {};
console.log("parent.prototype.__proto__-2:", parent.prototype.__proto__); //{}

//无限循环
console.log("parent:", parent); //parent的对象空间
console.log("parent:", parent.prototype); //parent的原型空间
console.log("parent:", parent.prototype.constructor); //parent的对象空间
console.log("parent:", parent.prototype.constructor.prototype); //parent的原型空间

继承

简单继承

可以看出,小A继承了实例上的funName,和原型上的 wife
但一个实例不能获取静态属性,也不能继承静态属性。静态属性 == 类属性

const LaoA = new parent("老A");
const XiaoA = new son("小A");
XiaoA.__proto__ = LaoA;

console.log("小A:", XiaoA, XiaoA.funName, XiaoA.wife);
//son {info: 'son属性', name: '小A'} 老A 父亲的女人 
console.log("小A-静态属性:", XiaoA.staticAge); //undefined  

中等继承

继承后,操作属性或方法,会一层层向上查找,但到原型空间查找时,原型空间有(constructor.prototype和__proto__
这时只走constructor.prototype 这条线去查找,所以找不到爷爷的job
但通过原型链是能找到爷爷类的

function grandfather() {}
grandfather.staticCar="奔驰"
grandfather.prototype.job = "老师";

parent.__proto__ = grandfather; //将父类的__proto__指向爷爷类
console.log("小A调用parent的对象空间:", XiaoA.__proto__.__proto__.constructor); //parent的对象空间
console.log(
  "小A调用parent的上层空间:",
  XiaoA.__proto__.__proto__.constructor.__proto__
); //grandfather
console.log("小A-获取爷爷的job:", XiaoA.job); //undefined
实例获取属性走的路线

子类继承静态属性

方法一:找到父亲爷爷的静态属性,然后手动赋值给 子类静态属性

console.log("son获取静态属性:", son.staticName); //undefined

//下面循环,打印出了3个静态属性和方法
// for (let key in parent) {
//   console.log("key:", key); //key:staticName, key:staticAge, key:staticCar
// }
for (let key in parent) {
  //只要本构造函数的自有属性
  if (Object.prototype.hasOwnProperty.call(parent, key)) {
    //console.log("v:", key); //key:staticName, key:staticAge
    son[key] = parent[key]; //好比son.staticX="abc" 直接写静态属性
  }
}
console.log("son获取静态属性2:", son.staticName); //我叫A   
获取静态属性走的路线

方法二:parent之所以能获取爷爷的静态属性,是因为在前面执行了这段代码

parent.__proto__ = grandfather  //将父类的__proto__指向爷爷类

那么直接把

son.__proto__ = parent  

就搞定了,一样的道理。

方法三: (ES6方式) 用Object.setPrototypeOf()

Object.setPrototypeOf(parent, grandfather)
这行代码最终建立的关系,还是son.__proto__ = parent

孙子获取静态属性路线

之所以列举3种方式,是因为浏览器有的使用es5,有的es6,考虑到兼容。
不然直接Object.setPrototypeOf()搞定。但梳理了一遍,也就更清楚原型链是如何继承的,并继承静态属性和方法


结尾:

抛开 JavaScript 用于模拟 Java 类的复杂语法设施(如 new、Function Object、函数的 prototype 属性等)
原型系统可以说相当简单,可以用两条概括:

1、如果所有对象都有私有字段[[prototype]],就是对象的原型;
2、读一个属性,如果对象本身没有,则会继续访问对象的原型,直到原型为空或者找到为止。

这个模型在 ES 的各个历史版本中并没有很大改变
ES6 提供了一系列内置函数,以便更为直接地访问操纵原型。
分别为:
Object.create 根据指定的原型创建新对象,原型可以是 null;
Object.getPrototypeOf 获得一个对象的原型;
Object.setPrototypeOf 设置一个对象的原型。

你可能感兴趣的:(JS原型链)